mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-06-04 01:28:58 -04:00
Compare commits
No commits in common. "main" and "v0.5.7" have entirely different histories.
29
.github/scripts/plugin.json
vendored
29
.github/scripts/plugin.json
vendored
@ -13,7 +13,7 @@
|
|||||||
"Linux"
|
"Linux"
|
||||||
],
|
],
|
||||||
"license": "GPLv3",
|
"license": "GPLv3",
|
||||||
"version": "0.5.11",
|
"version": "0.5.7",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"is_pack": false,
|
"is_pack": false,
|
||||||
"released_at": null,
|
"released_at": null,
|
||||||
@ -50,33 +50,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"is_latest": false,
|
|
||||||
"released_at": "2025-04-14T01:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "0.5.8",
|
|
||||||
"is_latest": false,
|
|
||||||
"released_at": "2025-04-17T10:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "0.5.9",
|
|
||||||
"is_latest": false,
|
|
||||||
"released_at": "2025-04-21T10:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "0.5.10",
|
|
||||||
"is_latest": false,
|
|
||||||
"released_at": "2025-04-24T10:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "0.5.11",
|
|
||||||
"is_latest": false,
|
|
||||||
"released_at": "2025-04-24T21:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "0.5.12",
|
|
||||||
"is_latest": true,
|
"is_latest": true,
|
||||||
"released_at": "2025-05-01T17:00:00Z"
|
"released_at": "2025-04-14T01:00:00Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d",
|
"icon": "https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d",
|
||||||
|
91
.github/workflows/build_cmake.yml
vendored
91
.github/workflows/build_cmake.yml
vendored
@ -12,13 +12,16 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: QodeAssist
|
PLUGIN_NAME: QodeAssist
|
||||||
|
QT_VERSION: 6.8.3
|
||||||
|
QT_CREATOR_VERSION: 16.0.1
|
||||||
|
QT_CREATOR_VERSION_INTERNAL: 16.0.1
|
||||||
MACOS_DEPLOYMENT_TARGET: "11.0"
|
MACOS_DEPLOYMENT_TARGET: "11.0"
|
||||||
CMAKE_VERSION: "3.29.6"
|
CMAKE_VERSION: "3.29.6"
|
||||||
NINJA_VERSION: "1.12.1"
|
NINJA_VERSION: "1.12.1"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.config.name }} (Qt ${{ matrix.qt_config.qt_version }}, QtC ${{ matrix.qt_config.qt_creator_version }})
|
name: ${{ matrix.config.name }}
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.git.outputs.tag }}
|
tag: ${{ steps.git.outputs.tag }}
|
||||||
@ -44,18 +47,12 @@ jobs:
|
|||||||
platform: mac_x64,
|
platform: mac_x64,
|
||||||
cc: "clang", cxx: "clang++"
|
cc: "clang", cxx: "clang++"
|
||||||
}
|
}
|
||||||
qt_config:
|
|
||||||
- {
|
|
||||||
qt_version: "6.8.3",
|
|
||||||
qt_creator_version: "16.0.2"
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
qt_version: "6.8.3",
|
|
||||||
qt_creator_version: "16.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
- name: Checkout submodules
|
- name: Checkout submodules
|
||||||
id: git
|
id: git
|
||||||
@ -64,12 +61,7 @@ jobs:
|
|||||||
if (${{github.ref}} MATCHES "tags/v(.*)")
|
if (${{github.ref}} MATCHES "tags/v(.*)")
|
||||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}")
|
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}")
|
||||||
else()
|
else()
|
||||||
execute_process(
|
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${{github.run_id}}")
|
||||||
COMMAND git rev-parse --short HEAD
|
|
||||||
OUTPUT_VARIABLE short_sha
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${short_sha}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
- name: Download Ninja and CMake
|
- name: Download Ninja and CMake
|
||||||
@ -104,7 +96,7 @@ jobs:
|
|||||||
id: qt
|
id: qt
|
||||||
shell: cmake -P {0}
|
shell: cmake -P {0}
|
||||||
run: |
|
run: |
|
||||||
set(qt_version "${{ matrix.qt_config.qt_version }}")
|
set(qt_version "$ENV{QT_VERSION}")
|
||||||
|
|
||||||
string(REPLACE "." "" qt_version_dotless "${qt_version}")
|
string(REPLACE "." "" qt_version_dotless "${qt_version}")
|
||||||
if ("${{ runner.os }}" STREQUAL "Windows")
|
if ("${{ runner.os }}" STREQUAL "Windows")
|
||||||
@ -182,11 +174,10 @@ jobs:
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
- name: Download Qt Creator
|
- name: Download Qt Creator
|
||||||
uses: qt-creator/install-dev-package@v2.0
|
uses: qt-creator/install-dev-package@v1.2
|
||||||
with:
|
with:
|
||||||
version: ${{ matrix.qt_config.qt_creator_version }}
|
version: ${{ env.QT_CREATOR_VERSION }}
|
||||||
unzip-to: 'qtcreator'
|
unzip-to: 'qtcreator'
|
||||||
platform: ${{ matrix.config.platform }}
|
|
||||||
|
|
||||||
- name: Extract Qt Creator
|
- name: Extract Qt Creator
|
||||||
id: qt_creator
|
id: qt_creator
|
||||||
@ -232,7 +223,7 @@ jobs:
|
|||||||
COMMAND python
|
COMMAND python
|
||||||
-u
|
-u
|
||||||
"${{ steps.qt_creator.outputs.qtc_dir }}/${build_plugin_py}"
|
"${{ steps.qt_creator.outputs.qtc_dir }}/${build_plugin_py}"
|
||||||
--name "$ENV{PLUGIN_NAME}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}"
|
--name "$ENV{PLUGIN_NAME}-$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}"
|
||||||
--src .
|
--src .
|
||||||
--build build
|
--build build
|
||||||
--qt-path "${{ steps.qt.outputs.qt_dir }}"
|
--qt-path "${{ steps.qt.outputs.qt_dir }}"
|
||||||
@ -250,18 +241,68 @@ jobs:
|
|||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: ./${{ env.PLUGIN_NAME }}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}.7z
|
path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
||||||
name: ${{ env.PLUGIN_NAME}}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}.7z
|
name: ${{ env.PLUGIN_NAME}}-${{ 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
|
||||||
|
if: startsWith(matrix.config.os, 'ubuntu')
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.PLUGIN_NAME }}-origin-json
|
||||||
|
path: ./build/build/${{ env.PLUGIN_NAME }}.json
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
if: startsWith(matrix.config.os, 'ubuntu')
|
if: startsWith(matrix.config.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
xvfb-run ./build/build/test/QodeAssistTest
|
xvfb-run ./build/build/test/QodeAssistTest
|
||||||
|
|
||||||
release:
|
update_json:
|
||||||
if: contains(github.ref, 'tags/v')
|
if: contains(github.ref, 'tags/v')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: [build]
|
needs: build
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Download the JSON file
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.PLUGIN_NAME }}-origin-json
|
||||||
|
path: ./${{ env.PLUGIN_NAME }}-origin
|
||||||
|
|
||||||
|
- name: Store Release upload_url
|
||||||
|
run: |
|
||||||
|
RELEASE_HTML_URL=$(echo "${{github.event.repository.html_url}}/releases/download/v${{ needs.build.outputs.tag }}")
|
||||||
|
echo "RELEASE_HTML_URL=${RELEASE_HTML_URL}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Run the Node.js script to update JSON
|
||||||
|
env:
|
||||||
|
QT_TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
API_URL: ${{ secrets.API_URL }}
|
||||||
|
run: |
|
||||||
|
node .github/scripts/registerPlugin.js ${{ env.RELEASE_HTML_URL }} ${{ env.PLUGIN_NAME }} ${{ env.QT_CREATOR_VERSION }} ${{ env.QT_CREATOR_VERSION_INTERNAL }} ${{ env.QT_TOKEN }} ${{ env.API_URL }}
|
||||||
|
|
||||||
|
- name: Delete previous json artifacts
|
||||||
|
uses: geekyeggo/delete-artifact@v5
|
||||||
|
with:
|
||||||
|
name: ${{ env.PLUGIN_NAME }}*-json
|
||||||
|
|
||||||
|
- name: Upload the modified JSON file as an artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: plugin-json
|
||||||
|
path: .github/scripts/${{ env.PLUGIN_NAME }}.json
|
||||||
|
|
||||||
|
release:
|
||||||
|
if: contains(github.ref, 'tags/v')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build, update_json]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
|
@ -92,7 +92,6 @@ add_qtc_plugin(QodeAssist
|
|||||||
providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp
|
providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp
|
||||||
providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp
|
providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp
|
||||||
providers/LlamaCppProvider.hpp providers/LlamaCppProvider.cpp
|
providers/LlamaCppProvider.hpp providers/LlamaCppProvider.cpp
|
||||||
providers/CodestralProvider.hpp providers/CodestralProvider.cpp
|
|
||||||
QodeAssist.qrc
|
QodeAssist.qrc
|
||||||
LSPCompletion.hpp
|
LSPCompletion.hpp
|
||||||
LLMSuggestion.hpp LLMSuggestion.cpp
|
LLMSuggestion.hpp LLMSuggestion.cpp
|
||||||
@ -106,7 +105,6 @@ add_qtc_plugin(QodeAssist
|
|||||||
widgets/ProgressWidget.hpp widgets/ProgressWidget.cpp
|
widgets/ProgressWidget.hpp widgets/ProgressWidget.cpp
|
||||||
widgets/EditorChatButton.hpp widgets/EditorChatButton.cpp
|
widgets/EditorChatButton.hpp widgets/EditorChatButton.cpp
|
||||||
widgets/EditorChatButtonHandler.hpp widgets/EditorChatButtonHandler.cpp
|
widgets/EditorChatButtonHandler.hpp widgets/EditorChatButtonHandler.cpp
|
||||||
widgets/QuickRefactorDialog.hpp widgets/QuickRefactorDialog.cpp
|
|
||||||
QuickRefactorHandler.hpp QuickRefactorHandler.cpp
|
QuickRefactorHandler.hpp QuickRefactorHandler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ qt_add_qml_module(QodeAssistChatView
|
|||||||
qml/parts/TopBar.qml
|
qml/parts/TopBar.qml
|
||||||
qml/parts/BottomBar.qml
|
qml/parts/BottomBar.qml
|
||||||
qml/parts/AttachedFilesPlace.qml
|
qml/parts/AttachedFilesPlace.qml
|
||||||
|
|
||||||
RESOURCES
|
RESOURCES
|
||||||
icons/attach-file-light.svg
|
icons/attach-file-light.svg
|
||||||
icons/attach-file-dark.svg
|
icons/attach-file-dark.svg
|
||||||
|
@ -124,7 +124,6 @@ QList<MessagePart> ChatModel::processMessageContent(const QString &content) cons
|
|||||||
QRegularExpression codeBlockRegex("```(\\w*)\\n?([\\s\\S]*?)```");
|
QRegularExpression codeBlockRegex("```(\\w*)\\n?([\\s\\S]*?)```");
|
||||||
int lastIndex = 0;
|
int lastIndex = 0;
|
||||||
auto blockMatches = codeBlockRegex.globalMatch(content);
|
auto blockMatches = codeBlockRegex.globalMatch(content);
|
||||||
bool foundCodeBlock = blockMatches.hasNext();
|
|
||||||
|
|
||||||
while (blockMatches.hasNext()) {
|
while (blockMatches.hasNext()) {
|
||||||
auto match = blockMatches.next();
|
auto match = blockMatches.next();
|
||||||
@ -141,19 +140,7 @@ QList<MessagePart> ChatModel::processMessageContent(const QString &content) cons
|
|||||||
|
|
||||||
if (lastIndex < content.length()) {
|
if (lastIndex < content.length()) {
|
||||||
QString remainingText = content.mid(lastIndex).trimmed();
|
QString remainingText = content.mid(lastIndex).trimmed();
|
||||||
|
if (!remainingText.isEmpty()) {
|
||||||
QRegularExpression unclosedBlockRegex("```(\\w*)\\n?([\\s\\S]*)$");
|
|
||||||
auto unclosedMatch = unclosedBlockRegex.match(remainingText);
|
|
||||||
|
|
||||||
if (unclosedMatch.hasMatch()) {
|
|
||||||
QString beforeCodeBlock = remainingText.left(unclosedMatch.capturedStart()).trimmed();
|
|
||||||
if (!beforeCodeBlock.isEmpty()) {
|
|
||||||
parts.append({MessagePart::Text, beforeCodeBlock, ""});
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.append(
|
|
||||||
{MessagePart::Code, unclosedMatch.captured(2).trimmed(), unclosedMatch.captured(1)});
|
|
||||||
} else if (!remainingText.isEmpty()) {
|
|
||||||
parts.append({MessagePart::Text, remainingText, ""});
|
parts.append({MessagePart::Text, remainingText, ""});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,16 +197,4 @@ QString ChatModel::lastMessageId() const
|
|||||||
return !m_messages.isEmpty() ? m_messages.last().id : "";
|
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()) {
|
|
||||||
beginRemoveRows(QModelIndex(), index, m_messages.size() - 1);
|
|
||||||
m_messages.remove(index, m_messages.size() - index);
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
@ -73,8 +73,6 @@ public:
|
|||||||
QString currentModel() const;
|
QString currentModel() const;
|
||||||
QString lastMessageId() const;
|
QString lastMessageId() const;
|
||||||
|
|
||||||
Q_INVOKABLE void resetModelTo(int index);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void tokensThresholdChanged();
|
void tokensThresholdChanged();
|
||||||
void modelReseted();
|
void modelReseted();
|
||||||
|
@ -102,31 +102,6 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(
|
|
||||||
&Settings::chatAssistantSettings().textFontFamily,
|
|
||||||
&Utils::BaseAspect::changed,
|
|
||||||
this,
|
|
||||||
&ChatRootView::textFamilyChanged);
|
|
||||||
connect(
|
|
||||||
&Settings::chatAssistantSettings().codeFontFamily,
|
|
||||||
&Utils::BaseAspect::changed,
|
|
||||||
this,
|
|
||||||
&ChatRootView::codeFamilyChanged);
|
|
||||||
connect(
|
|
||||||
&Settings::chatAssistantSettings().textFontSize,
|
|
||||||
&Utils::BaseAspect::changed,
|
|
||||||
this,
|
|
||||||
&ChatRootView::textFontSizeChanged);
|
|
||||||
connect(
|
|
||||||
&Settings::chatAssistantSettings().codeFontSize,
|
|
||||||
&Utils::BaseAspect::changed,
|
|
||||||
this,
|
|
||||||
&ChatRootView::codeFontSizeChanged);
|
|
||||||
connect(
|
|
||||||
&Settings::chatAssistantSettings().textFormat,
|
|
||||||
&Utils::BaseAspect::changed,
|
|
||||||
this,
|
|
||||||
&ChatRootView::textFormatChanged);
|
|
||||||
|
|
||||||
updateInputTokensCount();
|
updateInputTokensCount();
|
||||||
}
|
}
|
||||||
@ -535,7 +510,7 @@ void ChatRootView::onAppendLinkFileFromEditor(Core::IEditor *editor)
|
|||||||
{
|
{
|
||||||
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
||||||
QString filePath = document->filePath().toFSPathString();
|
QString filePath = document->filePath().toFSPathString();
|
||||||
if (!m_linkedFiles.contains(filePath) && !shouldIgnoreFileForAttach(document->filePath())) {
|
if (!m_linkedFiles.contains(filePath)) {
|
||||||
m_linkedFiles.append(filePath);
|
m_linkedFiles.append(filePath);
|
||||||
emit linkedFilesChanged();
|
emit linkedFilesChanged();
|
||||||
}
|
}
|
||||||
@ -562,44 +537,4 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ChatRootView::textFontFamily() const
|
|
||||||
{
|
|
||||||
return Settings::chatAssistantSettings().textFontFamily.stringValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ChatRootView::codeFontFamily() const
|
|
||||||
{
|
|
||||||
return Settings::chatAssistantSettings().codeFontFamily.stringValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChatRootView::codeFontSize() const
|
|
||||||
{
|
|
||||||
return Settings::chatAssistantSettings().codeFontSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChatRootView::textFontSize() const
|
|
||||||
{
|
|
||||||
return Settings::chatAssistantSettings().textFontSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChatRootView::textFormat() const
|
|
||||||
{
|
|
||||||
return Settings::chatAssistantSettings().textFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
@ -38,11 +38,6 @@ class ChatRootView : public QQuickItem
|
|||||||
Q_PROPERTY(QStringList linkedFiles READ linkedFiles NOTIFY linkedFilesChanged FINAL)
|
Q_PROPERTY(QStringList linkedFiles READ linkedFiles NOTIFY linkedFilesChanged FINAL)
|
||||||
Q_PROPERTY(int inputTokensCount READ inputTokensCount NOTIFY inputTokensCountChanged FINAL)
|
Q_PROPERTY(int inputTokensCount READ inputTokensCount NOTIFY inputTokensCountChanged FINAL)
|
||||||
Q_PROPERTY(QString chatFileName READ chatFileName NOTIFY chatFileNameChanged FINAL)
|
Q_PROPERTY(QString chatFileName READ chatFileName NOTIFY chatFileNameChanged FINAL)
|
||||||
Q_PROPERTY(QString textFontFamily READ textFontFamily NOTIFY textFamilyChanged FINAL)
|
|
||||||
Q_PROPERTY(QString codeFontFamily READ codeFontFamily NOTIFY codeFamilyChanged FINAL)
|
|
||||||
Q_PROPERTY(int codeFontSize READ codeFontSize NOTIFY codeFontSizeChanged FINAL)
|
|
||||||
Q_PROPERTY(int textFontSize READ textFontSize NOTIFY textFontSizeChanged FINAL)
|
|
||||||
Q_PROPERTY(int textFormat READ textFormat NOTIFY textFormatChanged FINAL)
|
|
||||||
|
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
@ -83,14 +78,6 @@ public:
|
|||||||
|
|
||||||
QString chatFileName() const;
|
QString chatFileName() const;
|
||||||
void setRecentFilePath(const QString &filePath);
|
void setRecentFilePath(const QString &filePath);
|
||||||
bool shouldIgnoreFileForAttach(const Utils::FilePath &filePath);
|
|
||||||
|
|
||||||
QString textFontFamily() const;
|
|
||||||
QString codeFontFamily() const;
|
|
||||||
|
|
||||||
int codeFontSize() const;
|
|
||||||
int textFontSize() const;
|
|
||||||
int textFormat() const;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void sendMessage(const QString &message);
|
void sendMessage(const QString &message);
|
||||||
@ -107,11 +94,6 @@ signals:
|
|||||||
void inputTokensCountChanged();
|
void inputTokensCountChanged();
|
||||||
void isSyncOpenFilesChanged();
|
void isSyncOpenFilesChanged();
|
||||||
void chatFileNameChanged();
|
void chatFileNameChanged();
|
||||||
void textFamilyChanged();
|
|
||||||
void codeFamilyChanged();
|
|
||||||
void codeFontSizeChanged();
|
|
||||||
void textFontSizeChanged();
|
|
||||||
void textFormatChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getChatsHistoryDir() const;
|
QString getChatsHistoryDir() const;
|
||||||
|
@ -29,40 +29,4 @@ void ChatUtils::copyToClipboard(const QString &text)
|
|||||||
QGuiApplication::clipboard()->setText(text);
|
QGuiApplication::clipboard()->setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ChatUtils::getSafeMarkdownText(const QString &text) const
|
|
||||||
{
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needsSanitization = false;
|
|
||||||
for (const QChar &ch : text) {
|
|
||||||
if (ch.isNull() || (!ch.isPrint() && ch != '\n' && ch != '\t' && ch != '\r' && ch != ' ')) {
|
|
||||||
needsSanitization = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsSanitization) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString safeText;
|
|
||||||
safeText.reserve(text.size());
|
|
||||||
|
|
||||||
for (QChar ch : text) {
|
|
||||||
if (ch.isNull()) {
|
|
||||||
safeText.append(' ');
|
|
||||||
} else if (ch == '\n' || ch == '\t' || ch == '\r' || ch == ' ') {
|
|
||||||
safeText.append(ch);
|
|
||||||
} else if (ch.isPrint()) {
|
|
||||||
safeText.append(ch);
|
|
||||||
} else {
|
|
||||||
safeText.append(QChar(0xFFFD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return safeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
@ -34,7 +34,6 @@ public:
|
|||||||
: QObject(parent) {};
|
: QObject(parent) {};
|
||||||
|
|
||||||
Q_INVOKABLE void copyToClipboard(const QString &text);
|
Q_INVOKABLE void copyToClipboard(const QString &text);
|
||||||
Q_INVOKABLE QString getSafeMarkdownText(const QString &text) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
@ -27,38 +27,13 @@ Rectangle {
|
|||||||
|
|
||||||
property alias msgModel: msgCreator.model
|
property alias msgModel: msgCreator.model
|
||||||
property alias messageAttachments: attachmentsModel.model
|
property alias messageAttachments: attachmentsModel.model
|
||||||
property string textFontFamily: Qt.application.font.family
|
|
||||||
property string codeFontFamily: {
|
|
||||||
switch (Qt.platform.os) {
|
|
||||||
case "windows":
|
|
||||||
return "Consolas";
|
|
||||||
case "osx":
|
|
||||||
return "Menlo";
|
|
||||||
case "linux":
|
|
||||||
return "DejaVu Sans Mono";
|
|
||||||
default:
|
|
||||||
return "monospace";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
property int textFontSize: Qt.application.font.pointSize
|
|
||||||
property int codeFontSize: Qt.application.font.pointSize
|
|
||||||
property int textFormat: 0
|
|
||||||
|
|
||||||
property bool isUserMessage: false
|
property bool isUserMessage: false
|
||||||
property int messageIndex: -1
|
|
||||||
property real listViewContentY: 0
|
|
||||||
|
|
||||||
signal resetChatToMessage(int index)
|
|
||||||
|
|
||||||
height: msgColumn.implicitHeight + 10
|
height: msgColumn.implicitHeight + 10
|
||||||
radius: 8
|
radius: 8
|
||||||
color: isUserMessage ? palette.alternateBase
|
color: isUserMessage ? palette.alternateBase
|
||||||
: palette.base
|
: palette.base
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: msgColumn
|
id: msgColumn
|
||||||
|
|
||||||
@ -102,8 +77,6 @@ Rectangle {
|
|||||||
id: codeBlockComponent
|
id: codeBlockComponent
|
||||||
CodeBlockComponent {
|
CodeBlockComponent {
|
||||||
itemData: msgCreatorDelegate.modelData
|
itemData: msgCreatorDelegate.modelData
|
||||||
blockStart: root.y + msgCreatorDelegate.y
|
|
||||||
currentContentY: root.listViewContentY
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,48 +128,16 @@ Rectangle {
|
|||||||
visible: root.isUserMessage
|
visible: root.isUserMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
QoAButton {
|
|
||||||
id: stopButtonId
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
}
|
|
||||||
|
|
||||||
text: qsTr("ResetTo")
|
|
||||||
visible: root.isUserMessage && mouse.hovered
|
|
||||||
onClicked: function() {
|
|
||||||
root.resetChatToMessage(root.messageIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component TextComponent : TextBlock {
|
component TextComponent : TextBlock {
|
||||||
required property var itemData
|
required property var itemData
|
||||||
height: implicitHeight + 10
|
height: implicitHeight + 10
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
text: textFormat == Text.MarkdownText ? utils.getSafeMarkdownText(itemData.text)
|
text: itemData.text
|
||||||
: itemData.text
|
|
||||||
font.family: root.textFontFamily
|
|
||||||
font.pointSize: root.textFontSize
|
|
||||||
textFormat: {
|
|
||||||
if (root.textFormat == 0) {
|
|
||||||
return Text.MarkdownText
|
|
||||||
} else if (root.textFormat == 1) {
|
|
||||||
return Text.RichText
|
|
||||||
} else {
|
|
||||||
return Text.PlainText
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatUtils {
|
|
||||||
id: utils
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component CodeBlockComponent : CodeBlock {
|
component CodeBlockComponent : CodeBlock {
|
||||||
id: codeblock
|
|
||||||
|
|
||||||
required property var itemData
|
required property var itemData
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
@ -207,7 +148,5 @@ Rectangle {
|
|||||||
|
|
||||||
code: itemData.text
|
code: itemData.text
|
||||||
language: itemData.language
|
language: itemData.language
|
||||||
codeFontFamily: root.codeFontFamily
|
|
||||||
codeFontSize: root.codeFontSize
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,25 +92,11 @@ ChatRootView {
|
|||||||
|
|
||||||
delegate: ChatItem {
|
delegate: ChatItem {
|
||||||
required property var model
|
required property var model
|
||||||
required property int index
|
|
||||||
|
|
||||||
width: ListView.view.width - scroll.width
|
width: ListView.view.width - scroll.width
|
||||||
msgModel: root.chatModel.processMessageContent(model.content)
|
msgModel: root.chatModel.processMessageContent(model.content)
|
||||||
messageAttachments: model.attachments
|
messageAttachments: model.attachments
|
||||||
isUserMessage: model.roleType === ChatModel.User
|
isUserMessage: model.roleType === ChatModel.User
|
||||||
messageIndex: index
|
|
||||||
listViewContentY: chatListView.contentY
|
|
||||||
textFontFamily: root.textFontFamily
|
|
||||||
codeFontFamily: root.codeFontFamily
|
|
||||||
codeFontSize: root.codeFontSize
|
|
||||||
textFontSize: root.textFontSize
|
|
||||||
textFormat: root.textFormat
|
|
||||||
|
|
||||||
onResetChatToMessage: function(index) {
|
|
||||||
messageInput.text = model.content
|
|
||||||
messageInput.cursorPosition = model.content.length
|
|
||||||
root.chatModel.resetModelTo(index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header: Item {
|
header: Item {
|
||||||
|
@ -27,25 +27,17 @@ Rectangle {
|
|||||||
property string code: ""
|
property string code: ""
|
||||||
property string language: ""
|
property string language: ""
|
||||||
|
|
||||||
property real currentContentY: 0
|
readonly property string monospaceFont: {
|
||||||
property real blockStart: 0
|
switch (Qt.platform.os) {
|
||||||
|
case "windows":
|
||||||
property alias codeFontFamily: codeText.font.family
|
return "Consolas";
|
||||||
property alias codeFontSize: codeText.font.pointSize
|
case "osx":
|
||||||
|
return "Menlo";
|
||||||
readonly property real buttonTopMargin: 5
|
case "linux":
|
||||||
readonly property real blockEnd: blockStart + root.height
|
return "DejaVu Sans Mono";
|
||||||
readonly property real maxButtonOffset: Math.max(0, root.height - copyButton.height - buttonTopMargin)
|
default:
|
||||||
|
return "monospace";
|
||||||
readonly property real buttonPosition: {
|
|
||||||
if (currentContentY > blockEnd) {
|
|
||||||
return buttonTopMargin;
|
|
||||||
}
|
}
|
||||||
else if (currentContentY > blockStart) {
|
|
||||||
let offset = currentContentY - blockStart;
|
|
||||||
return Math.min(offset, maxButtonOffset);
|
|
||||||
}
|
|
||||||
return buttonTopMargin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
color: palette.alternateBase
|
color: palette.alternateBase
|
||||||
@ -53,6 +45,7 @@ Rectangle {
|
|||||||
: Qt.lighter(root.color, 1.3)
|
: Qt.lighter(root.color, 1.3)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
implicitWidth: parent.width
|
implicitWidth: parent.width
|
||||||
implicitHeight: codeText.implicitHeight + 20
|
implicitHeight: codeText.implicitHeight + 20
|
||||||
|
|
||||||
@ -62,11 +55,14 @@ Rectangle {
|
|||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: codeText
|
id: codeText
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
anchors.margins: 10
|
||||||
text: root.code
|
text: root.code
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
font.family: root.monospaceFont
|
||||||
|
font.pointSize: Qt.application.font.pointSize
|
||||||
color: parent.color.hslLightness > 0.5 ? "black" : "white"
|
color: parent.color.hslLightness > 0.5 ? "black" : "white"
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
selectionColor: palette.highlight
|
selectionColor: palette.highlight
|
||||||
@ -81,20 +77,14 @@ Rectangle {
|
|||||||
text: root.language
|
text: root.language
|
||||||
color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.1)
|
color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.1)
|
||||||
: Qt.lighter(root.color, 1.1)
|
: Qt.lighter(root.color, 1.1)
|
||||||
font.pointSize: codeText.font.pointSize - 4
|
font.pointSize: 8
|
||||||
}
|
}
|
||||||
|
|
||||||
QoAButton {
|
QoAButton {
|
||||||
id: copyButton
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
anchors {
|
anchors.margins: 5
|
||||||
top: parent.top
|
text: "Copy"
|
||||||
topMargin: root.buttonPosition
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: root.buttonTopMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
text: qsTr("Copy")
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
utils.copyToClipboard(root.code)
|
utils.copyToClipboard(root.code)
|
||||||
text = qsTr("Copied")
|
text = qsTr("Copied")
|
||||||
|
@ -25,6 +25,7 @@ TextEdit {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
textFormat: Text.StyledText
|
||||||
selectionColor: palette.highlight
|
selectionColor: palette.highlight
|
||||||
color: palette.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
@ -57,17 +57,6 @@ LLMClientInterface::LLMClientInterface(
|
|||||||
&LLMCore::RequestHandler::completionReceived,
|
&LLMCore::RequestHandler::completionReceived,
|
||||||
this,
|
this,
|
||||||
&LLMClientInterface::sendCompletionToClient);
|
&LLMClientInterface::sendCompletionToClient);
|
||||||
|
|
||||||
// TODO handle error
|
|
||||||
// connect(
|
|
||||||
// &m_requestHandler,
|
|
||||||
// &LLMCore::RequestHandler::requestFinished,
|
|
||||||
// this,
|
|
||||||
// [this](const QString &, bool success, const QString &errorString) {
|
|
||||||
// if (!success) {
|
|
||||||
// emit error(errorString);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
||||||
@ -218,8 +207,10 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
|||||||
: QString{"generateContent?"};
|
: QString{"generateContent?"};
|
||||||
config.url = QUrl(QString("%1/models/%2:%3").arg(url, modelName, stream));
|
config.url = QUrl(QString("%1/models/%2:%3").arg(url, modelName, stream));
|
||||||
} else {
|
} else {
|
||||||
config.url = QUrl(
|
config.url = QUrl(QString("%1%2").arg(
|
||||||
QString("%1%2").arg(url, endpoint(provider, promptTemplate->type(), isPreset1Active)));
|
url,
|
||||||
|
promptTemplate->type() == LLMCore::TemplateType::FIM ? provider->completionEndpoint()
|
||||||
|
: provider->chatEndpoint()));
|
||||||
config.providerRequest = {{"model", modelName}, {"stream", m_completeSettings.stream()}};
|
config.providerRequest = {{"model", modelName}, {"stream", m_completeSettings.stream()}};
|
||||||
}
|
}
|
||||||
config.apiKey = provider->apiKey();
|
config.apiKey = provider->apiKey();
|
||||||
@ -298,31 +289,6 @@ LLMCore::ContextData LLMClientInterface::prepareContext(
|
|||||||
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
|
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LLMClientInterface::endpoint(
|
|
||||||
LLMCore::Provider *provider, LLMCore::TemplateType type, bool isLanguageSpecify)
|
|
||||||
{
|
|
||||||
QString endpoint;
|
|
||||||
auto endpointMode = isLanguageSpecify ? m_generalSettings.ccPreset1EndpointMode.stringValue()
|
|
||||||
: m_generalSettings.ccEndpointMode.stringValue();
|
|
||||||
if (endpointMode == "Auto") {
|
|
||||||
endpoint = type == LLMCore::TemplateType::FIM ? provider->completionEndpoint()
|
|
||||||
: provider->chatEndpoint();
|
|
||||||
} else if (endpointMode == "Custom") {
|
|
||||||
endpoint = isLanguageSpecify ? m_generalSettings.ccPreset1CustomEndpoint()
|
|
||||||
: m_generalSettings.ccCustomEndpoint();
|
|
||||||
} else if (endpointMode == "FIM") {
|
|
||||||
endpoint = provider->completionEndpoint();
|
|
||||||
} else if (endpointMode == "Chat") {
|
|
||||||
endpoint = provider->chatEndpoint();
|
|
||||||
}
|
|
||||||
return endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::ContextManager *LLMClientInterface::contextManager() const
|
|
||||||
{
|
|
||||||
return m_contextManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::sendCompletionToClient(
|
void LLMClientInterface::sendCompletionToClient(
|
||||||
const QString &completion, const QJsonObject &request, bool isComplete)
|
const QString &completion, const QJsonObject &request, bool isComplete)
|
||||||
{
|
{
|
||||||
@ -356,9 +322,7 @@ void LLMClientInterface::sendCompletionToClient(
|
|||||||
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
||||||
QJsonObject range;
|
QJsonObject range;
|
||||||
range["start"] = position;
|
range["start"] = position;
|
||||||
QJsonObject end = position;
|
range["end"] = position;
|
||||||
end["character"] = position["character"].toInt() + processedCompletion.length();
|
|
||||||
range["end"] = end;
|
|
||||||
completionItem[LanguageServerProtocol::rangeKey] = range;
|
completionItem[LanguageServerProtocol::rangeKey] = range;
|
||||||
completionItem[LanguageServerProtocol::positionKey] = position;
|
completionItem[LanguageServerProtocol::positionKey] = position;
|
||||||
completions.append(completionItem);
|
completions.append(completionItem);
|
||||||
|
@ -62,8 +62,6 @@ public:
|
|||||||
// exposed for tests
|
// exposed for tests
|
||||||
void sendData(const QByteArray &data) override;
|
void sendData(const QByteArray &data) override;
|
||||||
|
|
||||||
Context::ContextManager *contextManager() const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startImpl() override;
|
void startImpl() override;
|
||||||
|
|
||||||
@ -77,7 +75,6 @@ private:
|
|||||||
|
|
||||||
LLMCore::ContextData prepareContext(
|
LLMCore::ContextData prepareContext(
|
||||||
const QJsonObject &request, const Context::DocumentInfo &documentInfo);
|
const QJsonObject &request, const Context::DocumentInfo &documentInfo);
|
||||||
QString endpoint(LLMCore::Provider *provider, LLMCore::TemplateType type, bool isLanguageSpecify);
|
|
||||||
|
|
||||||
const Settings::CodeCompletionSettings &m_completeSettings;
|
const Settings::CodeCompletionSettings &m_completeSettings;
|
||||||
const Settings::GeneralSettings &m_generalSettings;
|
const Settings::GeneralSettings &m_generalSettings;
|
||||||
|
@ -29,36 +29,6 @@
|
|||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
QString mergeWithRightText(const QString &suggestion, const QString &rightText)
|
|
||||||
{
|
|
||||||
if (suggestion.isEmpty() || rightText.isEmpty()) {
|
|
||||||
return suggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
int j = 0;
|
|
||||||
QString processed = rightText;
|
|
||||||
QSet<int> matchedPositions;
|
|
||||||
|
|
||||||
for (int i = 0; i < suggestion.length() && j < processed.length(); ++i) {
|
|
||||||
if (suggestion[i] == processed[j]) {
|
|
||||||
matchedPositions.insert(j);
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchedPositions.isEmpty()) {
|
|
||||||
return suggestion + rightText;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<int> positions = matchedPositions.values();
|
|
||||||
std::sort(positions.begin(), positions.end(), std::greater<int>());
|
|
||||||
for (int pos : positions) {
|
|
||||||
processed.remove(pos, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return suggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
LLMSuggestion::LLMSuggestion(
|
LLMSuggestion::LLMSuggestion(
|
||||||
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion)
|
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion)
|
||||||
: TextEditor::CyclicSuggestion(suggestions, sourceDocument, currentCompletion)
|
: TextEditor::CyclicSuggestion(suggestions, sourceDocument, currentCompletion)
|
||||||
@ -68,28 +38,21 @@ LLMSuggestion::LLMSuggestion(
|
|||||||
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
||||||
int endPos = data.range.end.toPositionInDocument(sourceDocument);
|
int endPos = data.range.end.toPositionInDocument(sourceDocument);
|
||||||
|
|
||||||
startPos = qBound(0, startPos, sourceDocument->characterCount());
|
startPos = qBound(0, startPos, sourceDocument->characterCount() - 1);
|
||||||
endPos = qBound(startPos, endPos, sourceDocument->characterCount());
|
endPos = qBound(startPos, endPos, sourceDocument->characterCount() - 1);
|
||||||
|
|
||||||
QTextCursor cursor(sourceDocument);
|
QTextCursor cursor(sourceDocument);
|
||||||
cursor.setPosition(startPos);
|
cursor.setPosition(startPos);
|
||||||
|
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
QTextBlock block = cursor.block();
|
QTextBlock block = cursor.block();
|
||||||
QString blockText = block.text();
|
QString blockText = block.text();
|
||||||
|
|
||||||
int cursorPositionInBlock = cursor.positionInBlock();
|
int startPosInBlock = startPos - block.position();
|
||||||
|
int endPosInBlock = endPos - block.position();
|
||||||
|
|
||||||
QString rightText = blockText.mid(cursorPositionInBlock);
|
blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text);
|
||||||
|
replacementDocument()->setPlainText(blockText);
|
||||||
if (!data.text.contains('\n')) {
|
|
||||||
QString processedRightText = mergeWithRightText(data.text, rightText);
|
|
||||||
processedRightText = processedRightText.mid(data.text.length());
|
|
||||||
QString displayText = blockText.left(cursorPositionInBlock) + data.text
|
|
||||||
+ processedRightText;
|
|
||||||
replacementDocument()->setPlainText(displayText);
|
|
||||||
} else {
|
|
||||||
QString displayText = blockText.left(cursorPositionInBlock) + data.text;
|
|
||||||
replacementDocument()->setPlainText(displayText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
||||||
@ -114,82 +77,31 @@ bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
|||||||
|
|
||||||
int next = part == Word ? Utils::endOfNextWord(text, startPos) : text.indexOf('\n', startPos);
|
int next = part == Word ? Utils::endOfNextWord(text, startPos) : text.indexOf('\n', startPos);
|
||||||
|
|
||||||
if (next == -1) {
|
if (next == -1)
|
||||||
if (part == Line) {
|
|
||||||
next = text.length();
|
|
||||||
} else {
|
|
||||||
return apply();
|
return apply();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part == Line)
|
if (part == Line)
|
||||||
++next;
|
++next;
|
||||||
|
|
||||||
QString subText = text.mid(startPos, next - startPos);
|
QString subText = text.mid(startPos, next - startPos);
|
||||||
|
if (subText.isEmpty())
|
||||||
if (subText.isEmpty()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
QTextBlock currentBlock = currentCursor.block();
|
|
||||||
QString textAfterCursor = currentBlock.text().mid(currentCursor.positionInBlock());
|
|
||||||
|
|
||||||
if (!subText.contains('\n')) {
|
|
||||||
QTextCursor deleteCursor = currentCursor;
|
|
||||||
deleteCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
||||||
deleteCursor.removeSelectedText();
|
|
||||||
|
|
||||||
QString mergedText = mergeWithRightText(subText, textAfterCursor);
|
|
||||||
currentCursor.insertText(mergedText);
|
|
||||||
} else {
|
|
||||||
currentCursor.insertText(subText);
|
currentCursor.insertText(subText);
|
||||||
|
|
||||||
if (const int seperatorPos = subText.lastIndexOf('\n'); seperatorPos >= 0) {
|
if (const int seperatorPos = subText.lastIndexOf('\n'); seperatorPos >= 0) {
|
||||||
const QString newCompletionText = text.mid(startPos + seperatorPos + 1);
|
const QString newCompletionText = text.mid(startPos + seperatorPos + 1);
|
||||||
if (!newCompletionText.isEmpty()) {
|
if (!newCompletionText.isEmpty()) {
|
||||||
const Utils::Text::Position newStart{int(range.begin.line + subText.count('\n')), 0};
|
const Utils::Text::Position newStart{int(range.begin.line + subText.count('\n')), 0};
|
||||||
const Utils::Text::Position newEnd{newStart.line, int(newCompletionText.length())};
|
const Utils::Text::Position
|
||||||
|
newEnd{newStart.line, int(subText.length() - seperatorPos - 1)};
|
||||||
const Utils::Text::Range newRange{newStart, newEnd};
|
const Utils::Text::Range newRange{newStart, newEnd};
|
||||||
const QList<Data> newSuggestion{{newRange, newEnd, newCompletionText}};
|
const QList<Data> newSuggestion{{newRange, newEnd, newCompletionText}};
|
||||||
widget->insertSuggestion(
|
widget->insertSuggestion(
|
||||||
std::make_unique<LLMSuggestion>(newSuggestion, widget->document(), 0));
|
std::make_unique<LLMSuggestion>(newSuggestion, widget->document(), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMSuggestion::apply()
|
|
||||||
{
|
|
||||||
const Utils::Text::Range range = suggestions()[currentSuggestion()].range;
|
|
||||||
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
|
||||||
const QString text = suggestions()[currentSuggestion()].text;
|
|
||||||
|
|
||||||
QTextBlock currentBlock = cursor.block();
|
|
||||||
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
|
|
||||||
|
|
||||||
QTextCursor editCursor = cursor;
|
|
||||||
|
|
||||||
int firstLineEnd = text.indexOf('\n');
|
|
||||||
if (firstLineEnd != -1) {
|
|
||||||
QString firstLine = text.left(firstLineEnd);
|
|
||||||
QString restOfText = text.mid(firstLineEnd);
|
|
||||||
|
|
||||||
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
||||||
editCursor.removeSelectedText();
|
|
||||||
|
|
||||||
QString mergedFirstLine = mergeWithRightText(firstLine, textAfterCursor);
|
|
||||||
editCursor.insertText(mergedFirstLine + restOfText);
|
|
||||||
} else {
|
|
||||||
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
||||||
editCursor.removeSelectedText();
|
|
||||||
|
|
||||||
QString mergedText = mergeWithRightText(text, textAfterCursor);
|
|
||||||
editCursor.insertText(mergedText);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -40,6 +40,5 @@ public:
|
|||||||
bool applyWord(TextEditor::TextEditorWidget *widget) override;
|
bool applyWord(TextEditor::TextEditorWidget *widget) override;
|
||||||
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
||||||
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
|
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
|
||||||
bool apply() override;
|
|
||||||
};
|
};
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Id" : "qodeassist",
|
"Id" : "qodeassist",
|
||||||
"Name" : "QodeAssist",
|
"Name" : "QodeAssist",
|
||||||
"Version" : "0.5.13",
|
"Version" : "0.5.7",
|
||||||
"Vendor" : "Petr Mironychev",
|
"Vendor" : "Petr Mironychev",
|
||||||
"VendorId" : "petrmironychev",
|
"VendorId" : "petrmironychev",
|
||||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||||
|
@ -2,11 +2,5 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>resources/images/qoderassist-icon@2x.png</file>
|
<file>resources/images/qoderassist-icon@2x.png</file>
|
||||||
<file>resources/images/qoderassist-icon.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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -49,7 +49,6 @@ namespace QodeAssist {
|
|||||||
|
|
||||||
QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface)
|
QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface)
|
||||||
: LanguageClient::Client(clientInterface)
|
: LanguageClient::Client(clientInterface)
|
||||||
, m_llmClient(clientInterface)
|
|
||||||
, m_recentCharCount(0)
|
, m_recentCharCount(0)
|
||||||
{
|
{
|
||||||
setName("QodeAssist");
|
setName("QodeAssist");
|
||||||
@ -153,14 +152,6 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor)
|
|||||||
if (!isEnabled(project))
|
if (!isEnabled(project))
|
||||||
return;
|
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();
|
MultiTextCursor cursor = editor->multiTextCursor();
|
||||||
if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
|
if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
|
||||||
return;
|
return;
|
||||||
@ -190,14 +181,6 @@ void QodeAssistClient::requestQuickRefactor(
|
|||||||
if (!isEnabled(project))
|
if (!isEnabled(project))
|
||||||
return;
|
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) {
|
if (!m_refactorHandler) {
|
||||||
m_refactorHandler = new QuickRefactorHandler(this);
|
m_refactorHandler = new QuickRefactorHandler(this);
|
||||||
connect(
|
connect(
|
||||||
@ -344,6 +327,7 @@ void QodeAssistClient::cleanupConnections()
|
|||||||
|
|
||||||
void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
|
void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
|
||||||
{
|
{
|
||||||
|
m_progressHandler.hideProgress();
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
LOG_MESSAGE(QString("Refactoring failed: %1").arg(result.errorMessage));
|
LOG_MESSAGE(QString("Refactoring failed: %1").arg(result.errorMessage));
|
||||||
return;
|
return;
|
||||||
@ -368,6 +352,5 @@ void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
|
|||||||
|
|
||||||
cursor.insertText(result.newText);
|
cursor.insertText(result.newText);
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_progressHandler.hideProgress();
|
|
||||||
}
|
}
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -69,7 +69,6 @@ private:
|
|||||||
CompletionProgressHandler m_progressHandler;
|
CompletionProgressHandler m_progressHandler;
|
||||||
EditorChatButtonHandler m_chatButtonHandler;
|
EditorChatButtonHandler m_chatButtonHandler;
|
||||||
QuickRefactorHandler *m_refactorHandler{nullptr};
|
QuickRefactorHandler *m_refactorHandler{nullptr};
|
||||||
LLMClientInterface *m_llmClient;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
66
README.md
66
README.md
@ -2,8 +2,7 @@
|
|||||||
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
[](https://discord.gg/BGMkUsXUgf)
|
[](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.
|
 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.
|
||||||
@ -24,12 +23,10 @@
|
|||||||
7. [Configure for Ollama](#configure-for-ollama)
|
7. [Configure for Ollama](#configure-for-ollama)
|
||||||
8. [Configure for llama.cpp](#configure-for-llamacpp)
|
8. [Configure for llama.cpp](#configure-for-llamacpp)
|
||||||
9. [System Prompt Configuration](#system-prompt-configuration)
|
9. [System Prompt Configuration](#system-prompt-configuration)
|
||||||
10. [File Context Feature](#file-context-feature)
|
10. [File Context Features](#file-context-features)
|
||||||
11. [Quick Refactoring Feature](#quick-refactoring-feature)
|
11. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
||||||
12. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
12. [Development Progress](#development-progress)
|
||||||
13. [Development Progress](#development-progress)
|
13. [Hotkeys](#hotkeys)
|
||||||
14. [Hotkeys](#hotkeys)
|
|
||||||
15. [Ignoring Files](#ignoring-files)
|
|
||||||
14. [Troubleshooting](#troubleshooting)
|
14. [Troubleshooting](#troubleshooting)
|
||||||
15. [Support the Development](#support-the-development-of-qodeassist)
|
15. [Support the Development](#support-the-development-of-qodeassist)
|
||||||
16. [How to Build](#how-to-build)
|
16. [How to Build](#how-to-build)
|
||||||
@ -38,7 +35,6 @@
|
|||||||
|
|
||||||
- AI-powered code completion
|
- AI-powered code completion
|
||||||
- Sharing IDE opened files with model context (disabled by default, need enable in settings)
|
- 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:
|
- Chat functionality:
|
||||||
- Side and Bottom panels
|
- Side and Bottom panels
|
||||||
- Chat history autosave and restore
|
- Chat history autosave and restore
|
||||||
@ -65,11 +61,6 @@ 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">
|
<img src="https://github.com/user-attachments/assets/255a52f1-5cc0-4ca3-b05c-c4cf9cdbe25a" width="600" alt="QodeAssistPreview">
|
||||||
</details>
|
</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>
|
<details>
|
||||||
<summary>Multiline Code completion: (click to expand)</summary>
|
<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">
|
<img src="https://github.com/user-attachments/assets/c18dfbd2-8c54-4a7b-90d1-66e3bb51adb0" width="600" alt="QodeAssistPreview">
|
||||||
@ -204,7 +195,7 @@ You're all set! QodeAssist is now ready to use in Qt Creator.
|
|||||||
|
|
||||||
The plugin comes with default system prompts optimized for chat and instruct models, as these currently provide better results for code assistance. If you prefer using FIM (Fill-in-Middle) models, you can easily customize the system prompt in the settings.
|
The plugin comes with default system prompts optimized for chat and instruct models, as these currently provide better results for code assistance. If you prefer using FIM (Fill-in-Middle) models, you can easily customize the system prompt in the settings.
|
||||||
|
|
||||||
## File Context Feature
|
## File Context Features
|
||||||
|
|
||||||
QodeAssist provides two powerful ways to include source code files in your chat conversations: Attachments and Linked Files. Each serves a distinct purpose and helps provide better context for the AI assistant.
|
QodeAssist provides two powerful ways to include source code files in your chat conversations: Attachments and Linked Files. Each serves a distinct purpose and helps provide better context for the AI assistant.
|
||||||
|
|
||||||
@ -237,23 +228,9 @@ Linked files provide persistent context throughout the conversation:
|
|||||||
- Supports automatic syncing with open editor files (can be enabled in settings)
|
- Supports automatic syncing with open editor files (can be enabled in settings)
|
||||||
- Files can be added/removed at any time during the conversation
|
- Files can be added/removed at any time during the conversation
|
||||||
|
|
||||||
## Quick Refactoring Feature
|
|
||||||
### Setup
|
|
||||||
Since this is actually a small chat with redirected output, the main settings of the provider, model and template are taken from the chat settings
|
|
||||||
### Using
|
|
||||||
The request to model consist of instructions to model, selection code and cursor position
|
|
||||||
The default instruction is: "Refactor the code to improve its quality and maintainability." and sending if text field is empty
|
|
||||||
Also there buttons to quick call instractions:
|
|
||||||
* Repeat latest instruction, will activate after sending first request in QtCreator session
|
|
||||||
* Improve current selection code
|
|
||||||
* Suggestion alternative variant of selection code
|
|
||||||
* Other instructions[TBD]
|
|
||||||
|
|
||||||
## QtCreator Version Compatibility
|
## QtCreator Version Compatibility
|
||||||
|
|
||||||
- QtCreator 16.0.2 - 0.5.13 - 0.x.x
|
- 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.1 - 0.4.8 - 0.5.1
|
||||||
- QtCreator 15.0.0 - 0.4.0 - 0.4.7
|
- QtCreator 15.0.0 - 0.4.0 - 0.4.7
|
||||||
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
|
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
|
||||||
@ -267,7 +244,6 @@ Linked files provide persistent context throughout the conversation:
|
|||||||
- [x] Sharing diff with model
|
- [x] Sharing diff with model
|
||||||
- [ ] Sharing project source with model
|
- [ ] Sharing project source with model
|
||||||
- [ ] Support for more providers and models
|
- [ ] Support for more providers and models
|
||||||
- [ ] Support MCP
|
|
||||||
|
|
||||||
## Hotkeys
|
## Hotkeys
|
||||||
|
|
||||||
@ -277,34 +253,6 @@ Linked files provide persistent context throughout the conversation:
|
|||||||
- on Linux with KDE Plasma: Ctrl + Alt + Q
|
- on Linux with KDE Plasma: Ctrl + Alt + Q
|
||||||
- To insert the full suggestion, you can use the TAB key
|
- 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 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
|
|
||||||
|
|
||||||
## Ignoring Files
|
|
||||||
QodeAssist supports the ability to ignore files in context using a .qodeassistignore file. This allows you to exclude specific files from the context during code completion and in the chat assistant, which is especially useful for large projects.
|
|
||||||
|
|
||||||
### How to Use .qodeassistignore
|
|
||||||
- Create a .qodeassistignore file in the root directory of your project near CMakeLists.txt or pro.
|
|
||||||
- Add patterns for files and directories that should be excluded from the context.
|
|
||||||
- QodeAssist will automatically detect this file and apply the exclusion rules.
|
|
||||||
|
|
||||||
### .qodeassistignore File Format
|
|
||||||
The file format is similar to .gitignore:
|
|
||||||
- Each pattern is written on a separate line
|
|
||||||
- Empty lines are ignored
|
|
||||||
- Lines starting with # are considered comments
|
|
||||||
- Standard wildcards work the same as in .gitignore
|
|
||||||
- To negate a pattern, use ! at the beginning of the line
|
|
||||||
```
|
|
||||||
# Ignore all files in the build directory
|
|
||||||
/build
|
|
||||||
*.tmp
|
|
||||||
# Ignore a specific file
|
|
||||||
src/generated/autogen.cpp
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ add_library(Context STATIC
|
|||||||
TokenUtils.hpp TokenUtils.cpp
|
TokenUtils.hpp TokenUtils.cpp
|
||||||
ProgrammingLanguage.hpp ProgrammingLanguage.cpp
|
ProgrammingLanguage.hpp ProgrammingLanguage.cpp
|
||||||
IContextManager.hpp
|
IContextManager.hpp
|
||||||
IgnoreManager.hpp IgnoreManager.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(Context
|
target_link_libraries(Context
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include "settings/GeneralSettings.hpp"
|
#include "settings/GeneralSettings.hpp"
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/projectmanager.h>
|
|
||||||
#include <projectexplorer/projectnodes.h>
|
#include <projectexplorer/projectnodes.h>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
@ -37,7 +36,6 @@ namespace QodeAssist::Context {
|
|||||||
|
|
||||||
ContextManager::ContextManager(QObject *parent)
|
ContextManager::ContextManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_ignoreManager(new IgnoreManager(this))
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QString ContextManager::readFile(const QString &filePath) const
|
QString ContextManager::readFile(const QString &filePath) const
|
||||||
@ -54,13 +52,6 @@ QList<ContentFile> ContextManager::getContentFiles(const QStringList &filePaths)
|
|||||||
{
|
{
|
||||||
QList<ContentFile> files;
|
QList<ContentFile> files;
|
||||||
for (const QString &path : filePaths) {
|
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);
|
ContentFile contentFile = createContentFile(path);
|
||||||
files.append(contentFile);
|
files.append(contentFile);
|
||||||
}
|
}
|
||||||
@ -130,14 +121,6 @@ QList<QPair<QString, QString>> ContextManager::openedFiles(const QStringList exc
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto filePath = textDocument->filePath().toUrlishString();
|
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)) {
|
if (!excludeFiles.contains(filePath)) {
|
||||||
files.append({filePath, textDocument->plainText()});
|
files.append({filePath, textDocument->plainText()});
|
||||||
}
|
}
|
||||||
@ -161,13 +144,6 @@ QString ContextManager::openedFilesContext(const QStringList excludeFiles)
|
|||||||
if (excludeFiles.contains(filePath))
|
if (excludeFiles.contains(filePath))
|
||||||
continue;
|
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 += QString("File: %1\n").arg(filePath);
|
||||||
context += textDocument->plainText();
|
context += textDocument->plainText();
|
||||||
|
|
||||||
@ -177,9 +153,4 @@ QString ContextManager::openedFilesContext(const QStringList excludeFiles)
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
IgnoreManager *ContextManager::ignoreManager() const
|
|
||||||
{
|
|
||||||
return m_ignoreManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Context
|
} // namespace QodeAssist::Context
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include "ContentFile.hpp"
|
#include "ContentFile.hpp"
|
||||||
#include "IContextManager.hpp"
|
#include "IContextManager.hpp"
|
||||||
#include "IgnoreManager.hpp"
|
|
||||||
#include "ProgrammingLanguage.hpp"
|
#include "ProgrammingLanguage.hpp"
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
@ -50,11 +49,6 @@ public:
|
|||||||
bool isSpecifyCompletion(const DocumentInfo &documentInfo) const override;
|
bool isSpecifyCompletion(const DocumentInfo &documentInfo) const override;
|
||||||
QList<QPair<QString, QString>> openedFiles(const QStringList excludeFiles = QStringList{}) const;
|
QList<QPair<QString, QString>> openedFiles(const QStringList excludeFiles = QStringList{}) const;
|
||||||
QString openedFilesContext(const QStringList excludeFiles = QStringList{});
|
QString openedFilesContext(const QStringList excludeFiles = QStringList{});
|
||||||
|
|
||||||
IgnoreManager *ignoreManager() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
IgnoreManager *m_ignoreManager;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Context
|
} // namespace QodeAssist::Context
|
||||||
|
@ -1,274 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
@ -22,51 +22,14 @@
|
|||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
namespace QodeAssist::LLMCore {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
RequestHandler::RequestHandler(QObject *parent)
|
RequestHandler::RequestHandler(QObject *parent)
|
||||||
: RequestHandlerBase(parent)
|
: RequestHandlerBase(parent)
|
||||||
, m_manager(new QNetworkAccessManager(this))
|
{}
|
||||||
{
|
|
||||||
connect(
|
|
||||||
this,
|
|
||||||
&RequestHandler::doSendRequest,
|
|
||||||
this,
|
|
||||||
&RequestHandler::sendLLMRequestInternal,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(
|
|
||||||
this,
|
|
||||||
&RequestHandler::doCancelRequest,
|
|
||||||
this,
|
|
||||||
&RequestHandler::cancelRequestInternal,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestHandler::~RequestHandler()
|
|
||||||
{
|
|
||||||
for (auto reply : m_activeRequests) {
|
|
||||||
reply->abort();
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
m_activeRequests.clear();
|
|
||||||
m_accumulatedResponses.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request)
|
void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request)
|
||||||
{
|
|
||||||
emit doSendRequest(config, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestHandler::cancelRequest(const QString &id)
|
|
||||||
{
|
|
||||||
emit doCancelRequest(id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestHandler::sendLLMRequestInternal(const LLMConfig &config, const QJsonObject &request)
|
|
||||||
{
|
{
|
||||||
LOG_MESSAGE(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
LOG_MESSAGE(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
||||||
.arg(
|
.arg(
|
||||||
@ -74,13 +37,12 @@ void RequestHandler::sendLLMRequestInternal(const LLMConfig &config, const QJson
|
|||||||
QString::fromUtf8(
|
QString::fromUtf8(
|
||||||
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
|
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
|
||||||
|
|
||||||
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
QNetworkRequest networkRequest(config.url);
|
QNetworkRequest networkRequest(config.url);
|
||||||
networkRequest.setTransferTimeout(300000);
|
|
||||||
|
|
||||||
config.provider->prepareNetworkRequest(networkRequest);
|
config.provider->prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
QNetworkReply *reply
|
QNetworkReply *reply
|
||||||
= m_manager->post(networkRequest, QJsonDocument(config.providerRequest).toJson());
|
= manager->post(networkRequest, QJsonDocument(config.providerRequest).toJson());
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
LOG_MESSAGE("Error: Failed to create network reply");
|
LOG_MESSAGE("Error: Failed to create network reply");
|
||||||
return;
|
return;
|
||||||
@ -93,11 +55,7 @@ void RequestHandler::sendLLMRequestInternal(const LLMConfig &config, const QJson
|
|||||||
handleLLMResponse(reply, request, config);
|
handleLLMResponse(reply, request, config);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(
|
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId, manager]() {
|
||||||
reply,
|
|
||||||
&QNetworkReply::finished,
|
|
||||||
this,
|
|
||||||
[this, reply, requestId]() {
|
|
||||||
m_activeRequests.remove(requestId);
|
m_activeRequests.remove(requestId);
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
QString errorMessage = reply->errorString();
|
QString errorMessage = reply->errorString();
|
||||||
@ -113,8 +71,8 @@ void RequestHandler::sendLLMRequestInternal(const LLMConfig &config, const QJson
|
|||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
},
|
manager->deleteLater();
|
||||||
Qt::QueuedConnection);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandler::handleLLMResponse(
|
void RequestHandler::handleLLMResponse(
|
||||||
@ -144,18 +102,17 @@ void RequestHandler::handleLLMResponse(
|
|||||||
m_accumulatedResponses.remove(reply);
|
m_accumulatedResponses.remove(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandler::cancelRequestInternal(const QString &id)
|
bool RequestHandler::cancelRequest(const QString &id)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (m_activeRequests.contains(id)) {
|
if (m_activeRequests.contains(id)) {
|
||||||
QNetworkReply *reply = m_activeRequests[id];
|
QNetworkReply *reply = m_activeRequests[id];
|
||||||
reply->abort();
|
reply->abort();
|
||||||
m_activeRequests.remove(id);
|
m_activeRequests.remove(id);
|
||||||
m_accumulatedResponses.remove(reply);
|
m_accumulatedResponses.remove(reply);
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
emit requestCancelled(id);
|
emit requestCancelled(id);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestHandler::processSingleLineCompletion(
|
bool RequestHandler::processSingleLineCompletion(
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMutex>
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -33,32 +32,16 @@ namespace QodeAssist::LLMCore {
|
|||||||
|
|
||||||
class RequestHandler : public RequestHandlerBase
|
class RequestHandler : public RequestHandlerBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit RequestHandler(QObject *parent = nullptr);
|
explicit RequestHandler(QObject *parent = nullptr);
|
||||||
~RequestHandler() override;
|
|
||||||
|
|
||||||
void sendLLMRequest(const LLMConfig &config, const QJsonObject &request) override;
|
void sendLLMRequest(const LLMConfig &config, const QJsonObject &request) override;
|
||||||
bool cancelRequest(const QString &id) override;
|
bool cancelRequest(const QString &id) override;
|
||||||
|
void handleLLMResponse(QNetworkReply *reply, const QJsonObject &request, const LLMConfig &config);
|
||||||
signals:
|
|
||||||
void doSendRequest(QodeAssist::LLMCore::LLMConfig config, QJsonObject request);
|
|
||||||
void doCancelRequest(QString id);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void sendLLMRequestInternal(
|
|
||||||
const QodeAssist::LLMCore::LLMConfig &config, const QJsonObject &request);
|
|
||||||
void cancelRequestInternal(const QString &id);
|
|
||||||
void handleLLMResponse(
|
|
||||||
QNetworkReply *reply,
|
|
||||||
const QJsonObject &request,
|
|
||||||
const QodeAssist::LLMCore::LLMConfig &config);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, QNetworkReply *> m_activeRequests;
|
QMap<QString, QNetworkReply *> m_activeRequests;
|
||||||
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
||||||
QNetworkAccessManager *m_manager;
|
|
||||||
QMutex m_mutex;
|
|
||||||
|
|
||||||
bool processSingleLineCompletion(
|
bool processSingleLineCompletion(
|
||||||
QNetworkReply *reply,
|
QNetworkReply *reply,
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include "llmcore/ProvidersManager.hpp"
|
#include "llmcore/ProvidersManager.hpp"
|
||||||
#include "providers/ClaudeProvider.hpp"
|
#include "providers/ClaudeProvider.hpp"
|
||||||
#include "providers/CodestralProvider.hpp"
|
|
||||||
#include "providers/GoogleAIProvider.hpp"
|
#include "providers/GoogleAIProvider.hpp"
|
||||||
#include "providers/LMStudioProvider.hpp"
|
#include "providers/LMStudioProvider.hpp"
|
||||||
#include "providers/LlamaCppProvider.hpp"
|
#include "providers/LlamaCppProvider.hpp"
|
||||||
@ -45,7 +44,6 @@ inline void registerProviders()
|
|||||||
providerManager.registerProvider<MistralAIProvider>();
|
providerManager.registerProvider<MistralAIProvider>();
|
||||||
providerManager.registerProvider<GoogleAIProvider>();
|
providerManager.registerProvider<GoogleAIProvider>();
|
||||||
providerManager.registerProvider<LlamaCppProvider>();
|
providerManager.registerProvider<LlamaCppProvider>();
|
||||||
providerManager.registerProvider<CodestralProvider>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
#include "settings/ProjectSettingsPanel.hpp"
|
#include "settings/ProjectSettingsPanel.hpp"
|
||||||
#include "settings/SettingsConstants.hpp"
|
#include "settings/SettingsConstants.hpp"
|
||||||
#include "templates/Templates.hpp"
|
#include "templates/Templates.hpp"
|
||||||
#include "widgets/QuickRefactorDialog.hpp"
|
|
||||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
@ -149,17 +148,17 @@ public:
|
|||||||
quickRefactorAction.setIcon(QCODEASSIST_ICON.icon());
|
quickRefactorAction.setIcon(QCODEASSIST_ICON.icon());
|
||||||
quickRefactorAction.addOnTriggered(this, [this] {
|
quickRefactorAction.addOnTriggered(this, [this] {
|
||||||
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
||||||
|
bool ok;
|
||||||
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
||||||
QuickRefactorDialog
|
QString instructions = QInputDialog::getText(
|
||||||
dialog(Core::ICore::dialogParent(), m_lastRefactorInstructions);
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr("Quick Refactor"),
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
Tr::tr("Enter refactoring instructions:"),
|
||||||
QString instructions = dialog.instructions();
|
QLineEdit::Normal,
|
||||||
if (!instructions.isEmpty()) {
|
QString(),
|
||||||
m_lastRefactorInstructions = instructions;
|
&ok);
|
||||||
|
if (ok)
|
||||||
m_qodeAssistClient->requestQuickRefactor(editor, instructions);
|
m_qodeAssistClient->requestQuickRefactor(editor, instructions);
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "The QodeAssist is not ready. Please check your connection and "
|
qWarning() << "The QodeAssist is not ready. Please check your connection and "
|
||||||
"settings.";
|
"settings.";
|
||||||
@ -236,7 +235,6 @@ private:
|
|||||||
QPointer<Chat::NavigationPanel> m_navigationPanel;
|
QPointer<Chat::NavigationPanel> m_navigationPanel;
|
||||||
QPointer<PluginUpdater> m_updater;
|
QPointer<PluginUpdater> m_updater;
|
||||||
UpdateStatusWidget *m_statusWidget{nullptr};
|
UpdateStatusWidget *m_statusWidget{nullptr};
|
||||||
QString m_lastRefactorInstructions;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Internal
|
} // namespace QodeAssist::Internal
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 963 B |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
@ -22,8 +22,6 @@
|
|||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <QApplication>
|
|
||||||
#include <QFontDatabase>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "SettingsConstants.hpp"
|
#include "SettingsConstants.hpp"
|
||||||
@ -49,8 +47,8 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
chatTokensThreshold.setLabelText(Tr::tr("Chat history token limit:"));
|
chatTokensThreshold.setLabelText(Tr::tr("Chat history token limit:"));
|
||||||
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
|
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
|
||||||
"exceeded, oldest messages will be removed."));
|
"exceeded, oldest messages will be removed."));
|
||||||
chatTokensThreshold.setRange(1, 99999999);
|
chatTokensThreshold.setRange(1, 900000);
|
||||||
chatTokensThreshold.setDefaultValue(20000);
|
chatTokensThreshold.setDefaultValue(8000);
|
||||||
|
|
||||||
linkOpenFiles.setSettingsKey(Constants::CA_LINK_OPEN_FILES);
|
linkOpenFiles.setSettingsKey(Constants::CA_LINK_OPEN_FILES);
|
||||||
linkOpenFiles.setLabelText(Tr::tr("Sync open files with assistant by default"));
|
linkOpenFiles.setLabelText(Tr::tr("Sync open files with assistant by default"));
|
||||||
@ -139,65 +137,6 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
contextWindow.setRange(-1, 10000);
|
contextWindow.setRange(-1, 10000);
|
||||||
contextWindow.setDefaultValue(2048);
|
contextWindow.setDefaultValue(2048);
|
||||||
|
|
||||||
autosave.setDefaultValue(true);
|
|
||||||
autosave.setLabelText(Tr::tr("Enable autosave when message received"));
|
|
||||||
|
|
||||||
textFontFamily.setSettingsKey(Constants::CA_TEXT_FONT_FAMILY);
|
|
||||||
textFontFamily.setLabelText(Tr::tr("Text Font:"));
|
|
||||||
textFontFamily.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
const QStringList families = QFontDatabase::families();
|
|
||||||
for (const QString &family : families) {
|
|
||||||
textFontFamily.addOption(family);
|
|
||||||
}
|
|
||||||
textFontFamily.setDefaultValue(QApplication::font().family());
|
|
||||||
|
|
||||||
textFontSize.setSettingsKey(Constants::CA_TEXT_FONT_SIZE);
|
|
||||||
textFontSize.setLabelText(Tr::tr("Text Font Size:"));
|
|
||||||
textFontSize.setDefaultValue(QApplication::font().pointSize());
|
|
||||||
|
|
||||||
codeFontFamily.setSettingsKey(Constants::CA_CODE_FONT_FAMILY);
|
|
||||||
codeFontFamily.setLabelText(Tr::tr("Code Font:"));
|
|
||||||
codeFontFamily.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
const QStringList monospaceFamilies = QFontDatabase::families(QFontDatabase::Latin);
|
|
||||||
for (const QString &family : monospaceFamilies) {
|
|
||||||
if (QFontDatabase::isFixedPitch(family)) {
|
|
||||||
codeFontFamily.addOption(family);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defaultMonoFont;
|
|
||||||
QStringList fixedPitchFamilies;
|
|
||||||
|
|
||||||
for (const QString &family : monospaceFamilies) {
|
|
||||||
if (QFontDatabase::isFixedPitch(family)) {
|
|
||||||
fixedPitchFamilies.append(family);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fixedPitchFamilies.contains("Consolas")) {
|
|
||||||
defaultMonoFont = "Consolas";
|
|
||||||
} else if (fixedPitchFamilies.contains("Courier New")) {
|
|
||||||
defaultMonoFont = "Courier New";
|
|
||||||
} else if (fixedPitchFamilies.contains("Monospace")) {
|
|
||||||
defaultMonoFont = "Monospace";
|
|
||||||
} else if (!fixedPitchFamilies.isEmpty()) {
|
|
||||||
defaultMonoFont = fixedPitchFamilies.first();
|
|
||||||
} else {
|
|
||||||
defaultMonoFont = QApplication::font().family();
|
|
||||||
}
|
|
||||||
codeFontFamily.setDefaultValue(defaultMonoFont);
|
|
||||||
codeFontSize.setSettingsKey(Constants::CA_CODE_FONT_SIZE);
|
|
||||||
codeFontSize.setLabelText(Tr::tr("Code Font Size:"));
|
|
||||||
codeFontSize.setDefaultValue(QApplication::font().pointSize());
|
|
||||||
|
|
||||||
textFormat.setSettingsKey(Constants::CA_TEXT_FORMAT);
|
|
||||||
textFormat.setLabelText(Tr::tr("Text Format:"));
|
|
||||||
textFormat.setDefaultValue(0);
|
|
||||||
textFormat.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
textFormat.addOption("Markdown");
|
|
||||||
textFormat.addOption("HTML");
|
|
||||||
textFormat.addOption("Plain Text");
|
|
||||||
|
|
||||||
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
|
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
@ -221,11 +160,6 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
ollamaGrid.addRow({ollamaLivetime});
|
ollamaGrid.addRow({ollamaLivetime});
|
||||||
ollamaGrid.addRow({contextWindow});
|
ollamaGrid.addRow({contextWindow});
|
||||||
|
|
||||||
auto chatViewSettingsGrid = Grid{};
|
|
||||||
chatViewSettingsGrid.addRow({textFontFamily, textFontSize});
|
|
||||||
chatViewSettingsGrid.addRow({codeFontFamily, codeFontSize});
|
|
||||||
chatViewSettingsGrid.addRow({textFormat});
|
|
||||||
|
|
||||||
return Column{
|
return Column{
|
||||||
Row{Stretch{1}, resetToDefaults},
|
Row{Stretch{1}, resetToDefaults},
|
||||||
Space{8},
|
Space{8},
|
||||||
@ -247,7 +181,6 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
systemPrompt,
|
systemPrompt,
|
||||||
}},
|
}},
|
||||||
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
|
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
|
||||||
Group{title(Tr::tr("Chat Settings")), Row{chatViewSettingsGrid, Stretch{1}}},
|
|
||||||
Stretch{1}};
|
Stretch{1}};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -288,11 +221,6 @@ void ChatAssistantSettings::resetSettingsToDefaults()
|
|||||||
resetAspect(ollamaLivetime);
|
resetAspect(ollamaLivetime);
|
||||||
resetAspect(contextWindow);
|
resetAspect(contextWindow);
|
||||||
resetAspect(linkOpenFiles);
|
resetAspect(linkOpenFiles);
|
||||||
resetAspect(textFontFamily);
|
|
||||||
resetAspect(codeFontFamily);
|
|
||||||
resetAspect(textFontSize);
|
|
||||||
resetAspect(codeFontSize);
|
|
||||||
resetAspect(textFormat);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,6 @@ public:
|
|||||||
Utils::StringAspect ollamaLivetime{this};
|
Utils::StringAspect ollamaLivetime{this};
|
||||||
Utils::IntegerAspect contextWindow{this};
|
Utils::IntegerAspect contextWindow{this};
|
||||||
|
|
||||||
// Visuals settings
|
|
||||||
Utils::SelectionAspect textFontFamily{this};
|
|
||||||
Utils::IntegerAspect textFontSize{this};
|
|
||||||
Utils::SelectionAspect codeFontFamily{this};
|
|
||||||
Utils::IntegerAspect codeFontSize{this};
|
|
||||||
Utils::SelectionAspect textFormat{this};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void resetSettingsToDefaults();
|
void resetSettingsToDefaults();
|
||||||
|
@ -99,20 +99,9 @@ GeneralSettings::GeneralSettings()
|
|||||||
ccSelectTemplate.m_buttonText = TrConstants::SELECT;
|
ccSelectTemplate.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
initStringAspect(ccUrl, Constants::CC_URL, TrConstants::URL, "http://localhost:11434");
|
initStringAspect(ccUrl, Constants::CC_URL, TrConstants::URL, "http://localhost:11434");
|
||||||
ccUrl.setHistoryCompleter(Constants::CC_CUSTOM_ENDPOINT_HISTORY);
|
ccUrl.setHistoryCompleter(Constants::CC_URL_HISTORY);
|
||||||
ccSetUrl.m_buttonText = TrConstants::SELECT;
|
ccSetUrl.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
ccEndpointMode.setSettingsKey(Constants::CC_ENDPOINT_MODE);
|
|
||||||
ccEndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
ccEndpointMode.addOption("Auto");
|
|
||||||
ccEndpointMode.addOption("Custom");
|
|
||||||
ccEndpointMode.addOption("FIM");
|
|
||||||
ccEndpointMode.addOption("Chat");
|
|
||||||
ccEndpointMode.setDefaultValue("Auto");
|
|
||||||
|
|
||||||
initStringAspect(ccCustomEndpoint, Constants::CC_CUSTOM_ENDPOINT, TrConstants::ENDPOINT_MODE, "");
|
|
||||||
ccCustomEndpoint.setHistoryCompleter(Constants::CC_CUSTOM_ENDPOINT_HISTORY);
|
|
||||||
|
|
||||||
ccStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
ccStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
||||||
ccStatus.setLabelText(TrConstants::STATUS);
|
ccStatus.setLabelText(TrConstants::STATUS);
|
||||||
ccStatus.setDefaultValue("");
|
ccStatus.setDefaultValue("");
|
||||||
@ -145,21 +134,6 @@ GeneralSettings::GeneralSettings()
|
|||||||
ccPreset1Url.setHistoryCompleter(Constants::CC_PRESET1_URL_HISTORY);
|
ccPreset1Url.setHistoryCompleter(Constants::CC_PRESET1_URL_HISTORY);
|
||||||
ccPreset1SetUrl.m_buttonText = TrConstants::SELECT;
|
ccPreset1SetUrl.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
ccPreset1EndpointMode.setSettingsKey(Constants::CC_PRESET1_ENDPOINT_MODE);
|
|
||||||
ccPreset1EndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
ccPreset1EndpointMode.addOption("Auto");
|
|
||||||
ccPreset1EndpointMode.addOption("Custom");
|
|
||||||
ccPreset1EndpointMode.addOption("FIM");
|
|
||||||
ccPreset1EndpointMode.addOption("Chat");
|
|
||||||
ccPreset1EndpointMode.setDefaultValue("Auto");
|
|
||||||
|
|
||||||
initStringAspect(
|
|
||||||
ccPreset1CustomEndpoint,
|
|
||||||
Constants::CC_PRESET1_CUSTOM_ENDPOINT,
|
|
||||||
TrConstants::ENDPOINT_MODE,
|
|
||||||
"");
|
|
||||||
ccPreset1CustomEndpoint.setHistoryCompleter(Constants::CC_PRESET1_CUSTOM_ENDPOINT_HISTORY);
|
|
||||||
|
|
||||||
initStringAspect(
|
initStringAspect(
|
||||||
ccPreset1Model, Constants::CC_PRESET1_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
|
ccPreset1Model, Constants::CC_PRESET1_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
|
||||||
ccPreset1Model.setHistoryCompleter(Constants::CC_PRESET1_MODEL_HISTORY);
|
ccPreset1Model.setHistoryCompleter(Constants::CC_PRESET1_MODEL_HISTORY);
|
||||||
@ -188,17 +162,6 @@ GeneralSettings::GeneralSettings()
|
|||||||
caUrl.setHistoryCompleter(Constants::CA_URL_HISTORY);
|
caUrl.setHistoryCompleter(Constants::CA_URL_HISTORY);
|
||||||
caSetUrl.m_buttonText = TrConstants::SELECT;
|
caSetUrl.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
caEndpointMode.setSettingsKey(Constants::CA_ENDPOINT_MODE);
|
|
||||||
caEndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
caEndpointMode.addOption("Auto");
|
|
||||||
caEndpointMode.addOption("Custom");
|
|
||||||
caEndpointMode.addOption("FIM");
|
|
||||||
caEndpointMode.addOption("Chat");
|
|
||||||
caEndpointMode.setDefaultValue("Auto");
|
|
||||||
|
|
||||||
initStringAspect(caCustomEndpoint, Constants::CA_CUSTOM_ENDPOINT, TrConstants::ENDPOINT_MODE, "");
|
|
||||||
caCustomEndpoint.setHistoryCompleter(Constants::CA_CUSTOM_ENDPOINT_HISTORY);
|
|
||||||
|
|
||||||
caStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
caStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
||||||
caStatus.setLabelText(TrConstants::STATUS);
|
caStatus.setLabelText(TrConstants::STATUS);
|
||||||
caStatus.setDefaultValue("");
|
caStatus.setDefaultValue("");
|
||||||
@ -216,9 +179,6 @@ GeneralSettings::GeneralSettings()
|
|||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
updatePreset1Visiblity(specifyPreset1.value());
|
updatePreset1Visiblity(specifyPreset1.value());
|
||||||
ccCustomEndpoint.setEnabled(ccEndpointMode.stringValue() == "Custom");
|
|
||||||
ccPreset1CustomEndpoint.setEnabled(ccPreset1EndpointMode.stringValue() == "Custom");
|
|
||||||
caCustomEndpoint.setEnabled(caEndpointMode.stringValue() == "Custom");
|
|
||||||
|
|
||||||
setLayouter([this]() {
|
setLayouter([this]() {
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
@ -226,21 +186,18 @@ GeneralSettings::GeneralSettings()
|
|||||||
auto ccGrid = Grid{};
|
auto ccGrid = Grid{};
|
||||||
ccGrid.addRow({ccProvider, ccSelectProvider});
|
ccGrid.addRow({ccProvider, ccSelectProvider});
|
||||||
ccGrid.addRow({ccUrl, ccSetUrl});
|
ccGrid.addRow({ccUrl, ccSetUrl});
|
||||||
ccGrid.addRow({ccCustomEndpoint, ccEndpointMode});
|
|
||||||
ccGrid.addRow({ccModel, ccSelectModel});
|
ccGrid.addRow({ccModel, ccSelectModel});
|
||||||
ccGrid.addRow({ccTemplate, ccSelectTemplate});
|
ccGrid.addRow({ccTemplate, ccSelectTemplate});
|
||||||
|
|
||||||
auto ccPreset1Grid = Grid{};
|
auto ccPreset1Grid = Grid{};
|
||||||
ccPreset1Grid.addRow({ccPreset1Provider, ccPreset1SelectProvider});
|
ccPreset1Grid.addRow({ccPreset1Provider, ccPreset1SelectProvider});
|
||||||
ccPreset1Grid.addRow({ccPreset1Url, ccPreset1SetUrl});
|
ccPreset1Grid.addRow({ccPreset1Url, ccPreset1SetUrl});
|
||||||
ccPreset1Grid.addRow({ccPreset1CustomEndpoint, ccPreset1EndpointMode});
|
|
||||||
ccPreset1Grid.addRow({ccPreset1Model, ccPreset1SelectModel});
|
ccPreset1Grid.addRow({ccPreset1Model, ccPreset1SelectModel});
|
||||||
ccPreset1Grid.addRow({ccPreset1Template, ccPreset1SelectTemplate});
|
ccPreset1Grid.addRow({ccPreset1Template, ccPreset1SelectTemplate});
|
||||||
|
|
||||||
auto caGrid = Grid{};
|
auto caGrid = Grid{};
|
||||||
caGrid.addRow({caProvider, caSelectProvider});
|
caGrid.addRow({caProvider, caSelectProvider});
|
||||||
caGrid.addRow({caUrl, caSetUrl});
|
caGrid.addRow({caUrl, caSetUrl});
|
||||||
caGrid.addRow({caCustomEndpoint, caEndpointMode});
|
|
||||||
caGrid.addRow({caModel, caSelectModel});
|
caGrid.addRow({caModel, caSelectModel});
|
||||||
caGrid.addRow({caTemplate, caSelectTemplate});
|
caGrid.addRow({caTemplate, caSelectTemplate});
|
||||||
|
|
||||||
@ -432,8 +389,6 @@ void GeneralSettings::updatePreset1Visiblity(bool state)
|
|||||||
ccPreset1SelectModel.updateVisibility(specifyPreset1.volatileValue());
|
ccPreset1SelectModel.updateVisibility(specifyPreset1.volatileValue());
|
||||||
ccPreset1Template.setVisible(specifyPreset1.volatileValue());
|
ccPreset1Template.setVisible(specifyPreset1.volatileValue());
|
||||||
ccPreset1SelectTemplate.updateVisibility(specifyPreset1.volatileValue());
|
ccPreset1SelectTemplate.updateVisibility(specifyPreset1.volatileValue());
|
||||||
ccPreset1EndpointMode.setVisible(specifyPreset1.volatileValue());
|
|
||||||
ccPreset1CustomEndpoint.setVisible(specifyPreset1.volatileValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::setupConnections()
|
void GeneralSettings::setupConnections()
|
||||||
@ -449,19 +404,6 @@ void GeneralSettings::setupConnections()
|
|||||||
connect(&specifyPreset1, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
connect(&specifyPreset1, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
updatePreset1Visiblity(specifyPreset1.volatileValue());
|
updatePreset1Visiblity(specifyPreset1.volatileValue());
|
||||||
});
|
});
|
||||||
connect(&ccEndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() {
|
|
||||||
ccCustomEndpoint.setEnabled(
|
|
||||||
ccEndpointMode.volatileValue() == ccEndpointMode.indexForDisplay("Custom"));
|
|
||||||
});
|
|
||||||
connect(&ccPreset1EndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() {
|
|
||||||
ccPreset1CustomEndpoint.setEnabled(
|
|
||||||
ccPreset1EndpointMode.volatileValue()
|
|
||||||
== ccPreset1EndpointMode.indexForDisplay("Custom"));
|
|
||||||
});
|
|
||||||
connect(&caEndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() {
|
|
||||||
caCustomEndpoint.setEnabled(
|
|
||||||
caEndpointMode.volatileValue() == caEndpointMode.indexForDisplay("Custom"));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::resetPageToDefaults()
|
void GeneralSettings::resetPageToDefaults()
|
||||||
@ -491,12 +433,6 @@ void GeneralSettings::resetPageToDefaults()
|
|||||||
resetAspect(ccPreset1Model);
|
resetAspect(ccPreset1Model);
|
||||||
resetAspect(ccPreset1Template);
|
resetAspect(ccPreset1Template);
|
||||||
resetAspect(ccPreset1Url);
|
resetAspect(ccPreset1Url);
|
||||||
resetAspect(ccEndpointMode);
|
|
||||||
resetAspect(ccCustomEndpoint);
|
|
||||||
resetAspect(ccPreset1EndpointMode);
|
|
||||||
resetAspect(ccPreset1CustomEndpoint);
|
|
||||||
resetAspect(caEndpointMode);
|
|
||||||
resetAspect(caCustomEndpoint);
|
|
||||||
writeSettings();
|
writeSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,6 @@ public:
|
|||||||
Utils::StringAspect ccUrl{this};
|
Utils::StringAspect ccUrl{this};
|
||||||
ButtonAspect ccSetUrl{this};
|
ButtonAspect ccSetUrl{this};
|
||||||
|
|
||||||
Utils::SelectionAspect ccEndpointMode{this};
|
|
||||||
Utils::StringAspect ccCustomEndpoint{this};
|
|
||||||
|
|
||||||
Utils::StringAspect ccStatus{this};
|
Utils::StringAspect ccStatus{this};
|
||||||
ButtonAspect ccTest{this};
|
ButtonAspect ccTest{this};
|
||||||
|
|
||||||
@ -73,9 +70,6 @@ public:
|
|||||||
Utils::StringAspect ccPreset1Url{this};
|
Utils::StringAspect ccPreset1Url{this};
|
||||||
ButtonAspect ccPreset1SetUrl{this};
|
ButtonAspect ccPreset1SetUrl{this};
|
||||||
|
|
||||||
Utils::SelectionAspect ccPreset1EndpointMode{this};
|
|
||||||
Utils::StringAspect ccPreset1CustomEndpoint{this};
|
|
||||||
|
|
||||||
Utils::StringAspect ccPreset1Model{this};
|
Utils::StringAspect ccPreset1Model{this};
|
||||||
ButtonAspect ccPreset1SelectModel{this};
|
ButtonAspect ccPreset1SelectModel{this};
|
||||||
|
|
||||||
@ -95,9 +89,6 @@ public:
|
|||||||
Utils::StringAspect caUrl{this};
|
Utils::StringAspect caUrl{this};
|
||||||
ButtonAspect caSetUrl{this};
|
ButtonAspect caSetUrl{this};
|
||||||
|
|
||||||
Utils::SelectionAspect caEndpointMode{this};
|
|
||||||
Utils::StringAspect caCustomEndpoint{this};
|
|
||||||
|
|
||||||
Utils::StringAspect caStatus{this};
|
Utils::StringAspect caStatus{this};
|
||||||
ButtonAspect caTest{this};
|
ButtonAspect caTest{this};
|
||||||
|
|
||||||
|
@ -87,14 +87,6 @@ ProviderSettings::ProviderSettings()
|
|||||||
mistralAiApiKey.setDefaultValue("");
|
mistralAiApiKey.setDefaultValue("");
|
||||||
mistralAiApiKey.setAutoApply(true);
|
mistralAiApiKey.setAutoApply(true);
|
||||||
|
|
||||||
codestralApiKey.setSettingsKey(Constants::CODESTRAL_API_KEY);
|
|
||||||
codestralApiKey.setLabelText(Tr::tr("Codestral API Key:"));
|
|
||||||
codestralApiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
|
||||||
codestralApiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
|
|
||||||
codestralApiKey.setHistoryCompleter(Constants::CODESTRAL_API_KEY_HISTORY);
|
|
||||||
codestralApiKey.setDefaultValue("");
|
|
||||||
codestralApiKey.setAutoApply(true);
|
|
||||||
|
|
||||||
// GoogleAI Settings
|
// GoogleAI Settings
|
||||||
googleAiApiKey.setSettingsKey(Constants::GOOGLE_AI_API_KEY);
|
googleAiApiKey.setSettingsKey(Constants::GOOGLE_AI_API_KEY);
|
||||||
googleAiApiKey.setLabelText(Tr::tr("Google AI API Key:"));
|
googleAiApiKey.setLabelText(Tr::tr("Google AI API Key:"));
|
||||||
@ -133,7 +125,7 @@ ProviderSettings::ProviderSettings()
|
|||||||
Space{8},
|
Space{8},
|
||||||
Group{title(Tr::tr("Claude Settings")), Column{claudeApiKey}},
|
Group{title(Tr::tr("Claude Settings")), Column{claudeApiKey}},
|
||||||
Space{8},
|
Space{8},
|
||||||
Group{title(Tr::tr("Mistral AI Settings")), Column{mistralAiApiKey, codestralApiKey}},
|
Group{title(Tr::tr("Mistral AI Settings")), Column{mistralAiApiKey}},
|
||||||
Space{8},
|
Space{8},
|
||||||
Group{title(Tr::tr("Google AI Settings")), Column{googleAiApiKey}},
|
Group{title(Tr::tr("Google AI Settings")), Column{googleAiApiKey}},
|
||||||
Space{8},
|
Space{8},
|
||||||
@ -157,9 +149,6 @@ void ProviderSettings::setupConnections()
|
|||||||
connect(&mistralAiApiKey, &ButtonAspect::changed, this, [this]() {
|
connect(&mistralAiApiKey, &ButtonAspect::changed, this, [this]() {
|
||||||
mistralAiApiKey.writeSettings();
|
mistralAiApiKey.writeSettings();
|
||||||
});
|
});
|
||||||
connect(&codestralApiKey, &ButtonAspect::changed, this, [this]() {
|
|
||||||
codestralApiKey.writeSettings();
|
|
||||||
});
|
|
||||||
connect(&googleAiApiKey, &ButtonAspect::changed, this, [this]() {
|
connect(&googleAiApiKey, &ButtonAspect::changed, this, [this]() {
|
||||||
googleAiApiKey.writeSettings();
|
googleAiApiKey.writeSettings();
|
||||||
});
|
});
|
||||||
|
@ -38,7 +38,6 @@ public:
|
|||||||
Utils::StringAspect claudeApiKey{this};
|
Utils::StringAspect claudeApiKey{this};
|
||||||
Utils::StringAspect openAiApiKey{this};
|
Utils::StringAspect openAiApiKey{this};
|
||||||
Utils::StringAspect mistralAiApiKey{this};
|
Utils::StringAspect mistralAiApiKey{this};
|
||||||
Utils::StringAspect codestralApiKey{this};
|
|
||||||
Utils::StringAspect googleAiApiKey{this};
|
Utils::StringAspect googleAiApiKey{this};
|
||||||
Utils::StringAspect ollamaBasicAuthApiKey{this};
|
Utils::StringAspect ollamaBasicAuthApiKey{this};
|
||||||
|
|
||||||
|
@ -35,9 +35,6 @@ const char CC_MODEL_HISTORY[] = "QodeAssist.ccModelHistory";
|
|||||||
const char CC_TEMPLATE[] = "QodeAssist.ccTemplate";
|
const char CC_TEMPLATE[] = "QodeAssist.ccTemplate";
|
||||||
const char CC_URL[] = "QodeAssist.ccUrl";
|
const char CC_URL[] = "QodeAssist.ccUrl";
|
||||||
const char CC_URL_HISTORY[] = "QodeAssist.ccUrlHistory";
|
const char CC_URL_HISTORY[] = "QodeAssist.ccUrlHistory";
|
||||||
const char CC_ENDPOINT_MODE[] = "QodeAssist.ccEndpointMode";
|
|
||||||
const char CC_CUSTOM_ENDPOINT[] = "QodeAssist.ccCustomEndpoint";
|
|
||||||
const char CC_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.ccCustomEndpointHistory";
|
|
||||||
|
|
||||||
const char CA_PROVIDER[] = "QodeAssist.caProvider";
|
const char CA_PROVIDER[] = "QodeAssist.caProvider";
|
||||||
const char CA_MODEL[] = "QodeAssist.caModel";
|
const char CA_MODEL[] = "QodeAssist.caModel";
|
||||||
@ -45,9 +42,6 @@ const char CA_MODEL_HISTORY[] = "QodeAssist.caModelHistory";
|
|||||||
const char CA_TEMPLATE[] = "QodeAssist.caTemplate";
|
const char CA_TEMPLATE[] = "QodeAssist.caTemplate";
|
||||||
const char CA_URL[] = "QodeAssist.caUrl";
|
const char CA_URL[] = "QodeAssist.caUrl";
|
||||||
const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory";
|
const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory";
|
||||||
const char CA_ENDPOINT_MODE[] = "QodeAssist.caEndpointMode";
|
|
||||||
const char CA_CUSTOM_ENDPOINT[] = "QodeAssist.caCustomEndpoint";
|
|
||||||
const char CA_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.caCustomEndpointHistory";
|
|
||||||
|
|
||||||
const char CC_SPECIFY_PRESET1[] = "QodeAssist.ccSpecifyPreset1";
|
const char CC_SPECIFY_PRESET1[] = "QodeAssist.ccSpecifyPreset1";
|
||||||
const char CC_PRESET1_LANGUAGE[] = "QodeAssist.ccPreset1Language";
|
const char CC_PRESET1_LANGUAGE[] = "QodeAssist.ccPreset1Language";
|
||||||
@ -57,9 +51,6 @@ const char CC_PRESET1_MODEL_HISTORY[] = "QodeAssist.ccPreset1ModelHistory";
|
|||||||
const char CC_PRESET1_TEMPLATE[] = "QodeAssist.ccPreset1Template";
|
const char CC_PRESET1_TEMPLATE[] = "QodeAssist.ccPreset1Template";
|
||||||
const char CC_PRESET1_URL[] = "QodeAssist.ccPreset1Url";
|
const char CC_PRESET1_URL[] = "QodeAssist.ccPreset1Url";
|
||||||
const char CC_PRESET1_URL_HISTORY[] = "QodeAssist.ccPreset1UrlHistory";
|
const char CC_PRESET1_URL_HISTORY[] = "QodeAssist.ccPreset1UrlHistory";
|
||||||
const char CC_PRESET1_ENDPOINT_MODE[] = "QodeAssist.caPreset1EndpointMode";
|
|
||||||
const char CC_PRESET1_CUSTOM_ENDPOINT[] = "QodeAssist.caPreset1CustomEndpointHistory";
|
|
||||||
const char CC_PRESET1_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.caPreset1CustomEndpointHistory";
|
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
|
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
|
||||||
@ -110,8 +101,6 @@ const char OPEN_AI_API_KEY[] = "QodeAssist.openAiApiKey";
|
|||||||
const char OPEN_AI_API_KEY_HISTORY[] = "QodeAssist.openAiApiKeyHistory";
|
const char OPEN_AI_API_KEY_HISTORY[] = "QodeAssist.openAiApiKeyHistory";
|
||||||
const char MISTRAL_AI_API_KEY[] = "QodeAssist.mistralAiApiKey";
|
const char MISTRAL_AI_API_KEY[] = "QodeAssist.mistralAiApiKey";
|
||||||
const char MISTRAL_AI_API_KEY_HISTORY[] = "QodeAssist.mistralAiApiKeyHistory";
|
const char MISTRAL_AI_API_KEY_HISTORY[] = "QodeAssist.mistralAiApiKeyHistory";
|
||||||
const char CODESTRAL_API_KEY[] = "QodeAssist.codestralApiKey";
|
|
||||||
const char CODESTRAL_API_KEY_HISTORY[] = "QodeAssist.codestralApiKeyHistory";
|
|
||||||
const char GOOGLE_AI_API_KEY[] = "QodeAssist.googleAiApiKey";
|
const char GOOGLE_AI_API_KEY[] = "QodeAssist.googleAiApiKey";
|
||||||
const char GOOGLE_AI_API_KEY_HISTORY[] = "QodeAssist.googleAiApiKeyHistory";
|
const char GOOGLE_AI_API_KEY_HISTORY[] = "QodeAssist.googleAiApiKeyHistory";
|
||||||
const char OLLAMA_BASIC_AUTH_API_KEY[] = "QodeAssist.ollamaBasicAuthApiKey";
|
const char OLLAMA_BASIC_AUTH_API_KEY[] = "QodeAssist.ollamaBasicAuthApiKey";
|
||||||
@ -160,10 +149,5 @@ const char CA_USE_FREQUENCY_PENALTY[] = "QodeAssist.chatUseFrequencyPenalty";
|
|||||||
const char CA_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
|
const char CA_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
|
||||||
const char CA_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
|
const char CA_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
|
||||||
const char CA_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.caOllamaContextWindow";
|
const char CA_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.caOllamaContextWindow";
|
||||||
const char CA_TEXT_FONT_FAMILY[] = "QodeAssist.caTextFontFamily";
|
|
||||||
const char CA_TEXT_FONT_SIZE[] = "QodeAssist.caTextFontSize";
|
|
||||||
const char CA_CODE_FONT_FAMILY[] = "QodeAssist.caCodeFontFamily";
|
|
||||||
const char CA_CODE_FONT_SIZE[] = "QodeAssist.caCodeFontSize";
|
|
||||||
const char CA_TEXT_FORMAT[] = "QodeAssist.caTextFormat";
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Constants
|
} // namespace QodeAssist::Constants
|
||||||
|
@ -44,7 +44,6 @@ inline const char *ENABLE_CHECK_UPDATE_ON_START
|
|||||||
inline const char *ENABLE_CHAT = QT_TRANSLATE_NOOP(
|
inline const char *ENABLE_CHAT = QT_TRANSLATE_NOOP(
|
||||||
"QtC::QodeAssist",
|
"QtC::QodeAssist",
|
||||||
"Enable Chat(If you have performance issues try disabling this, need restart QtC)");
|
"Enable Chat(If you have performance issues try disabling this, need restart QtC)");
|
||||||
inline const char *ENDPOINT_MODE = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Endpoint Mode:");
|
|
||||||
|
|
||||||
inline const char *CODE_COMPLETION = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Code Completion");
|
inline const char *CODE_COMPLETION = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Code Completion");
|
||||||
inline const char *CHAT_ASSISTANT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Chat Assistant");
|
inline const char *CHAT_ASSISTANT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Chat Assistant");
|
||||||
|
@ -38,6 +38,7 @@ namespace QodeAssist {
|
|||||||
void CompletionProgressHandler::showProgress(TextEditor::TextEditorWidget *widget)
|
void CompletionProgressHandler::showProgress(TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
m_widget = widget;
|
m_widget = widget;
|
||||||
|
m_isActive = true;
|
||||||
|
|
||||||
if (m_widget) {
|
if (m_widget) {
|
||||||
const QRect cursorRect = m_widget->cursorRect(m_widget->textCursor());
|
const QRect cursorRect = m_widget->cursorRect(m_widget->textCursor());
|
||||||
@ -53,13 +54,14 @@ void CompletionProgressHandler::showProgress(TextEditor::TextEditorWidget *widge
|
|||||||
|
|
||||||
void CompletionProgressHandler::hideProgress()
|
void CompletionProgressHandler::hideProgress()
|
||||||
{
|
{
|
||||||
Utils::ToolTip::hideImmediately();
|
m_isActive = false;
|
||||||
|
Utils::ToolTip::hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionProgressHandler::identifyMatch(
|
void CompletionProgressHandler::identifyMatch(
|
||||||
TextEditor::TextEditorWidget *editorWidget, int pos, ReportPriority report)
|
TextEditor::TextEditorWidget *editorWidget, int pos, ReportPriority report)
|
||||||
{
|
{
|
||||||
if (!editorWidget) {
|
if (!m_isActive || !editorWidget) {
|
||||||
report(Priority_None);
|
report(Priority_None);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -70,7 +72,7 @@ void CompletionProgressHandler::identifyMatch(
|
|||||||
void CompletionProgressHandler::operateTooltip(
|
void CompletionProgressHandler::operateTooltip(
|
||||||
TextEditor::TextEditorWidget *editorWidget, const QPoint &point)
|
TextEditor::TextEditorWidget *editorWidget, const QPoint &point)
|
||||||
{
|
{
|
||||||
if (!editorWidget)
|
if (!m_isActive || !editorWidget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto progressWidget = new ProgressWidget(editorWidget);
|
auto progressWidget = new ProgressWidget(editorWidget);
|
||||||
|
@ -38,6 +38,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
QPointer<TextEditor::TextEditorWidget> m_widget;
|
QPointer<TextEditor::TextEditorWidget> m_widget;
|
||||||
QPoint m_iconPosition;
|
QPoint m_iconPosition;
|
||||||
|
bool m_isActive = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "QuickRefactorDialog.hpp"
|
|
||||||
#include "QodeAssisttr.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QFontMetrics>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPlainTextEdit>
|
|
||||||
#include <QScreen>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include <utils/theme/theme.h>
|
|
||||||
#include <utils/utilsicons.h>
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
|
||||||
|
|
||||||
QuickRefactorDialog::QuickRefactorDialog(QWidget *parent, const QString &lastInstructions)
|
|
||||||
: QDialog(parent)
|
|
||||||
, m_lastInstructions(lastInstructions)
|
|
||||||
{
|
|
||||||
setWindowTitle(Tr::tr("Quick Refactor"));
|
|
||||||
setupUi();
|
|
||||||
|
|
||||||
QTimer::singleShot(0, this, &QuickRefactorDialog::updateDialogSize);
|
|
||||||
m_textEdit->installEventFilter(this);
|
|
||||||
updateDialogSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::setupUi()
|
|
||||||
{
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
||||||
mainLayout->setContentsMargins(10, 10, 10, 10);
|
|
||||||
mainLayout->setSpacing(8);
|
|
||||||
|
|
||||||
QHBoxLayout *actionsLayout = new QHBoxLayout();
|
|
||||||
actionsLayout->setSpacing(4);
|
|
||||||
createActionButtons();
|
|
||||||
actionsLayout->addWidget(m_repeatButton);
|
|
||||||
actionsLayout->addWidget(m_improveButton);
|
|
||||||
actionsLayout->addWidget(m_alternativeButton);
|
|
||||||
actionsLayout->addStretch();
|
|
||||||
mainLayout->addLayout(actionsLayout);
|
|
||||||
|
|
||||||
m_instructionsLabel = new QLabel(Tr::tr("Enter refactoring instructions:"), this);
|
|
||||||
mainLayout->addWidget(m_instructionsLabel);
|
|
||||||
|
|
||||||
m_textEdit = new QPlainTextEdit(this);
|
|
||||||
m_textEdit->setMinimumHeight(100);
|
|
||||||
m_textEdit->setPlaceholderText(Tr::tr("Type your refactoring instructions here..."));
|
|
||||||
|
|
||||||
connect(m_textEdit, &QPlainTextEdit::textChanged, this, &QuickRefactorDialog::updateDialogSize);
|
|
||||||
|
|
||||||
mainLayout->addWidget(m_textEdit);
|
|
||||||
|
|
||||||
QDialogButtonBox *buttonBox
|
|
||||||
= new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
|
||||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
||||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
mainLayout->addWidget(buttonBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::createActionButtons()
|
|
||||||
{
|
|
||||||
Utils::Icon REPEAT_ICON(
|
|
||||||
{{":/resources/images/repeat-last-instruct-icon.png", Utils::Theme::IconsBaseColor}});
|
|
||||||
Utils::Icon IMPROVE_ICON(
|
|
||||||
{{":/resources/images/improve-current-code-icon.png", Utils::Theme::IconsBaseColor}});
|
|
||||||
Utils::Icon ALTER_ICON(
|
|
||||||
{{":/resources/images/suggest-new-icon.png", Utils::Theme::IconsBaseColor}});
|
|
||||||
m_repeatButton = new QToolButton(this);
|
|
||||||
m_repeatButton->setIcon(REPEAT_ICON.icon());
|
|
||||||
m_repeatButton->setToolTip(Tr::tr("Repeat Last Instructions"));
|
|
||||||
m_repeatButton->setEnabled(!m_lastInstructions.isEmpty());
|
|
||||||
connect(m_repeatButton, &QToolButton::clicked, this, &QuickRefactorDialog::useLastInstructions);
|
|
||||||
|
|
||||||
m_improveButton = new QToolButton(this);
|
|
||||||
m_improveButton->setIcon(IMPROVE_ICON.icon());
|
|
||||||
m_improveButton->setToolTip(Tr::tr("Improve Current Code"));
|
|
||||||
connect(
|
|
||||||
m_improveButton, &QToolButton::clicked, this, &QuickRefactorDialog::useImproveCodeTemplate);
|
|
||||||
|
|
||||||
m_alternativeButton = new QToolButton(this);
|
|
||||||
m_alternativeButton->setIcon(ALTER_ICON.icon());
|
|
||||||
m_alternativeButton->setToolTip(Tr::tr("Suggest Alternative Solution"));
|
|
||||||
connect(
|
|
||||||
m_alternativeButton,
|
|
||||||
&QToolButton::clicked,
|
|
||||||
this,
|
|
||||||
&QuickRefactorDialog::useAlternativeSolutionTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QuickRefactorDialog::instructions() const
|
|
||||||
{
|
|
||||||
return m_textEdit->toPlainText();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::setInstructions(const QString &instructions)
|
|
||||||
{
|
|
||||||
m_textEdit->setPlainText(instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
QuickRefactorDialog::Action QuickRefactorDialog::selectedAction() const
|
|
||||||
{
|
|
||||||
return m_selectedAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QuickRefactorDialog::eventFilter(QObject *watched, QEvent *event)
|
|
||||||
{
|
|
||||||
if (watched == m_textEdit && event->type() == QEvent::KeyPress) {
|
|
||||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
|
||||||
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
|
|
||||||
if (keyEvent->modifiers() & Qt::ShiftModifier) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QDialog::eventFilter(watched, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::useLastInstructions()
|
|
||||||
{
|
|
||||||
if (!m_lastInstructions.isEmpty()) {
|
|
||||||
m_textEdit->setPlainText(m_lastInstructions);
|
|
||||||
m_selectedAction = Action::RepeatLast;
|
|
||||||
}
|
|
||||||
accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::useImproveCodeTemplate()
|
|
||||||
{
|
|
||||||
m_textEdit->setPlainText(Tr::tr(
|
|
||||||
"Improve the selected code by enhancing readability, efficiency, and maintainability. "
|
|
||||||
"Follow best practices for C++/Qt and fix any potential issues."));
|
|
||||||
m_selectedAction = Action::ImproveCode;
|
|
||||||
accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::useAlternativeSolutionTemplate()
|
|
||||||
{
|
|
||||||
m_textEdit->setPlainText(
|
|
||||||
Tr::tr("Suggest an alternative implementation approach for the selected code. "
|
|
||||||
"Provide a different solution that might be cleaner, more efficient, "
|
|
||||||
"or uses different Qt/C++ patterns or idioms."));
|
|
||||||
m_selectedAction = Action::AlternativeSolution;
|
|
||||||
accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickRefactorDialog::updateDialogSize()
|
|
||||||
{
|
|
||||||
QString text = m_textEdit->toPlainText();
|
|
||||||
|
|
||||||
QFontMetrics fm(m_textEdit->font());
|
|
||||||
|
|
||||||
QStringList lines = text.split('\n');
|
|
||||||
int lineCount = lines.size();
|
|
||||||
|
|
||||||
if (lineCount <= 1) {
|
|
||||||
int singleLineHeight = fm.height() + 10;
|
|
||||||
m_textEdit->setMinimumHeight(singleLineHeight);
|
|
||||||
m_textEdit->setMaximumHeight(singleLineHeight);
|
|
||||||
} else {
|
|
||||||
m_textEdit->setMaximumHeight(QWIDGETSIZE_MAX);
|
|
||||||
|
|
||||||
int lineHeight = fm.height() + 2;
|
|
||||||
|
|
||||||
int textEditHeight = qMin(qMax(lineCount, 2) * lineHeight, 20 * lineHeight);
|
|
||||||
m_textEdit->setMinimumHeight(textEditHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxWidth = 500;
|
|
||||||
for (const QString &line : lines) {
|
|
||||||
int lineWidth = fm.horizontalAdvance(line) + 30;
|
|
||||||
maxWidth = qMax(maxWidth, qMin(lineWidth, 800));
|
|
||||||
}
|
|
||||||
|
|
||||||
QScreen *screen = QApplication::primaryScreen();
|
|
||||||
QRect screenGeometry = screen->availableGeometry();
|
|
||||||
|
|
||||||
int newWidth = qMin(maxWidth + 40, screenGeometry.width() * 3 / 4);
|
|
||||||
|
|
||||||
int newHeight;
|
|
||||||
if (lineCount <= 1) {
|
|
||||||
newHeight = 150;
|
|
||||||
} else {
|
|
||||||
newHeight = m_textEdit->minimumHeight() + 150;
|
|
||||||
}
|
|
||||||
newHeight = qMin(newHeight, screenGeometry.height() * 3 / 4);
|
|
||||||
|
|
||||||
resize(newWidth, newHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <QDialog>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class QPlainTextEdit;
|
|
||||||
class QToolButton;
|
|
||||||
class QLabel;
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
|
||||||
|
|
||||||
class QuickRefactorDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum class Action { Custom, RepeatLast, ImproveCode, AlternativeSolution };
|
|
||||||
|
|
||||||
explicit QuickRefactorDialog(
|
|
||||||
QWidget *parent = nullptr, const QString &lastInstructions = QString());
|
|
||||||
~QuickRefactorDialog() override = default;
|
|
||||||
|
|
||||||
QString instructions() const;
|
|
||||||
void setInstructions(const QString &instructions);
|
|
||||||
|
|
||||||
Action selectedAction() const;
|
|
||||||
|
|
||||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void useLastInstructions();
|
|
||||||
void useImproveCodeTemplate();
|
|
||||||
void useAlternativeSolutionTemplate();
|
|
||||||
void updateDialogSize();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setupUi();
|
|
||||||
void createActionButtons();
|
|
||||||
|
|
||||||
QPlainTextEdit *m_textEdit;
|
|
||||||
QToolButton *m_repeatButton;
|
|
||||||
QToolButton *m_improveButton;
|
|
||||||
QToolButton *m_alternativeButton;
|
|
||||||
QLabel *m_instructionsLabel;
|
|
||||||
|
|
||||||
Action m_selectedAction = Action::Custom;
|
|
||||||
QString m_lastInstructions;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
|
Loading…
x
Reference in New Issue
Block a user