mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-02-12 10:10:44 -05:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d24adff0f5 | |||
| 447324eb07 | |||
| 4ca494cc51 | |||
| 8a80dbe8f5 | |||
| 2b539bbdeb | |||
| 3f2c146df1 | |||
| 9a54f04a0d | |||
| 7a33425d1a | |||
| 711aa672f2 | |||
| 8cb6a2f6d2 | |||
| 2f9622e23e | |||
| 674b1fecde | |||
| b36d01d2c7 | |||
| 615175bea8 | |||
| 7515599acb | |||
| 3652d4d5d9 | |||
| 75677770b2 | |||
| 329a1efd5d | |||
| 27760a3b99 | |||
| a93b3cd7f5 | |||
| bacde51d71 | |||
| 418578743a | |||
| 56e5ef22f1 | |||
| e90933d713 | |||
| 5b9c67c2d8 | |||
| fe84a2a303 | |||
| 62de53c306 | |||
| 7c6a10936c | |||
| 032c9bbbf3 | |||
| 8906f98038 | |||
| 5126092449 | |||
| 9d2d70fc63 | |||
| ffaf6bd61b | |||
| 79218d8412 | |||
| 7e6e526ac8 | |||
| 80646e2af0 | |||
| 5808a892c1 | |||
| d58ff90458 | |||
| 7d06ab04dc | |||
| 9d40e8ca25 | |||
| 5b16c5403a | |||
| 4ddbe0b8b9 |
26
.github/scripts/plugin.json
vendored
26
.github/scripts/plugin.json
vendored
@ -6,14 +6,14 @@
|
||||
"llm",
|
||||
"ai"
|
||||
],
|
||||
"compatibility": "Qt 6.8.2",
|
||||
"compatibility": "Qt 6.8.3",
|
||||
"platforms": [
|
||||
"Windows",
|
||||
"macOS",
|
||||
"Linux"
|
||||
],
|
||||
"license": "GPLv3",
|
||||
"version": "0.5.4",
|
||||
"version": "0.5.8",
|
||||
"status": "draft",
|
||||
"is_pack": false,
|
||||
"released_at": null,
|
||||
@ -35,8 +35,28 @@
|
||||
},
|
||||
{
|
||||
"version": "0.5.4",
|
||||
"is_latest": true,
|
||||
"is_latest": false,
|
||||
"released_at": "2025-03-17T03:00:00Z"
|
||||
},
|
||||
{
|
||||
"version": "0.5.5",
|
||||
"is_latest": false,
|
||||
"released_at": "2025-03-20T19:00:00Z"
|
||||
},
|
||||
{
|
||||
"version": "0.5.6",
|
||||
"is_latest": false,
|
||||
"released_at": "2025-04-04T19:00:00Z"
|
||||
},
|
||||
{
|
||||
"version": "0.5.7",
|
||||
"is_latest": false,
|
||||
"released_at": "2025-04-14T01:00:00Z"
|
||||
},
|
||||
{
|
||||
"version": "0.5.8",
|
||||
"is_latest": true,
|
||||
"released_at": "2025-04-17T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"icon": "https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d",
|
||||
|
||||
12
.github/workflows/build_cmake.yml
vendored
12
.github/workflows/build_cmake.yml
vendored
@ -12,9 +12,9 @@ on:
|
||||
|
||||
env:
|
||||
PLUGIN_NAME: QodeAssist
|
||||
QT_VERSION: 6.8.2
|
||||
QT_CREATOR_VERSION: 16.0.0
|
||||
QT_CREATOR_VERSION_INTERNAL: 16.0.0
|
||||
QT_VERSION: 6.8.3
|
||||
QT_CREATOR_VERSION: 16.0.1
|
||||
QT_CREATOR_VERSION_INTERNAL: 16.0.1
|
||||
MACOS_DEPLOYMENT_TARGET: "11.0"
|
||||
CMAKE_VERSION: "3.29.6"
|
||||
NINJA_VERSION: "1.12.1"
|
||||
@ -223,7 +223,7 @@ jobs:
|
||||
COMMAND python
|
||||
-u
|
||||
"${{ steps.qt_creator.outputs.qtc_dir }}/${build_plugin_py}"
|
||||
--name "$ENV{PLUGIN_NAME}-$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}"
|
||||
--name "$ENV{PLUGIN_NAME}-v${{ steps.git.outputs.tag }}-QtC$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}"
|
||||
--src .
|
||||
--build build
|
||||
--qt-path "${{ steps.qt.outputs.qt_dir }}"
|
||||
@ -241,8 +241,8 @@ jobs:
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
||||
name: ${{ env.PLUGIN_NAME}}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
||||
path: ./${{ env.PLUGIN_NAME }}-v${{ steps.git.outputs.tag }}-QtC${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
||||
name: ${{ env.PLUGIN_NAME}}-v${{ steps.git.outputs.tag }}-QtC${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
||||
|
||||
# The json is the same for all platforms, but we need to save one
|
||||
- name: Upload plugin json
|
||||
|
||||
@ -92,6 +92,7 @@ add_qtc_plugin(QodeAssist
|
||||
providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp
|
||||
providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp
|
||||
providers/LlamaCppProvider.hpp providers/LlamaCppProvider.cpp
|
||||
providers/CodestralProvider.hpp providers/CodestralProvider.cpp
|
||||
QodeAssist.qrc
|
||||
LSPCompletion.hpp
|
||||
LLMSuggestion.hpp LLMSuggestion.cpp
|
||||
@ -101,4 +102,27 @@ add_qtc_plugin(QodeAssist
|
||||
ConfigurationManager.hpp ConfigurationManager.cpp
|
||||
CodeHandler.hpp CodeHandler.cpp
|
||||
UpdateStatusWidget.hpp UpdateStatusWidget.cpp
|
||||
widgets/CompletionProgressHandler.hpp widgets/CompletionProgressHandler.cpp
|
||||
widgets/ProgressWidget.hpp widgets/ProgressWidget.cpp
|
||||
widgets/EditorChatButton.hpp widgets/EditorChatButton.cpp
|
||||
widgets/EditorChatButtonHandler.hpp widgets/EditorChatButtonHandler.cpp
|
||||
widgets/QuickRefactorDialog.hpp widgets/QuickRefactorDialog.cpp
|
||||
QuickRefactorHandler.hpp QuickRefactorHandler.cpp
|
||||
)
|
||||
|
||||
get_target_property(QtCreatorCorePath QtCreator::Core LOCATION)
|
||||
find_program(QtCreatorExecutable
|
||||
NAMES
|
||||
qtcreator "Qt Creator"
|
||||
PATHS
|
||||
"${QtCreatorCorePath}/../../../bin"
|
||||
"${QtCreatorCorePath}/../../../MacOS"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (QtCreatorExecutable)
|
||||
add_custom_target(RunQtCreator
|
||||
COMMAND ${QtCreatorExecutable} -pluginpath $<TARGET_FILE_DIR:QodeAssist>
|
||||
DEPENDS QodeAssist
|
||||
)
|
||||
set_target_properties(RunQtCreator PROPERTIES FOLDER "qtc_runnable")
|
||||
endif()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -197,4 +197,16 @@ QString ChatModel::lastMessageId() const
|
||||
return !m_messages.isEmpty() ? m_messages.last().id : "";
|
||||
}
|
||||
|
||||
void ChatModel::resetModelTo(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_messages.size())
|
||||
return;
|
||||
|
||||
if (index < m_messages.size() - 1) {
|
||||
beginRemoveRows(QModelIndex(), index, m_messages.size() - 1);
|
||||
m_messages.remove(index, m_messages.size() - index);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -73,6 +73,8 @@ public:
|
||||
QString currentModel() const;
|
||||
QString lastMessageId() const;
|
||||
|
||||
Q_INVOKABLE void resetModelTo(int index);
|
||||
|
||||
signals:
|
||||
void tokensThresholdChanged();
|
||||
void modelReseted();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -464,12 +464,12 @@ void ChatRootView::updateInputTokensCount()
|
||||
}
|
||||
|
||||
if (!m_attachmentFiles.isEmpty()) {
|
||||
auto attachFiles = Context::ContextManager::instance().getContentFiles(m_attachmentFiles);
|
||||
auto attachFiles = m_clientInterface->contextManager()->getContentFiles(m_attachmentFiles);
|
||||
inputTokens += Context::TokenUtils::estimateFilesTokens(attachFiles);
|
||||
}
|
||||
|
||||
if (!m_linkedFiles.isEmpty()) {
|
||||
auto linkFiles = Context::ContextManager::instance().getContentFiles(m_linkedFiles);
|
||||
auto linkFiles = m_clientInterface->contextManager()->getContentFiles(m_linkedFiles);
|
||||
inputTokens += Context::TokenUtils::estimateFilesTokens(linkFiles);
|
||||
}
|
||||
|
||||
@ -510,7 +510,7 @@ void ChatRootView::onAppendLinkFileFromEditor(Core::IEditor *editor)
|
||||
{
|
||||
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
||||
QString filePath = document->filePath().toFSPathString();
|
||||
if (!m_linkedFiles.contains(filePath)) {
|
||||
if (!m_linkedFiles.contains(filePath) && !shouldIgnoreFileForAttach(document->filePath())) {
|
||||
m_linkedFiles.append(filePath);
|
||||
emit linkedFilesChanged();
|
||||
}
|
||||
@ -537,4 +537,19 @@ void ChatRootView::setRecentFilePath(const QString &filePath)
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatRootView::shouldIgnoreFileForAttach(const Utils::FilePath &filePath)
|
||||
{
|
||||
auto project = ProjectExplorer::ProjectManager::projectForFile(filePath);
|
||||
if (project
|
||||
&& m_clientInterface->contextManager()
|
||||
->ignoreManager()
|
||||
->shouldIgnore(filePath.toFSPathString(), project)) {
|
||||
LOG_MESSAGE(QString("Ignoring file for attachment due to .qodeassistignore: %1")
|
||||
.arg(filePath.toFSPathString()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -78,6 +78,7 @@ public:
|
||||
|
||||
QString chatFileName() const;
|
||||
void setRecentFilePath(const QString &filePath);
|
||||
bool shouldIgnoreFileForAttach(const Utils::FilePath &filePath);
|
||||
|
||||
public slots:
|
||||
void sendMessage(const QString &message);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -33,7 +33,6 @@
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include "ChatAssistantSettings.hpp"
|
||||
#include "ContextManager.hpp"
|
||||
#include "GeneralSettings.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "ProvidersManager.hpp"
|
||||
@ -46,6 +45,7 @@ ClientInterface::ClientInterface(
|
||||
, m_requestHandler(new LLMCore::RequestHandler(this))
|
||||
, m_chatModel(chatModel)
|
||||
, m_promptProvider(promptProvider)
|
||||
, m_contextManager(new Context::ContextManager(this))
|
||||
{
|
||||
connect(
|
||||
m_requestHandler,
|
||||
@ -73,7 +73,7 @@ void ClientInterface::sendMessage(
|
||||
{
|
||||
cancelRequest();
|
||||
|
||||
auto attachFiles = Context::ContextManager::instance().getContentFiles(attachments);
|
||||
auto attachFiles = m_contextManager->getContentFiles(attachments);
|
||||
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "", attachFiles);
|
||||
|
||||
auto &chatAssistantSettings = Settings::chatAssistantSettings();
|
||||
@ -200,7 +200,7 @@ QString ClientInterface::getSystemPromptWithLinkedFiles(
|
||||
if (!linkedFiles.isEmpty()) {
|
||||
updatedPrompt += "\n\nLinked files for reference:\n";
|
||||
|
||||
auto contentFiles = Context::ContextManager::instance().getContentFiles(linkedFiles);
|
||||
auto contentFiles = m_contextManager->getContentFiles(linkedFiles);
|
||||
for (const auto &file : contentFiles) {
|
||||
updatedPrompt += QString("\nFile: %1\nContent:\n%2\n").arg(file.filename, file.content);
|
||||
}
|
||||
@ -209,4 +209,9 @@ QString ClientInterface::getSystemPromptWithLinkedFiles(
|
||||
return updatedPrompt;
|
||||
}
|
||||
|
||||
Context::ContextManager *ClientInterface::contextManager() const
|
||||
{
|
||||
return m_contextManager;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -26,6 +26,7 @@
|
||||
#include "ChatModel.hpp"
|
||||
#include "RequestHandler.hpp"
|
||||
#include "llmcore/IPromptProvider.hpp"
|
||||
#include <context/ContextManager.hpp>
|
||||
|
||||
namespace QodeAssist::Chat {
|
||||
|
||||
@ -45,6 +46,8 @@ public:
|
||||
void clearMessages();
|
||||
void cancelRequest();
|
||||
|
||||
Context::ContextManager *contextManager() const;
|
||||
|
||||
signals:
|
||||
void errorOccurred(const QString &error);
|
||||
void messageReceivedCompletely();
|
||||
@ -58,6 +61,7 @@ private:
|
||||
LLMCore::IPromptProvider *m_promptProvider = nullptr;
|
||||
ChatModel *m_chatModel;
|
||||
LLMCore::RequestHandler *m_requestHandler;
|
||||
Context::ContextManager *m_contextManager;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -28,12 +28,19 @@ Rectangle {
|
||||
property alias msgModel: msgCreator.model
|
||||
property alias messageAttachments: attachmentsModel.model
|
||||
property bool isUserMessage: false
|
||||
property int messageIndex: -1
|
||||
|
||||
signal resetChatToMessage(int index)
|
||||
|
||||
height: msgColumn.implicitHeight + 10
|
||||
radius: 8
|
||||
color: isUserMessage ? palette.alternateBase
|
||||
: palette.base
|
||||
|
||||
HoverHandler {
|
||||
id: mouse
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: msgColumn
|
||||
|
||||
@ -128,6 +135,22 @@ Rectangle {
|
||||
visible: root.isUserMessage
|
||||
}
|
||||
|
||||
QoAButton {
|
||||
id: stopButtonId
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 2
|
||||
}
|
||||
|
||||
text: qsTr("ResetTo")
|
||||
visible: root.isUserMessage && mouse.hovered
|
||||
onClicked: function() {
|
||||
root.resetChatToMessage(root.messageIndex)
|
||||
}
|
||||
}
|
||||
|
||||
component TextComponent : TextBlock {
|
||||
required property var itemData
|
||||
height: implicitHeight + 10
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -92,11 +92,19 @@ ChatRootView {
|
||||
|
||||
delegate: ChatItem {
|
||||
required property var model
|
||||
required property int index
|
||||
|
||||
width: ListView.view.width - scroll.width
|
||||
msgModel: root.chatModel.processMessageContent(model.content)
|
||||
messageAttachments: model.attachments
|
||||
isUserMessage: model.roleType === ChatModel.User
|
||||
messageIndex: index
|
||||
|
||||
onResetChatToMessage: function(index) {
|
||||
messageInput.text = model.content
|
||||
messageInput.cursorPosition = model.content.length
|
||||
root.chatModel.resetModelTo(index)
|
||||
}
|
||||
}
|
||||
|
||||
header: Item {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
* Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "CodeHandler.hpp"
|
||||
#include <settings/CodeCompletionSettings.hpp>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
|
||||
@ -32,6 +33,44 @@ struct LanguageProperties
|
||||
QVector<QString> fileExtensions;
|
||||
};
|
||||
|
||||
const QVector<LanguageProperties> customLanguagesFromSettings()
|
||||
{
|
||||
QVector<LanguageProperties> customLanguages;
|
||||
|
||||
const QStringList customLanguagesList = Settings::codeCompletionSettings().customLanguages();
|
||||
for (const QString &entry : customLanguagesList) {
|
||||
if (entry.trimmed().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList parts = entry.split(',');
|
||||
if (parts.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString name = parts[0].trimmed();
|
||||
QString commentStyle = parts[1].trimmed();
|
||||
QStringList modelNamesList = parts[2].trimmed().split(' ', Qt::SkipEmptyParts);
|
||||
QStringList extensionsList = parts[3].trimmed().split(' ', Qt::SkipEmptyParts);
|
||||
|
||||
if (!name.isEmpty() && !commentStyle.isEmpty() && !modelNamesList.isEmpty()
|
||||
&& !extensionsList.isEmpty()) {
|
||||
QVector<QString> modelNames;
|
||||
for (const auto &modelName : modelNamesList) {
|
||||
modelNames.append(modelName);
|
||||
}
|
||||
|
||||
QVector<QString> extensions;
|
||||
for (const auto &ext : extensionsList) {
|
||||
extensions.append(ext);
|
||||
}
|
||||
|
||||
customLanguages.append({name, commentStyle, modelNames, extensions});
|
||||
}
|
||||
}
|
||||
|
||||
return customLanguages;
|
||||
}
|
||||
const QVector<LanguageProperties> &getKnownLanguages()
|
||||
{
|
||||
static QVector<LanguageProperties> knownLanguages = {
|
||||
@ -55,6 +94,8 @@ const QVector<LanguageProperties> &getKnownLanguages()
|
||||
{"qml", "//", {"qml"}, {"qml"}},
|
||||
};
|
||||
|
||||
knownLanguages.append(customLanguagesFromSettings());
|
||||
|
||||
return knownLanguages;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -24,7 +24,6 @@
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "CodeHandler.hpp"
|
||||
#include "context/ContextManager.hpp"
|
||||
#include "context/DocumentContextReader.hpp"
|
||||
#include "context/Utils.hpp"
|
||||
#include "llmcore/PromptTemplateManager.hpp"
|
||||
@ -51,6 +50,7 @@ LLMClientInterface::LLMClientInterface(
|
||||
, m_requestHandler(requestHandler)
|
||||
, m_documentReader(documentReader)
|
||||
, m_performanceLogger(performanceLogger)
|
||||
, m_contextManager(new Context::ContextManager(this))
|
||||
{
|
||||
connect(
|
||||
&m_requestHandler,
|
||||
@ -61,7 +61,7 @@ LLMClientInterface::LLMClientInterface(
|
||||
|
||||
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
||||
{
|
||||
return "Qode Assist";
|
||||
return "QodeAssist";
|
||||
}
|
||||
|
||||
void LLMClientInterface::startImpl()
|
||||
@ -170,8 +170,7 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||
|
||||
auto updatedContext = prepareContext(request, documentInfo);
|
||||
|
||||
bool isPreset1Active
|
||||
= Context::ContextManager::isSpecifyCompletion(documentInfo, m_generalSettings);
|
||||
bool isPreset1Active = m_contextManager->isSpecifyCompletion(documentInfo);
|
||||
|
||||
const auto providerName = !isPreset1Active ? m_generalSettings.ccProvider()
|
||||
: m_generalSettings.ccPreset1Provider();
|
||||
@ -231,6 +230,19 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||
if (updatedContext.fileContext.has_value())
|
||||
systemPrompt.append(updatedContext.fileContext.value());
|
||||
|
||||
if (m_completeSettings.useOpenFilesContext()) {
|
||||
if (provider->providerID() == LLMCore::ProviderID::LlamaCpp) {
|
||||
for (const auto openedFilePath : m_contextManager->openedFiles({filePath})) {
|
||||
if (!updatedContext.filesMetadata) {
|
||||
updatedContext.filesMetadata = QList<LLMCore::FileMetadata>();
|
||||
}
|
||||
updatedContext.filesMetadata->append({openedFilePath.first, openedFilePath.second});
|
||||
}
|
||||
} else {
|
||||
systemPrompt.append(m_contextManager->openedFilesContext({filePath}));
|
||||
}
|
||||
}
|
||||
|
||||
updatedContext.systemPrompt = systemPrompt;
|
||||
|
||||
if (promptTemplate->type() == LLMCore::TemplateType::Chat) {
|
||||
@ -277,13 +289,17 @@ LLMCore::ContextData LLMClientInterface::prepareContext(
|
||||
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
|
||||
}
|
||||
|
||||
Context::ContextManager *LLMClientInterface::contextManager() const
|
||||
{
|
||||
return m_contextManager;
|
||||
}
|
||||
|
||||
void LLMClientInterface::sendCompletionToClient(
|
||||
const QString &completion, const QJsonObject &request, bool isComplete)
|
||||
{
|
||||
auto filePath = Context::extractFilePathFromRequest(request);
|
||||
auto documentInfo = m_documentReader.readDocument(filePath);
|
||||
bool isPreset1Active
|
||||
= Context::ContextManager::isSpecifyCompletion(documentInfo, m_generalSettings);
|
||||
bool isPreset1Active = m_contextManager->isSpecifyCompletion(documentInfo);
|
||||
|
||||
auto templateName = !isPreset1Active ? m_generalSettings.ccTemplate()
|
||||
: m_generalSettings.ccPreset1Template();
|
||||
@ -332,6 +348,4 @@ void LLMClientInterface::sendCompletionToClient(
|
||||
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
||||
}
|
||||
|
||||
void LLMClientInterface::parseCurrentMessage() {}
|
||||
|
||||
} // namespace QodeAssist
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -22,6 +22,7 @@
|
||||
#include <languageclient/languageclientinterface.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <context/ContextManager.hpp>
|
||||
#include <context/IDocumentReader.hpp>
|
||||
#include <context/ProgrammingLanguage.hpp>
|
||||
#include <llmcore/ContextData.hpp>
|
||||
@ -61,9 +62,10 @@ public:
|
||||
// exposed for tests
|
||||
void sendData(const QByteArray &data) override;
|
||||
|
||||
Context::ContextManager *contextManager() const;
|
||||
|
||||
protected:
|
||||
void startImpl() override;
|
||||
void parseCurrentMessage() override;
|
||||
|
||||
private:
|
||||
void handleInitialize(const QJsonObject &request);
|
||||
@ -84,6 +86,7 @@ private:
|
||||
Context::IDocumentReader &m_documentReader;
|
||||
IRequestPerformanceLogger &m_performanceLogger;
|
||||
QElapsedTimer m_completionTimer;
|
||||
Context::ContextManager *m_contextManager;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Qt Company Ltd.
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -38,8 +38,8 @@ LLMSuggestion::LLMSuggestion(
|
||||
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
||||
int endPos = data.range.end.toPositionInDocument(sourceDocument);
|
||||
|
||||
startPos = qBound(0, startPos, sourceDocument->characterCount() - 1);
|
||||
endPos = qBound(startPos, endPos, sourceDocument->characterCount() - 1);
|
||||
startPos = qBound(0, startPos, sourceDocument->characterCount());
|
||||
endPos = qBound(startPos, endPos, sourceDocument->characterCount());
|
||||
|
||||
QTextCursor cursor(sourceDocument);
|
||||
cursor.setPosition(startPos);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Qt Company Ltd.
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Qt Company Ltd.
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Id" : "qodeassist",
|
||||
"Name" : "QodeAssist",
|
||||
"Version" : "0.5.4",
|
||||
"Version" : "0.5.9",
|
||||
"Vendor" : "Petr Mironychev",
|
||||
"VendorId" : "petrmironychev",
|
||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||
|
||||
@ -2,5 +2,11 @@
|
||||
<qresource prefix="/">
|
||||
<file>resources/images/qoderassist-icon@2x.png</file>
|
||||
<file>resources/images/qoderassist-icon.png</file>
|
||||
<file>resources/images/repeat-last-instruct-icon@2x.png</file>
|
||||
<file>resources/images/repeat-last-instruct-icon.png</file>
|
||||
<file>resources/images/improve-current-code-icon@2x.png</file>
|
||||
<file>resources/images/improve-current-code-icon.png</file>
|
||||
<file>resources/images/suggest-new-icon.png</file>
|
||||
<file>resources/images/suggest-new-icon@2x.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Qt Company Ltd.
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of Qode Assist.
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* The Qt Company portions:
|
||||
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
@ -24,8 +24,10 @@
|
||||
|
||||
#include "QodeAssistClient.hpp"
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QTimer>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <languageclient/languageclientsettings.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
|
||||
@ -35,6 +37,7 @@
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include "settings/ProjectSettings.hpp"
|
||||
#include <context/ChangesManager.h>
|
||||
#include <logger/Logger.hpp>
|
||||
|
||||
using namespace LanguageServerProtocol;
|
||||
using namespace TextEditor;
|
||||
@ -46,9 +49,10 @@ namespace QodeAssist {
|
||||
|
||||
QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface)
|
||||
: LanguageClient::Client(clientInterface)
|
||||
, m_llmClient(clientInterface)
|
||||
, m_recentCharCount(0)
|
||||
{
|
||||
setName("Qode Assist");
|
||||
setName("QodeAssist");
|
||||
LanguageClient::LanguageFilter filter;
|
||||
filter.mimeTypes = QStringList() << "*";
|
||||
setSupportedLanguage(filter);
|
||||
@ -128,6 +132,13 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
|
||||
scheduleRequest(widget);
|
||||
}
|
||||
});
|
||||
|
||||
// auto editors = BaseTextEditor::textEditorsForDocument(document);
|
||||
// connect(
|
||||
// editors.first()->editorWidget(),
|
||||
// &TextEditorWidget::selectionChanged,
|
||||
// this,
|
||||
// [this, editors]() { m_chatButtonHandler.showButton(editors.first()->editorWidget()); });
|
||||
}
|
||||
|
||||
bool QodeAssistClient::canOpenProject(ProjectExplorer::Project *project)
|
||||
@ -142,6 +153,14 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor)
|
||||
if (!isEnabled(project))
|
||||
return;
|
||||
|
||||
if (m_llmClient->contextManager()
|
||||
->ignoreManager()
|
||||
->shouldIgnore(editor->textDocument()->filePath().toUrlishString(), project)) {
|
||||
LOG_MESSAGE(QString("Ignoring file due to .qodeassistignore: %1")
|
||||
.arg(editor->textDocument()->filePath().toUrlishString()));
|
||||
return;
|
||||
}
|
||||
|
||||
MultiTextCursor cursor = editor->multiTextCursor();
|
||||
if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
|
||||
return;
|
||||
@ -151,6 +170,9 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor)
|
||||
{TextDocumentIdentifier(hostPathToServerUri(filePath)),
|
||||
documentVersion(filePath),
|
||||
Position(cursor.mainCursor())}};
|
||||
if (Settings::codeCompletionSettings().showProgressWidget()) {
|
||||
m_progressHandler.showProgress(editor);
|
||||
}
|
||||
request.setResponseCallback([this, editor = QPointer<TextEditorWidget>(editor)](
|
||||
const GetCompletionRequest::Response &response) {
|
||||
QTC_ASSERT(editor, return);
|
||||
@ -160,6 +182,35 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor)
|
||||
sendMessage(request);
|
||||
}
|
||||
|
||||
void QodeAssistClient::requestQuickRefactor(
|
||||
TextEditor::TextEditorWidget *editor, const QString &instructions)
|
||||
{
|
||||
auto project = ProjectManager::projectForFile(editor->textDocument()->filePath());
|
||||
|
||||
if (!isEnabled(project))
|
||||
return;
|
||||
|
||||
if (m_llmClient->contextManager()
|
||||
->ignoreManager()
|
||||
->shouldIgnore(editor->textDocument()->filePath().toUrlishString(), project)) {
|
||||
LOG_MESSAGE(QString("Ignoring file due to .qodeassistignore: %1")
|
||||
.arg(editor->textDocument()->filePath().toUrlishString()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_refactorHandler) {
|
||||
m_refactorHandler = new QuickRefactorHandler(this);
|
||||
connect(
|
||||
m_refactorHandler,
|
||||
&QuickRefactorHandler::refactoringCompleted,
|
||||
this,
|
||||
&QodeAssistClient::handleRefactoringResult);
|
||||
}
|
||||
|
||||
m_progressHandler.showProgress(editor);
|
||||
m_refactorHandler->sendRefactorRequest(editor, instructions);
|
||||
}
|
||||
|
||||
void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
|
||||
{
|
||||
cancelRunningRequest(editor);
|
||||
@ -237,6 +288,7 @@ void QodeAssistClient::handleCompletions(
|
||||
Text::Position pos{toTextPos(c.position())};
|
||||
return TextSuggestion::Data{range, pos, c.text()};
|
||||
});
|
||||
m_progressHandler.hideProgress();
|
||||
if (completions.isEmpty())
|
||||
return;
|
||||
editor->insertSuggestion(std::make_unique<LLMSuggestion>(suggestions, editor->document()));
|
||||
@ -248,6 +300,7 @@ void QodeAssistClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor
|
||||
const auto it = m_runningRequests.constFind(editor);
|
||||
if (it == m_runningRequests.constEnd())
|
||||
return;
|
||||
m_progressHandler.hideProgress();
|
||||
cancelRequest(it->id());
|
||||
m_runningRequests.erase(it);
|
||||
}
|
||||
@ -289,4 +342,32 @@ void QodeAssistClient::cleanupConnections()
|
||||
m_scheduledRequests.clear();
|
||||
}
|
||||
|
||||
void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
|
||||
{
|
||||
if (!result.success) {
|
||||
LOG_MESSAGE(QString("Refactoring failed: %1").arg(result.errorMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
auto editor = BaseTextEditor::currentTextEditor();
|
||||
if (!editor) {
|
||||
LOG_MESSAGE("Refactoring failed: No active editor found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto editorWidget = editor->editorWidget();
|
||||
|
||||
QTextCursor cursor = editorWidget->textCursor();
|
||||
cursor.beginEditBlock();
|
||||
|
||||
int startPos = result.insertRange.begin.toPositionInDocument(editorWidget->document());
|
||||
int endPos = result.insertRange.end.toPositionInDocument(editorWidget->document());
|
||||
|
||||
cursor.setPosition(startPos);
|
||||
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
||||
|
||||
cursor.insertText(result.newText);
|
||||
cursor.endEditBlock();
|
||||
m_progressHandler.hideProgress();
|
||||
}
|
||||
} // namespace QodeAssist
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Qt Company Ltd.
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of Qode Assist.
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* The Qt Company portions:
|
||||
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
@ -26,6 +26,9 @@
|
||||
|
||||
#include "LLMClientInterface.hpp"
|
||||
#include "LSPCompletion.hpp"
|
||||
#include "QuickRefactorHandler.hpp"
|
||||
#include "widgets/CompletionProgressHandler.hpp"
|
||||
#include "widgets/EditorChatButtonHandler.hpp"
|
||||
#include <languageclient/client.h>
|
||||
#include <llmcore/IPromptProvider.hpp>
|
||||
#include <llmcore/IProviderRegistry.hpp>
|
||||
@ -42,6 +45,8 @@ public:
|
||||
bool canOpenProject(ProjectExplorer::Project *project) override;
|
||||
|
||||
void requestCompletions(TextEditor::TextEditorWidget *editor);
|
||||
void requestQuickRefactor(
|
||||
TextEditor::TextEditorWidget *editor, const QString &instructions = QString());
|
||||
|
||||
private:
|
||||
void scheduleRequest(TextEditor::TextEditorWidget *editor);
|
||||
@ -52,6 +57,7 @@ private:
|
||||
|
||||
void setupConnections();
|
||||
void cleanupConnections();
|
||||
void handleRefactoringResult(const RefactorResult &result);
|
||||
|
||||
QHash<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
||||
QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests;
|
||||
@ -60,6 +66,10 @@ private:
|
||||
|
||||
QElapsedTimer m_typingTimer;
|
||||
int m_recentCharCount;
|
||||
CompletionProgressHandler m_progressHandler;
|
||||
EditorChatButtonHandler m_chatButtonHandler;
|
||||
QuickRefactorHandler *m_refactorHandler{nullptr};
|
||||
LLMClientInterface *m_llmClient;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
293
QuickRefactorHandler.cpp
Normal file
293
QuickRefactorHandler.cpp
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "QuickRefactorHandler.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QUuid>
|
||||
|
||||
#include <context/DocumentContextReader.hpp>
|
||||
#include <context/DocumentReaderQtCreator.hpp>
|
||||
#include <context/Utils.hpp>
|
||||
#include <llmcore/PromptTemplateManager.hpp>
|
||||
#include <llmcore/ProvidersManager.hpp>
|
||||
#include <logger/Logger.hpp>
|
||||
#include <settings/ChatAssistantSettings.hpp>
|
||||
#include <settings/GeneralSettings.hpp>
|
||||
|
||||
namespace QodeAssist {
|
||||
|
||||
QuickRefactorHandler::QuickRefactorHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_requestHandler(new LLMCore::RequestHandler(this))
|
||||
, m_currentEditor(nullptr)
|
||||
, m_isRefactoringInProgress(false)
|
||||
, m_contextManager(this)
|
||||
{
|
||||
connect(
|
||||
m_requestHandler,
|
||||
&LLMCore::RequestHandler::completionReceived,
|
||||
this,
|
||||
&QuickRefactorHandler::handleLLMResponse);
|
||||
|
||||
connect(
|
||||
m_requestHandler,
|
||||
&LLMCore::RequestHandler::requestFinished,
|
||||
this,
|
||||
[this](const QString &requestId, bool success, const QString &errorString) {
|
||||
if (!success && requestId == m_lastRequestId) {
|
||||
m_isRefactoringInProgress = false;
|
||||
RefactorResult result;
|
||||
result.success = false;
|
||||
result.errorMessage = errorString;
|
||||
emit refactoringCompleted(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QuickRefactorHandler::~QuickRefactorHandler() {}
|
||||
|
||||
void QuickRefactorHandler::sendRefactorRequest(
|
||||
TextEditor::TextEditorWidget *editor, const QString &instructions)
|
||||
{
|
||||
if (m_isRefactoringInProgress) {
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
m_currentEditor = editor;
|
||||
|
||||
Utils::Text::Range range;
|
||||
if (editor->textCursor().hasSelection()) {
|
||||
QTextCursor cursor = editor->textCursor();
|
||||
int startPos = cursor.selectionStart();
|
||||
int endPos = cursor.selectionEnd();
|
||||
|
||||
QTextBlock startBlock = editor->document()->findBlock(startPos);
|
||||
int startLine = startBlock.blockNumber() + 1;
|
||||
int startColumn = startPos - startBlock.position();
|
||||
|
||||
QTextBlock endBlock = editor->document()->findBlock(endPos);
|
||||
int endLine = endBlock.blockNumber() + 1;
|
||||
int endColumn = endPos - endBlock.position();
|
||||
|
||||
Utils::Text::Position startPosition;
|
||||
startPosition.line = startLine;
|
||||
startPosition.column = startColumn;
|
||||
|
||||
Utils::Text::Position endPosition;
|
||||
endPosition.line = endLine;
|
||||
endPosition.column = endColumn;
|
||||
|
||||
range = Utils::Text::Range();
|
||||
range.begin = startPosition;
|
||||
range.end = endPosition;
|
||||
} else {
|
||||
QTextCursor cursor = editor->textCursor();
|
||||
int cursorPos = cursor.position();
|
||||
|
||||
QTextBlock block = editor->document()->findBlock(cursorPos);
|
||||
int line = block.blockNumber() + 1;
|
||||
int column = cursorPos - block.position();
|
||||
|
||||
Utils::Text::Position cursorPosition;
|
||||
cursorPosition.line = line;
|
||||
cursorPosition.column = column;
|
||||
range = Utils::Text::Range();
|
||||
range.begin = cursorPosition;
|
||||
range.end = cursorPosition;
|
||||
}
|
||||
|
||||
m_currentRange = range;
|
||||
prepareAndSendRequest(editor, instructions, range);
|
||||
}
|
||||
|
||||
void QuickRefactorHandler::prepareAndSendRequest(
|
||||
TextEditor::TextEditorWidget *editor,
|
||||
const QString &instructions,
|
||||
const Utils::Text::Range &range)
|
||||
{
|
||||
auto &settings = Settings::generalSettings();
|
||||
|
||||
auto &providerRegistry = LLMCore::ProvidersManager::instance();
|
||||
auto &promptManager = LLMCore::PromptTemplateManager::instance();
|
||||
|
||||
const auto providerName = settings.caProvider();
|
||||
auto provider = providerRegistry.getProviderByName(providerName);
|
||||
|
||||
if (!provider) {
|
||||
LOG_MESSAGE(QString("No provider found with name: %1").arg(providerName));
|
||||
RefactorResult result;
|
||||
result.success = false;
|
||||
result.errorMessage = QString("No provider found with name: %1").arg(providerName);
|
||||
emit refactoringCompleted(result);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto templateName = settings.caTemplate();
|
||||
auto promptTemplate = promptManager.getChatTemplateByName(templateName);
|
||||
|
||||
if (!promptTemplate) {
|
||||
LOG_MESSAGE(QString("No template found with name: %1").arg(templateName));
|
||||
RefactorResult result;
|
||||
result.success = false;
|
||||
result.errorMessage = QString("No template found with name: %1").arg(templateName);
|
||||
emit refactoringCompleted(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LLMCore::LLMConfig config;
|
||||
config.requestType = LLMCore::RequestType::Chat;
|
||||
config.provider = provider;
|
||||
config.promptTemplate = promptTemplate;
|
||||
config.url = QString("%1%2").arg(settings.caUrl(), provider->chatEndpoint());
|
||||
config.providerRequest
|
||||
= {{"model", settings.caModel()}, {"stream", Settings::chatAssistantSettings().stream()}};
|
||||
config.apiKey = provider->apiKey();
|
||||
|
||||
LLMCore::ContextData context = prepareContext(editor, range, instructions);
|
||||
|
||||
provider
|
||||
->prepareRequest(config.providerRequest, promptTemplate, context, LLMCore::RequestType::Chat);
|
||||
|
||||
QString requestId = QUuid::createUuid().toString();
|
||||
m_lastRequestId = requestId;
|
||||
QJsonObject request{{"id", requestId}};
|
||||
|
||||
m_isRefactoringInProgress = true;
|
||||
|
||||
m_requestHandler->sendLLMRequest(config, request);
|
||||
}
|
||||
|
||||
LLMCore::ContextData QuickRefactorHandler::prepareContext(
|
||||
TextEditor::TextEditorWidget *editor,
|
||||
const Utils::Text::Range &range,
|
||||
const QString &instructions)
|
||||
{
|
||||
LLMCore::ContextData context;
|
||||
|
||||
auto textDocument = editor->textDocument();
|
||||
Context::DocumentReaderQtCreator documentReader;
|
||||
auto documentInfo = documentReader.readDocument(textDocument->filePath().toUrlishString());
|
||||
|
||||
if (!documentInfo.document) {
|
||||
LOG_MESSAGE("Error: Document is not available");
|
||||
return context;
|
||||
}
|
||||
|
||||
QTextCursor cursor = editor->textCursor();
|
||||
int cursorPos = cursor.position();
|
||||
|
||||
// TODO add selecting content before and after cursor/selection
|
||||
QString fullContent = documentInfo.document->toPlainText();
|
||||
QString taggedContent = fullContent;
|
||||
|
||||
if (cursor.hasSelection()) {
|
||||
int selEnd = cursor.selectionEnd();
|
||||
int selStart = cursor.selectionStart();
|
||||
taggedContent
|
||||
.insert(selEnd, selEnd == cursorPos ? "<selection_end><cursor>" : "<selection_end>");
|
||||
taggedContent.insert(
|
||||
selStart, selStart == cursorPos ? "<cursor><selection_start>" : "<selection_start>");
|
||||
} else {
|
||||
taggedContent.insert(cursorPos, "<cursor>");
|
||||
}
|
||||
|
||||
QString systemPrompt = Settings::codeCompletionSettings().quickRefactorSystemPrompt();
|
||||
systemPrompt += "\n\nFile information:";
|
||||
systemPrompt += "\nLanguage: " + documentInfo.mimeType;
|
||||
systemPrompt += "\nFile path: " + documentInfo.filePath;
|
||||
|
||||
systemPrompt += "\n\nCode context with position markers:";
|
||||
systemPrompt += taggedContent;
|
||||
|
||||
systemPrompt += "\n\nOutput format:";
|
||||
systemPrompt += "\n- Generate ONLY the code that should replace the current selection "
|
||||
"between<selection_start><selection_end> or be "
|
||||
"inserted at cursor position<cursor>";
|
||||
systemPrompt += "\n- Do not include any explanations, comments about the code, or markdown "
|
||||
"code block markers";
|
||||
systemPrompt += "\n- The output should be ready to insert directly into the editor";
|
||||
systemPrompt += "\n- Follow the existing code style and indentation patterns";
|
||||
|
||||
if (Settings::codeCompletionSettings().useOpenFilesInQuickRefactor()) {
|
||||
systemPrompt += "\n\n" + m_contextManager.openedFilesContext({documentInfo.filePath});
|
||||
}
|
||||
|
||||
context.systemPrompt = systemPrompt;
|
||||
|
||||
QVector<LLMCore::Message> messages;
|
||||
messages.append(
|
||||
{"user",
|
||||
instructions.isEmpty() ? "Refactor the code to improve its quality and maintainability."
|
||||
: instructions});
|
||||
context.history = messages;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void QuickRefactorHandler::handleLLMResponse(
|
||||
const QString &response, const QJsonObject &request, bool isComplete)
|
||||
{
|
||||
if (request["id"].toString() != m_lastRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isComplete) {
|
||||
QString cleanedResponse = response.trimmed();
|
||||
if (cleanedResponse.startsWith("```")) {
|
||||
int firstNewLine = cleanedResponse.indexOf('\n');
|
||||
int lastFence = cleanedResponse.lastIndexOf("```");
|
||||
|
||||
if (firstNewLine != -1 && lastFence > firstNewLine) {
|
||||
cleanedResponse
|
||||
= cleanedResponse.mid(firstNewLine + 1, lastFence - firstNewLine - 1).trimmed();
|
||||
} else if (lastFence != -1) {
|
||||
cleanedResponse = cleanedResponse.mid(3, lastFence - 3).trimmed();
|
||||
}
|
||||
}
|
||||
|
||||
RefactorResult result;
|
||||
result.newText = cleanedResponse;
|
||||
result.insertRange = m_currentRange;
|
||||
result.success = true;
|
||||
|
||||
LOG_MESSAGE("Refactoring completed successfully. New code to insert: ");
|
||||
LOG_MESSAGE("---------- BEGIN REFACTORED CODE ----------");
|
||||
LOG_MESSAGE(cleanedResponse);
|
||||
LOG_MESSAGE("----------- END REFACTORED CODE -----------");
|
||||
|
||||
emit refactoringCompleted(result);
|
||||
}
|
||||
}
|
||||
|
||||
void QuickRefactorHandler::cancelRequest()
|
||||
{
|
||||
if (m_isRefactoringInProgress) {
|
||||
m_requestHandler->cancelRequest(m_lastRequestId);
|
||||
m_isRefactoringInProgress = false;
|
||||
|
||||
RefactorResult result;
|
||||
result.success = false;
|
||||
result.errorMessage = "Refactoring request was cancelled";
|
||||
emit refactoringCompleted(result);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QodeAssist
|
||||
77
QuickRefactorHandler.hpp
Normal file
77
QuickRefactorHandler.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
#include <context/ContextManager.hpp>
|
||||
#include <context/IDocumentReader.hpp>
|
||||
#include <llmcore/RequestHandler.hpp>
|
||||
|
||||
namespace QodeAssist {
|
||||
|
||||
struct RefactorResult
|
||||
{
|
||||
QString newText;
|
||||
Utils::Text::Range insertRange;
|
||||
bool success;
|
||||
QString errorMessage;
|
||||
};
|
||||
|
||||
class QuickRefactorHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QuickRefactorHandler(QObject *parent = nullptr);
|
||||
~QuickRefactorHandler() override;
|
||||
|
||||
void sendRefactorRequest(TextEditor::TextEditorWidget *editor, const QString &instructions);
|
||||
|
||||
void cancelRequest();
|
||||
|
||||
signals:
|
||||
void refactoringCompleted(const QodeAssist::RefactorResult &result);
|
||||
|
||||
private:
|
||||
void prepareAndSendRequest(
|
||||
TextEditor::TextEditorWidget *editor,
|
||||
const QString &instructions,
|
||||
const Utils::Text::Range &range);
|
||||
|
||||
void handleLLMResponse(const QString &response, const QJsonObject &request, bool isComplete);
|
||||
LLMCore::ContextData prepareContext(
|
||||
TextEditor::TextEditorWidget *editor,
|
||||
const Utils::Text::Range &range,
|
||||
const QString &instructions);
|
||||
|
||||
LLMCore::RequestHandler *m_requestHandler;
|
||||
TextEditor::TextEditorWidget *m_currentEditor;
|
||||
Utils::Text::Range m_currentRange;
|
||||
bool m_isRefactoringInProgress;
|
||||
QString m_lastRequestId;
|
||||
Context::ContextManager m_contextManager;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist
|
||||
31
README.md
31
README.md
@ -2,7 +2,7 @@
|
||||
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://discord.gg/BGMkUsXUgf)
|
||||
|
||||
 QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
||||
@ -13,15 +13,6 @@
|
||||
> - The QodeAssist developer bears no responsibility for any charges incurred
|
||||
> - Please carefully review the provider's pricing and your account settings before use
|
||||
|
||||
⚠️ **Commercial Support and Custom Development**
|
||||
> The QodeAssist developer offers commercial services for:
|
||||
> - Adapting the plugin for specific Qt Creator versions
|
||||
> - Custom development for particular operating systems
|
||||
> - Integration with specific language models
|
||||
> - Implementing custom features and modifications
|
||||
>
|
||||
> For commercial inquiries, please contact: qodeassist.dev@pm.me
|
||||
|
||||
## Table of Contents
|
||||
1. [Overview](#overview)
|
||||
2. [Install plugin to QtCreator](#install-plugin-to-qtcreator)
|
||||
@ -43,6 +34,8 @@
|
||||
## Overview
|
||||
|
||||
- AI-powered code completion
|
||||
- Sharing IDE opened files with model context (disabled by default, need enable in settings)
|
||||
- Quick refactor code via fast chat command and opened files
|
||||
- Chat functionality:
|
||||
- Side and Bottom panels
|
||||
- Chat history autosave and restore
|
||||
@ -60,7 +53,6 @@
|
||||
- Google AI
|
||||
- OpenAI-compatible providers(eg. llama.cpp, https://openrouter.ai)
|
||||
- Extensive library of model-specific templates
|
||||
- Custom template support
|
||||
- Easy configuration and model selection
|
||||
|
||||
Join our Discord Community: Have questions or want to discuss QodeAssist? Join our [Discord server](https://discord.gg/BGMkUsXUgf) to connect with other users and get support!
|
||||
@ -70,6 +62,11 @@ Join our Discord Community: Have questions or want to discuss QodeAssist? Join o
|
||||
<img src="https://github.com/user-attachments/assets/255a52f1-5cc0-4ca3-b05c-c4cf9cdbe25a" width="600" alt="QodeAssistPreview">
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Quick refactor in code: (click to expand)</summary>
|
||||
<img src="https://github.com/user-attachments/assets/4a9092e0-429f-41eb-8723-cbb202fd0a8c" width="600" alt="QodeAssistPreview">
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Multiline Code completion: (click to expand)</summary>
|
||||
<img src="https://github.com/user-attachments/assets/c18dfbd2-8c54-4a7b-90d1-66e3bb51adb0" width="600" alt="QodeAssistPreview">
|
||||
@ -174,7 +171,7 @@ ollama run qwen2.5-coder:32b
|
||||
```
|
||||
|
||||
1. Open Qt Creator settings (Edit > Preferences on Linux/Windows, Qt Creator > Preferences on macOS)
|
||||
2. Navigate to the "Qode Assist" tab
|
||||
2. Navigate to the "QodeAssist" tab
|
||||
3. On the "General" page, verify:
|
||||
- Ollama is selected as your LLM provider
|
||||
- The URL is set to http://localhost:11434
|
||||
@ -239,7 +236,8 @@ Linked files provide persistent context throughout the conversation:
|
||||
|
||||
## QtCreator Version Compatibility
|
||||
|
||||
- QtCreator 16.0.0 - 0.5.2 - 0.5.x
|
||||
- QtCreator 16.0.1 - 0.5.7 - 0.x.x
|
||||
- QtCreator 16.0.0 - 0.5.2 - 0.5.6
|
||||
- QtCreator 15.0.1 - 0.4.8 - 0.5.1
|
||||
- QtCreator 15.0.0 - 0.4.0 - 0.4.7
|
||||
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
|
||||
@ -259,8 +257,13 @@ Linked files provide persistent context throughout the conversation:
|
||||
- To call manual request to suggestion, you can use or change it in settings
|
||||
- on Mac: Option + Command + Q
|
||||
- on Windows: Ctrl + Alt + Q
|
||||
- on Linux with KDE Plasma: Ctrl + Alt + Q
|
||||
- To insert the full suggestion, you can use the TAB key
|
||||
- To insert word of suggistion, you can use Alt + Right Arrow for Win/Lin, or Option + Right Arrow for Mac
|
||||
- To call Quick Refactor dialog, select some code or place cursor and press
|
||||
- on Mac: Option + Command + R
|
||||
- on Windows: Ctrl + Alt + R
|
||||
- on Linux with KDE Plasma: Ctrl + Alt + R
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@ -281,7 +284,7 @@ If you need compatiblity with another os, you have to build manualy. our experim
|
||||
|
||||
If you're still experiencing issues with QodeAssist, you can try resetting the settings to their default values:
|
||||
1. Open Qt Creator settings
|
||||
2. Navigate to the "Qode Assist" tab
|
||||
2. Navigate to the "QodeAssist" tab
|
||||
3. Pick settings page for reset
|
||||
4. Click on the "Reset Page to Defaults" button
|
||||
- The API key will not reset
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -52,10 +52,10 @@ void UpdateStatusWidget::setDefaultAction(QAction *action)
|
||||
|
||||
void UpdateStatusWidget::showUpdateAvailable(const QString &version)
|
||||
{
|
||||
m_versionLabel->setText(tr("new version: v%1").arg(version));
|
||||
m_versionLabel->setText(tr("New version: v%1").arg(version));
|
||||
m_versionLabel->setVisible(true);
|
||||
m_updateButton->setVisible(true);
|
||||
m_updateButton->setToolTip(tr("Update QodeAssist to version %1").arg(version));
|
||||
m_updateButton->setToolTip(tr("Check update information"));
|
||||
}
|
||||
|
||||
void UpdateStatusWidget::hideUpdateInfo()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -7,6 +7,8 @@ add_library(Context STATIC
|
||||
IDocumentReader.hpp
|
||||
TokenUtils.hpp TokenUtils.cpp
|
||||
ProgrammingLanguage.hpp ProgrammingLanguage.cpp
|
||||
IContextManager.hpp
|
||||
IgnoreManager.hpp IgnoreManager.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Context
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -24,19 +24,20 @@
|
||||
#include <QJsonObject>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "GeneralSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include "Logger.hpp"
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
ContextManager &ContextManager::instance()
|
||||
{
|
||||
static ContextManager manager;
|
||||
return manager;
|
||||
}
|
||||
|
||||
ContextManager::ContextManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_ignoreManager(new IgnoreManager(this))
|
||||
{}
|
||||
|
||||
QString ContextManager::readFile(const QString &filePath) const
|
||||
@ -53,12 +54,40 @@ QList<ContentFile> ContextManager::getContentFiles(const QStringList &filePaths)
|
||||
{
|
||||
QList<ContentFile> files;
|
||||
for (const QString &path : filePaths) {
|
||||
auto project = ProjectExplorer::ProjectManager::projectForFile(
|
||||
Utils::FilePath::fromString(path));
|
||||
if (project && m_ignoreManager->shouldIgnore(path, project)) {
|
||||
LOG_MESSAGE(QString("Ignoring file in context due to .qodeassistignore: %1").arg(path));
|
||||
continue;
|
||||
}
|
||||
|
||||
ContentFile contentFile = createContentFile(path);
|
||||
files.append(contentFile);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
QStringList ContextManager::getProjectSourceFiles(ProjectExplorer::Project *project) const
|
||||
{
|
||||
QStringList sourceFiles;
|
||||
if (!project)
|
||||
return sourceFiles;
|
||||
|
||||
auto projectNode = project->rootProjectNode();
|
||||
if (!projectNode)
|
||||
return sourceFiles;
|
||||
|
||||
projectNode->forEachNode(
|
||||
[&sourceFiles, this](ProjectExplorer::FileNode *fileNode) {
|
||||
if (fileNode /*&& shouldProcessFile(fileNode->filePath().toString())*/) {
|
||||
sourceFiles.append(fileNode->filePath().toUrlishString());
|
||||
}
|
||||
},
|
||||
nullptr);
|
||||
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
ContentFile ContextManager::createContentFile(const QString &filePath) const
|
||||
{
|
||||
ContentFile contentFile;
|
||||
@ -68,7 +97,7 @@ ContentFile ContextManager::createContentFile(const QString &filePath) const
|
||||
return contentFile;
|
||||
}
|
||||
|
||||
ProgrammingLanguage ContextManager::getDocumentLanguage(const DocumentInfo &documentInfo)
|
||||
ProgrammingLanguage ContextManager::getDocumentLanguage(const DocumentInfo &documentInfo) const
|
||||
{
|
||||
if (!documentInfo.document) {
|
||||
LOG_MESSAGE("Error: Document is not available for" + documentInfo.filePath);
|
||||
@ -78,9 +107,10 @@ ProgrammingLanguage ContextManager::getDocumentLanguage(const DocumentInfo &docu
|
||||
return Context::ProgrammingLanguageUtils::fromMimeType(documentInfo.mimeType);
|
||||
}
|
||||
|
||||
bool ContextManager::isSpecifyCompletion(
|
||||
const DocumentInfo &documentInfo, const Settings::GeneralSettings &generalSettings)
|
||||
bool ContextManager::isSpecifyCompletion(const DocumentInfo &documentInfo) const
|
||||
{
|
||||
const auto &generalSettings = Settings::generalSettings();
|
||||
|
||||
Context::ProgrammingLanguage documentLanguage = getDocumentLanguage(documentInfo);
|
||||
Context::ProgrammingLanguage preset1Language = Context::ProgrammingLanguageUtils::fromString(
|
||||
generalSettings.preset1Language.displayForIndex(generalSettings.preset1Language()));
|
||||
@ -88,4 +118,68 @@ bool ContextManager::isSpecifyCompletion(
|
||||
return generalSettings.specifyPreset1() && documentLanguage == preset1Language;
|
||||
}
|
||||
|
||||
QList<QPair<QString, QString>> ContextManager::openedFiles(const QStringList excludeFiles) const
|
||||
{
|
||||
auto documents = Core::DocumentModel::openedDocuments();
|
||||
|
||||
QList<QPair<QString, QString>> files;
|
||||
|
||||
for (const auto *document : std::as_const(documents)) {
|
||||
auto textDocument = qobject_cast<const TextEditor::TextDocument *>(document);
|
||||
if (!textDocument)
|
||||
continue;
|
||||
|
||||
auto filePath = textDocument->filePath().toUrlishString();
|
||||
|
||||
auto project = ProjectExplorer::ProjectManager::projectForFile(textDocument->filePath());
|
||||
if (project && m_ignoreManager->shouldIgnore(filePath, project)) {
|
||||
LOG_MESSAGE(
|
||||
QString("Ignoring file in context due to .qodeassistignore: %1").arg(filePath));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!excludeFiles.contains(filePath)) {
|
||||
files.append({filePath, textDocument->plainText()});
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
QString ContextManager::openedFilesContext(const QStringList excludeFiles)
|
||||
{
|
||||
QString context = "User files context:\n";
|
||||
|
||||
auto documents = Core::DocumentModel::openedDocuments();
|
||||
|
||||
for (const auto *document : documents) {
|
||||
auto textDocument = qobject_cast<const TextEditor::TextDocument *>(document);
|
||||
if (!textDocument)
|
||||
continue;
|
||||
|
||||
auto filePath = textDocument->filePath().toUrlishString();
|
||||
if (excludeFiles.contains(filePath))
|
||||
continue;
|
||||
|
||||
auto project = ProjectExplorer::ProjectManager::projectForFile(textDocument->filePath());
|
||||
if (project && m_ignoreManager->shouldIgnore(filePath, project)) {
|
||||
LOG_MESSAGE(
|
||||
QString("Ignoring file in context due to .qodeassistignore: %1").arg(filePath));
|
||||
continue;
|
||||
}
|
||||
|
||||
context += QString("File: %1\n").arg(filePath);
|
||||
context += textDocument->plainText();
|
||||
|
||||
context += "\n";
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
IgnoreManager *ContextManager::ignoreManager() const
|
||||
{
|
||||
return m_ignoreManager;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -23,31 +23,38 @@
|
||||
#include <QString>
|
||||
|
||||
#include "ContentFile.hpp"
|
||||
#include "IDocumentReader.hpp"
|
||||
#include "IContextManager.hpp"
|
||||
#include "IgnoreManager.hpp"
|
||||
#include "ProgrammingLanguage.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
class ContextManager : public QObject
|
||||
class ContextManager : public QObject, public IContextManager
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ContextManager &instance();
|
||||
QString readFile(const QString &filePath) const;
|
||||
QList<ContentFile> getContentFiles(const QStringList &filePaths) const;
|
||||
explicit ContextManager(QObject *parent = nullptr);
|
||||
~ContextManager() override = default;
|
||||
|
||||
static ProgrammingLanguage getDocumentLanguage(const DocumentInfo &documentInfo);
|
||||
static bool isSpecifyCompletion(
|
||||
const DocumentInfo &documentInfo, const Settings::GeneralSettings &generalSettings);
|
||||
QString readFile(const QString &filePath) const override;
|
||||
QList<ContentFile> getContentFiles(const QStringList &filePaths) const override;
|
||||
QStringList getProjectSourceFiles(ProjectExplorer::Project *project) const override;
|
||||
ContentFile createContentFile(const QString &filePath) const override;
|
||||
|
||||
ProgrammingLanguage getDocumentLanguage(const DocumentInfo &documentInfo) const override;
|
||||
bool isSpecifyCompletion(const DocumentInfo &documentInfo) const override;
|
||||
QList<QPair<QString, QString>> openedFiles(const QStringList excludeFiles = QStringList{}) const;
|
||||
QString openedFilesContext(const QStringList excludeFiles = QStringList{});
|
||||
|
||||
IgnoreManager *ignoreManager() const;
|
||||
|
||||
private:
|
||||
explicit ContextManager(QObject *parent = nullptr);
|
||||
~ContextManager() = default;
|
||||
ContextManager(const ContextManager &) = delete;
|
||||
ContextManager &operator=(const ContextManager &) = delete;
|
||||
ContentFile createContentFile(const QString &filePath) const;
|
||||
IgnoreManager *m_ignoreManager;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -145,9 +145,24 @@ CopyrightInfo DocumentContextReader::findCopyright()
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
QString matchedText = match.captured().toLower();
|
||||
|
||||
if (matchedText.contains("copyright") || matchedText.contains("(C)")
|
||||
|| matchedText.contains("(c)") || matchedText.contains("©")
|
||||
|| getYearRegex().match(text).hasMatch() || getNameRegex().match(text).hasMatch()) {
|
||||
bool hasCopyrightIndicator = matchedText.contains("copyright")
|
||||
|| matchedText.contains("(c)") || matchedText.contains("©")
|
||||
|| matchedText.contains("copr.")
|
||||
|| matchedText.contains("all rights reserved")
|
||||
|| matchedText.contains("proprietary")
|
||||
|| matchedText.contains("licensed under")
|
||||
|| matchedText.contains("license:")
|
||||
|| matchedText.contains("gpl") || matchedText.contains("lgpl")
|
||||
|| matchedText.contains("mit license")
|
||||
|| matchedText.contains("apache license")
|
||||
|| matchedText.contains("bsd license")
|
||||
|| matchedText.contains("mozilla public license")
|
||||
|| matchedText.contains("copyleft");
|
||||
|
||||
bool hasYear = getYearRegex().match(matchedText).hasMatch();
|
||||
bool hasName = getNameRegex().match(matchedText).hasMatch();
|
||||
|
||||
if ((hasCopyrightIndicator && (hasYear || hasName)) || (hasYear && hasName)) {
|
||||
int startPos = match.capturedStart();
|
||||
int endPos = match.capturedEnd();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
49
context/IContextManager.hpp
Normal file
49
context/IContextManager.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "ContentFile.hpp"
|
||||
#include "IDocumentReader.hpp"
|
||||
#include "ProgrammingLanguage.hpp"
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
class IContextManager
|
||||
{
|
||||
public:
|
||||
virtual ~IContextManager() = default;
|
||||
|
||||
virtual QString readFile(const QString &filePath) const = 0;
|
||||
virtual QList<ContentFile> getContentFiles(const QStringList &filePaths) const = 0;
|
||||
virtual QStringList getProjectSourceFiles(ProjectExplorer::Project *project) const = 0;
|
||||
virtual ContentFile createContentFile(const QString &filePath) const = 0;
|
||||
|
||||
virtual ProgrammingLanguage getDocumentLanguage(const DocumentInfo &documentInfo) const = 0;
|
||||
virtual bool isSpecifyCompletion(const DocumentInfo &documentInfo) const = 0;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
274
context/IgnoreManager.cpp
Normal file
274
context/IgnoreManager.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "IgnoreManager.hpp"
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
IgnoreManager::IgnoreManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
auto projectManager = ProjectExplorer::ProjectManager::instance();
|
||||
if (projectManager) {
|
||||
connect(
|
||||
projectManager,
|
||||
&ProjectExplorer::ProjectManager::projectRemoved,
|
||||
this,
|
||||
&IgnoreManager::removeIgnorePatterns);
|
||||
}
|
||||
|
||||
connect(
|
||||
QCoreApplication::instance(),
|
||||
&QCoreApplication::aboutToQuit,
|
||||
this,
|
||||
&IgnoreManager::cleanupConnections);
|
||||
}
|
||||
|
||||
IgnoreManager::~IgnoreManager()
|
||||
{
|
||||
cleanupConnections();
|
||||
}
|
||||
|
||||
void IgnoreManager::cleanupConnections()
|
||||
{
|
||||
QList<ProjectExplorer::Project *> projects = m_projectConnections.keys();
|
||||
for (ProjectExplorer::Project *project : projects) {
|
||||
if (project) {
|
||||
disconnect(m_projectConnections.take(project));
|
||||
}
|
||||
}
|
||||
m_projectConnections.clear();
|
||||
m_projectIgnorePatterns.clear();
|
||||
m_ignoreCache.clear();
|
||||
}
|
||||
|
||||
bool IgnoreManager::shouldIgnore(const QString &filePath, ProjectExplorer::Project *project) const
|
||||
{
|
||||
if (!project)
|
||||
return false;
|
||||
|
||||
if (!m_projectIgnorePatterns.contains(project)) {
|
||||
const_cast<IgnoreManager *>(this)->reloadIgnorePatterns(project);
|
||||
}
|
||||
|
||||
const QStringList &patterns = m_projectIgnorePatterns[project];
|
||||
if (patterns.isEmpty())
|
||||
return false;
|
||||
|
||||
QDir projectDir(project->projectDirectory().toUrlishString());
|
||||
QString relativePath = projectDir.relativeFilePath(filePath);
|
||||
|
||||
return matchesIgnorePatterns(relativePath, patterns);
|
||||
}
|
||||
|
||||
bool IgnoreManager::matchesIgnorePatterns(const QString &path, const QStringList &patterns) const
|
||||
{
|
||||
QString cacheKey = path + ":" + patterns.join("|");
|
||||
if (m_ignoreCache.contains(cacheKey))
|
||||
return m_ignoreCache[cacheKey];
|
||||
|
||||
bool result = isPathExcluded(path, patterns);
|
||||
m_ignoreCache.insert(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IgnoreManager::isPathExcluded(const QString &path, const QStringList &patterns) const
|
||||
{
|
||||
bool excluded = false;
|
||||
|
||||
for (const QString &pattern : patterns) {
|
||||
if (pattern.isEmpty() || pattern.startsWith('#'))
|
||||
continue;
|
||||
|
||||
bool isNegative = pattern.startsWith('!');
|
||||
QString actualPattern = isNegative ? pattern.mid(1) : pattern;
|
||||
|
||||
bool matches = matchPathWithPattern(path, actualPattern);
|
||||
|
||||
if (matches) {
|
||||
excluded = !isNegative;
|
||||
}
|
||||
}
|
||||
|
||||
return excluded;
|
||||
}
|
||||
|
||||
bool IgnoreManager::matchPathWithPattern(const QString &path, const QString &pattern) const
|
||||
{
|
||||
QString adjustedPattern = pattern.trimmed();
|
||||
|
||||
bool matchFromRoot = adjustedPattern.startsWith('/');
|
||||
if (matchFromRoot)
|
||||
adjustedPattern = adjustedPattern.mid(1);
|
||||
|
||||
bool matchDirOnly = adjustedPattern.endsWith('/');
|
||||
if (matchDirOnly)
|
||||
adjustedPattern.chop(1);
|
||||
|
||||
QString regexPattern = QRegularExpression::escape(adjustedPattern);
|
||||
|
||||
regexPattern.replace("\\*\\*", ".*");
|
||||
|
||||
regexPattern.replace("\\*", "[^/]*");
|
||||
|
||||
regexPattern.replace("\\?", ".");
|
||||
|
||||
if (matchFromRoot)
|
||||
regexPattern = QString("^%1").arg(regexPattern);
|
||||
else
|
||||
regexPattern = QString("(^|/)%1").arg(regexPattern);
|
||||
|
||||
if (matchDirOnly)
|
||||
regexPattern = QString("%1$").arg(regexPattern);
|
||||
else
|
||||
regexPattern = QString("%1($|/)").arg(regexPattern);
|
||||
|
||||
QRegularExpression regex(regexPattern);
|
||||
QRegularExpressionMatch match = regex.match(path);
|
||||
return match.hasMatch();
|
||||
}
|
||||
|
||||
QStringList IgnoreManager::loadIgnorePatterns(ProjectExplorer::Project *project)
|
||||
{
|
||||
QStringList patterns;
|
||||
if (!project)
|
||||
return patterns;
|
||||
|
||||
QString ignoreFile = ignoreFilePath(project);
|
||||
if (ignoreFile.isEmpty() || !QFile::exists(ignoreFile)) {
|
||||
// LOG_MESSAGE(
|
||||
// QString("No .qodeassistignore file found for project: %1").arg(project->displayName()));
|
||||
return patterns;
|
||||
}
|
||||
|
||||
QFile file(ignoreFile);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
LOG_MESSAGE(QString("Could not open .qodeassistignore file: %1").arg(ignoreFile));
|
||||
return patterns;
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
if (!line.isEmpty() && !line.startsWith('#'))
|
||||
patterns << line;
|
||||
}
|
||||
|
||||
LOG_MESSAGE(QString("Successfully loaded .qodeassistignore file: %1 with %2 patterns")
|
||||
.arg(ignoreFile)
|
||||
.arg(patterns.size()));
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
void IgnoreManager::reloadIgnorePatterns(ProjectExplorer::Project *project)
|
||||
{
|
||||
if (!project)
|
||||
return;
|
||||
|
||||
QStringList patterns = loadIgnorePatterns(project);
|
||||
m_projectIgnorePatterns[project] = patterns;
|
||||
|
||||
QStringList keysToRemove;
|
||||
QString projectPath = project->projectDirectory().toUrlishString();
|
||||
for (auto it = m_ignoreCache.begin(); it != m_ignoreCache.end(); ++it) {
|
||||
if (it.key().contains(projectPath))
|
||||
keysToRemove << it.key();
|
||||
}
|
||||
|
||||
for (const QString &key : keysToRemove)
|
||||
m_ignoreCache.remove(key);
|
||||
|
||||
if (!m_projectConnections.contains(project)) {
|
||||
QPointer<ProjectExplorer::Project> projectPtr(project);
|
||||
auto connection = connect(project, &QObject::destroyed, this, [this, projectPtr]() {
|
||||
if (projectPtr) {
|
||||
m_projectIgnorePatterns.remove(projectPtr);
|
||||
m_projectConnections.remove(projectPtr);
|
||||
|
||||
QStringList keysToRemove;
|
||||
for (auto it = m_ignoreCache.begin(); it != m_ignoreCache.end(); ++it) {
|
||||
if (it.key().contains(projectPtr->projectDirectory().toUrlishString()))
|
||||
keysToRemove << it.key();
|
||||
}
|
||||
|
||||
for (const QString &key : keysToRemove)
|
||||
m_ignoreCache.remove(key);
|
||||
}
|
||||
});
|
||||
|
||||
m_projectConnections[project] = connection;
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreManager::removeIgnorePatterns(ProjectExplorer::Project *project)
|
||||
{
|
||||
m_projectIgnorePatterns.remove(project);
|
||||
|
||||
QStringList keysToRemove;
|
||||
for (auto it = m_ignoreCache.begin(); it != m_ignoreCache.end(); ++it) {
|
||||
if (it.key().contains(project->projectDirectory().toUrlishString()))
|
||||
keysToRemove << it.key();
|
||||
}
|
||||
|
||||
for (const QString &key : keysToRemove)
|
||||
m_ignoreCache.remove(key);
|
||||
|
||||
if (m_projectConnections.contains(project)) {
|
||||
disconnect(m_projectConnections[project]);
|
||||
m_projectConnections.remove(project);
|
||||
}
|
||||
|
||||
LOG_MESSAGE(QString("Removed ignore patterns for project: %1").arg(project->displayName()));
|
||||
}
|
||||
|
||||
void IgnoreManager::reloadAllPatterns()
|
||||
{
|
||||
QList<ProjectExplorer::Project *> projects = m_projectIgnorePatterns.keys();
|
||||
|
||||
for (ProjectExplorer::Project *project : projects) {
|
||||
if (project) {
|
||||
reloadIgnorePatterns(project);
|
||||
}
|
||||
}
|
||||
|
||||
m_ignoreCache.clear();
|
||||
}
|
||||
|
||||
QString IgnoreManager::ignoreFilePath(ProjectExplorer::Project *project) const
|
||||
{
|
||||
if (!project) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return project->projectDirectory().toUrlishString() + "/.qodeassistignore";
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
62
context/IgnoreManager.hpp
Normal file
62
context/IgnoreManager.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
class IgnoreManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IgnoreManager(QObject *parent = nullptr);
|
||||
~IgnoreManager() override;
|
||||
|
||||
bool shouldIgnore(const QString &filePath, ProjectExplorer::Project *project = nullptr) const;
|
||||
void reloadIgnorePatterns(ProjectExplorer::Project *project);
|
||||
void removeIgnorePatterns(ProjectExplorer::Project *project);
|
||||
|
||||
void reloadAllPatterns();
|
||||
|
||||
private slots:
|
||||
void cleanupConnections();
|
||||
|
||||
private:
|
||||
bool matchesIgnorePatterns(const QString &path, const QStringList &patterns) const;
|
||||
bool isPathExcluded(const QString &path, const QStringList &patterns) const;
|
||||
bool matchPathWithPattern(const QString &path, const QString &pattern) const;
|
||||
QStringList loadIgnorePatterns(ProjectExplorer::Project *project);
|
||||
QString ignoreFilePath(ProjectExplorer::Project *project) const;
|
||||
|
||||
QHash<ProjectExplorer::Project *, QStringList> m_projectIgnorePatterns;
|
||||
mutable QHash<QString, bool> m_ignoreCache;
|
||||
QHash<ProjectExplorer::Project *, QMetaObject::Connection> m_projectConnections;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -34,6 +34,13 @@ struct Message
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
struct FileMetadata
|
||||
{
|
||||
QString filePath;
|
||||
QString content;
|
||||
bool operator==(const FileMetadata &) const = default;
|
||||
};
|
||||
|
||||
struct ContextData
|
||||
{
|
||||
std::optional<QString> systemPrompt;
|
||||
@ -41,6 +48,7 @@ struct ContextData
|
||||
std::optional<QString> suffix;
|
||||
std::optional<QString> fileContext;
|
||||
std::optional<QVector<Message>> history;
|
||||
std::optional<QList<FileMetadata>> filesMetadata;
|
||||
|
||||
bool operator==(const ContextData &) const = default;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -28,7 +28,7 @@ void Logger::log(const QString &message, bool silent)
|
||||
if (!m_loggingEnabled)
|
||||
return;
|
||||
|
||||
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
|
||||
const QString prefixedMessage = QLatin1String("[QodeAssist] ") + message;
|
||||
if (silent) {
|
||||
Core::MessageManager::writeSilently(prefixedMessage);
|
||||
} else {
|
||||
@ -43,7 +43,7 @@ void Logger::logMessages(const QStringList &messages, bool silent)
|
||||
|
||||
QStringList prefixedMessages;
|
||||
for (const QString &message : messages) {
|
||||
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
|
||||
prefixedMessages << (QLatin1String("[QodeAssist] ") + message);
|
||||
}
|
||||
|
||||
if (silent) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
46
providers/CodestralProvider.cpp
Normal file
46
providers/CodestralProvider.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CodestralProvider.hpp"
|
||||
|
||||
#include "settings/ProviderSettings.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
QString CodestralProvider::name() const
|
||||
{
|
||||
return "Codestral";
|
||||
}
|
||||
|
||||
QString CodestralProvider::url() const
|
||||
{
|
||||
return "https://codestral.mistral.ai";
|
||||
}
|
||||
|
||||
bool CodestralProvider::supportsModelListing() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QString CodestralProvider::apiKey() const
|
||||
{
|
||||
return Settings::providerSettings().codestralApiKey();
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
35
providers/CodestralProvider.hpp
Normal file
35
providers/CodestralProvider.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MistralAIProvider.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
class CodestralProvider : public MistralAIProvider
|
||||
{
|
||||
public:
|
||||
QString name() const override;
|
||||
QString url() const override;
|
||||
bool supportsModelListing() const override;
|
||||
QString apiKey() const override;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -30,7 +30,6 @@
|
||||
#include "logger/Logger.hpp"
|
||||
#include "settings/ChatAssistantSettings.hpp"
|
||||
#include "settings/CodeCompletionSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include "settings/ProviderSettings.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -167,6 +167,7 @@ QList<QString> LlamaCppProvider::validateRequest(
|
||||
{"model", {}},
|
||||
{"input_prefix", {}},
|
||||
{"input_suffix", {}},
|
||||
{"input_extra", {}},
|
||||
{"prompt", {}},
|
||||
{"temperature", {}},
|
||||
{"top_p", {}},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
@ -30,6 +30,7 @@
|
||||
#include "logger/Logger.hpp"
|
||||
#include "settings/ChatAssistantSettings.hpp"
|
||||
#include "settings/CodeCompletionSettings.hpp"
|
||||
#include "settings/ProviderSettings.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
@ -139,6 +140,7 @@ QList<QString> OllamaProvider::getInstalledModels(const QString &url)
|
||||
QList<QString> models;
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkRequest request(QString("%1%2").arg(url, "/api/tags"));
|
||||
prepareNetworkRequest(request);
|
||||
QNetworkReply *reply = manager.get(request);
|
||||
|
||||
QEventLoop loop;
|
||||
@ -210,6 +212,10 @@ QString OllamaProvider::apiKey() const
|
||||
void OllamaProvider::prepareNetworkRequest(QNetworkRequest &networkRequest) const
|
||||
{
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
const auto key = Settings::providerSettings().ollamaBasicAuthApiKey();
|
||||
if (!key.isEmpty()) {
|
||||
networkRequest.setRawHeader("Authorization", "Basic " + key.toLatin1());
|
||||
}
|
||||
}
|
||||
|
||||
LLMCore::ProviderID OllamaProvider::providerID() const
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
* Copyright (C) 2024-2025 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user