mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-06-04 01:28:58 -04:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
af3fdb58ff | ||
|
637a4d9d4c | ||
|
7e2345773f | ||
|
14a5ddbdd8 | ||
|
e178b7daa7 | ||
|
4b353d5091 | ||
|
f7ba7b95be | ||
|
6ae95fec45 | ||
|
dad8ab2bf3 | ||
|
25a6983de0 | ||
|
4e05abc7d2 | ||
|
784529e344 | ||
|
155153a763 | ||
|
9225c0c1a9 | ||
|
43adc95857 | ||
|
ee672f2cda | ||
|
a3edb8a577 | ||
|
407d3b11c0 | ||
|
285e739074 | ||
|
f7e748ba7e | ||
|
acb1306321 | ||
|
8b38ecc29b | ||
|
cfb364f033 | ||
|
2fe6850a06 | ||
|
3e9506ca92 |
24
.github/scripts/plugin.json
vendored
24
.github/scripts/plugin.json
vendored
@ -13,7 +13,7 @@
|
|||||||
"Linux"
|
"Linux"
|
||||||
],
|
],
|
||||||
"license": "GPLv3",
|
"license": "GPLv3",
|
||||||
"version": "0.5.8",
|
"version": "0.5.11",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"is_pack": false,
|
"is_pack": false,
|
||||||
"released_at": null,
|
"released_at": null,
|
||||||
@ -55,8 +55,28 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"version": "0.5.8",
|
"version": "0.5.8",
|
||||||
"is_latest": true,
|
"is_latest": false,
|
||||||
"released_at": "2025-04-17T10:00:00Z"
|
"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,
|
||||||
|
"released_at": "2025-05-01T17: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,16 +12,13 @@ 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 }}
|
name: ${{ matrix.config.name }} (Qt ${{ matrix.qt_config.qt_version }}, QtC ${{ matrix.qt_config.qt_creator_version }})
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.git.outputs.tag }}
|
tag: ${{ steps.git.outputs.tag }}
|
||||||
@ -47,12 +44,18 @@ 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
|
||||||
@ -61,7 +64,12 @@ 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()
|
||||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${{github.run_id}}")
|
execute_process(
|
||||||
|
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
|
||||||
@ -96,7 +104,7 @@ jobs:
|
|||||||
id: qt
|
id: qt
|
||||||
shell: cmake -P {0}
|
shell: cmake -P {0}
|
||||||
run: |
|
run: |
|
||||||
set(qt_version "$ENV{QT_VERSION}")
|
set(qt_version "${{ matrix.qt_config.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")
|
||||||
@ -174,10 +182,11 @@ jobs:
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
- name: Download Qt Creator
|
- name: Download Qt Creator
|
||||||
uses: qt-creator/install-dev-package@v1.2
|
uses: qt-creator/install-dev-package@v2.0
|
||||||
with:
|
with:
|
||||||
version: ${{ env.QT_CREATOR_VERSION }}
|
version: ${{ matrix.qt_config.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
|
||||||
@ -223,7 +232,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$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}"
|
--name "$ENV{PLUGIN_NAME}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.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 }}"
|
||||||
@ -241,68 +250,18 @@ 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${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
path: ./${{ env.PLUGIN_NAME }}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}.7z
|
||||||
name: ${{ env.PLUGIN_NAME}}-v${{ steps.git.outputs.tag }}-QtC${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
|
name: ${{ env.PLUGIN_NAME}}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.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
|
||||||
|
|
||||||
update_json:
|
|
||||||
if: contains(github.ref, 'tags/v')
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
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:
|
release:
|
||||||
if: contains(github.ref, 'tags/v')
|
if: contains(github.ref, 'tags/v')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
needs: [build, update_json]
|
needs: [build]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
|
@ -17,6 +17,7 @@ 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,6 +124,7 @@ 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();
|
||||||
@ -140,7 +141,19 @@ 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, ""});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,7 +215,7 @@ void ChatModel::resetModelTo(int index)
|
|||||||
if (index < 0 || index >= m_messages.size())
|
if (index < 0 || index >= m_messages.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (index < m_messages.size() - 1) {
|
if (index < m_messages.size()) {
|
||||||
beginRemoveRows(QModelIndex(), index, m_messages.size() - 1);
|
beginRemoveRows(QModelIndex(), index, m_messages.size() - 1);
|
||||||
m_messages.remove(index, m_messages.size() - index);
|
m_messages.remove(index, m_messages.size() - index);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
@ -102,6 +102,31 @@ 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();
|
||||||
}
|
}
|
||||||
@ -552,4 +577,29 @@ bool ChatRootView::shouldIgnoreFileForAttach(const Utils::FilePath &filePath)
|
|||||||
return false;
|
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,6 +38,11 @@ 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
|
||||||
|
|
||||||
@ -80,6 +85,13 @@ public:
|
|||||||
void setRecentFilePath(const QString &filePath);
|
void setRecentFilePath(const QString &filePath);
|
||||||
bool shouldIgnoreFileForAttach(const Utils::FilePath &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);
|
||||||
void copyToClipboard(const QString &text);
|
void copyToClipboard(const QString &text);
|
||||||
@ -95,6 +107,11 @@ 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,4 +29,40 @@ 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,6 +34,7 @@ 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,8 +27,26 @@ 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 int messageIndex: -1
|
||||||
|
property real listViewContentY: 0
|
||||||
|
|
||||||
signal resetChatToMessage(int index)
|
signal resetChatToMessage(int index)
|
||||||
|
|
||||||
@ -84,6 +102,8 @@ Rectangle {
|
|||||||
id: codeBlockComponent
|
id: codeBlockComponent
|
||||||
CodeBlockComponent {
|
CodeBlockComponent {
|
||||||
itemData: msgCreatorDelegate.modelData
|
itemData: msgCreatorDelegate.modelData
|
||||||
|
blockStart: root.y + msgCreatorDelegate.y
|
||||||
|
currentContentY: root.listViewContentY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,8 +160,7 @@ Rectangle {
|
|||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
right: parent.right
|
right: parent.right
|
||||||
bottom: parent.bottom
|
top: parent.top
|
||||||
bottomMargin: 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
text: qsTr("ResetTo")
|
text: qsTr("ResetTo")
|
||||||
@ -156,11 +175,28 @@ Rectangle {
|
|||||||
height: implicitHeight + 10
|
height: implicitHeight + 10
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
text: itemData.text
|
text: textFormat == Text.MarkdownText ? utils.getSafeMarkdownText(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
|
||||||
@ -171,5 +207,7 @@ Rectangle {
|
|||||||
|
|
||||||
code: itemData.text
|
code: itemData.text
|
||||||
language: itemData.language
|
language: itemData.language
|
||||||
|
codeFontFamily: root.codeFontFamily
|
||||||
|
codeFontSize: root.codeFontSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,12 @@ ChatRootView {
|
|||||||
messageAttachments: model.attachments
|
messageAttachments: model.attachments
|
||||||
isUserMessage: model.roleType === ChatModel.User
|
isUserMessage: model.roleType === ChatModel.User
|
||||||
messageIndex: index
|
messageIndex: index
|
||||||
|
listViewContentY: chatListView.contentY
|
||||||
|
textFontFamily: root.textFontFamily
|
||||||
|
codeFontFamily: root.codeFontFamily
|
||||||
|
codeFontSize: root.codeFontSize
|
||||||
|
textFontSize: root.textFontSize
|
||||||
|
textFormat: root.textFormat
|
||||||
|
|
||||||
onResetChatToMessage: function(index) {
|
onResetChatToMessage: function(index) {
|
||||||
messageInput.text = model.content
|
messageInput.text = model.content
|
||||||
|
@ -27,17 +27,25 @@ Rectangle {
|
|||||||
property string code: ""
|
property string code: ""
|
||||||
property string language: ""
|
property string language: ""
|
||||||
|
|
||||||
readonly property string monospaceFont: {
|
property real currentContentY: 0
|
||||||
switch (Qt.platform.os) {
|
property real blockStart: 0
|
||||||
case "windows":
|
|
||||||
return "Consolas";
|
property alias codeFontFamily: codeText.font.family
|
||||||
case "osx":
|
property alias codeFontSize: codeText.font.pointSize
|
||||||
return "Menlo";
|
|
||||||
case "linux":
|
readonly property real buttonTopMargin: 5
|
||||||
return "DejaVu Sans Mono";
|
readonly property real blockEnd: blockStart + root.height
|
||||||
default:
|
readonly property real maxButtonOffset: Math.max(0, root.height - copyButton.height - buttonTopMargin)
|
||||||
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
|
||||||
@ -45,7 +53,6 @@ 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
|
||||||
|
|
||||||
@ -55,14 +62,11 @@ 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
|
||||||
@ -77,14 +81,20 @@ 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: 8
|
font.pointSize: codeText.font.pointSize - 4
|
||||||
}
|
}
|
||||||
|
|
||||||
QoAButton {
|
QoAButton {
|
||||||
anchors.top: parent.top
|
id: copyButton
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: 5
|
anchors {
|
||||||
text: "Copy"
|
top: parent.top
|
||||||
|
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,7 +25,6 @@ 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,6 +57,17 @@ 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
|
||||||
@ -207,10 +218,8 @@ 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(QString("%1%2").arg(
|
config.url = QUrl(
|
||||||
url,
|
QString("%1%2").arg(url, endpoint(provider, promptTemplate->type(), isPreset1Active)));
|
||||||
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();
|
||||||
@ -289,6 +298,26 @@ 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
|
Context::ContextManager *LLMClientInterface::contextManager() const
|
||||||
{
|
{
|
||||||
return m_contextManager;
|
return m_contextManager;
|
||||||
@ -327,7 +356,9 @@ void LLMClientInterface::sendCompletionToClient(
|
|||||||
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
||||||
QJsonObject range;
|
QJsonObject range;
|
||||||
range["start"] = position;
|
range["start"] = position;
|
||||||
range["end"] = position;
|
QJsonObject 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);
|
||||||
|
@ -77,6 +77,7 @@ 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,6 +29,36 @@
|
|||||||
|
|
||||||
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)
|
||||||
@ -43,16 +73,23 @@ LLMSuggestion::LLMSuggestion(
|
|||||||
|
|
||||||
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 startPosInBlock = startPos - block.position();
|
int cursorPositionInBlock = cursor.positionInBlock();
|
||||||
int endPosInBlock = endPos - block.position();
|
|
||||||
|
|
||||||
blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text);
|
QString rightText = blockText.mid(cursorPositionInBlock);
|
||||||
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)
|
||||||
@ -77,31 +114,82 @@ 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())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
if (subText.isEmpty()) {
|
||||||
|
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
|
const Utils::Text::Position newEnd{newStart.line, int(newCompletionText.length())};
|
||||||
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,5 +40,6 @@ 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.9",
|
"Version" : "0.5.13",
|
||||||
"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",
|
||||||
|
51
README.md
51
README.md
@ -3,6 +3,7 @@
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
[](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.
|
||||||
@ -23,10 +24,12 @@
|
|||||||
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 Features](#file-context-features)
|
10. [File Context Feature](#file-context-feature)
|
||||||
11. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
11. [Quick Refactoring Feature](#quick-refactoring-feature)
|
||||||
12. [Development Progress](#development-progress)
|
12. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
||||||
13. [Hotkeys](#hotkeys)
|
13. [Development Progress](#development-progress)
|
||||||
|
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)
|
||||||
@ -201,7 +204,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 Features
|
## File Context Feature
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
@ -234,8 +237,21 @@ 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.1 - 0.5.7 - 0.x.x
|
- QtCreator 16.0.1 - 0.5.7 - 0.x.x
|
||||||
- QtCreator 16.0.0 - 0.5.2 - 0.5.6
|
- 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
|
||||||
@ -251,6 +267,7 @@ 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
|
||||||
|
|
||||||
@ -265,6 +282,30 @@ Linked files provide persistent context throughout the conversation:
|
|||||||
- on Windows: Ctrl + Alt + R
|
- on Windows: Ctrl + Alt + R
|
||||||
- on Linux with KDE Plasma: 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
|
||||||
|
|
||||||
If QodeAssist is having problems connecting to the LLM provider, please check the following:
|
If QodeAssist is having problems connecting to the LLM provider, please check the following:
|
||||||
|
@ -22,14 +22,51 @@
|
|||||||
|
|
||||||
#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(
|
||||||
@ -37,12 +74,13 @@ void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &
|
|||||||
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
|
||||||
= manager->post(networkRequest, QJsonDocument(config.providerRequest).toJson());
|
= m_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;
|
||||||
@ -55,7 +93,11 @@ void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &
|
|||||||
handleLLMResponse(reply, request, config);
|
handleLLMResponse(reply, request, config);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId, manager]() {
|
connect(
|
||||||
|
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();
|
||||||
@ -71,8 +113,8 @@ void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &
|
|||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
manager->deleteLater();
|
},
|
||||||
});
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandler::handleLLMResponse(
|
void RequestHandler::handleLLMResponse(
|
||||||
@ -102,17 +144,18 @@ void RequestHandler::handleLLMResponse(
|
|||||||
m_accumulatedResponses.remove(reply);
|
m_accumulatedResponses.remove(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestHandler::cancelRequest(const QString &id)
|
void RequestHandler::cancelRequestInternal(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,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QMutex>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -32,16 +33,32 @@ 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,
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#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"
|
||||||
@ -47,8 +49,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, std::numeric_limits<qint64>::max());
|
chatTokensThreshold.setRange(1, 99999999);
|
||||||
chatTokensThreshold.setDefaultValue(8000);
|
chatTokensThreshold.setDefaultValue(20000);
|
||||||
|
|
||||||
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"));
|
||||||
@ -137,6 +139,65 @@ 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();
|
||||||
@ -160,6 +221,11 @@ 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},
|
||||||
@ -181,6 +247,7 @@ 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}};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -221,6 +288,11 @@ 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,6 +63,13 @@ 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,9 +99,20 @@ 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_URL_HISTORY);
|
ccUrl.setHistoryCompleter(Constants::CC_CUSTOM_ENDPOINT_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("");
|
||||||
@ -134,6 +145,21 @@ 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);
|
||||||
@ -162,6 +188,17 @@ 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("");
|
||||||
@ -179,6 +216,9 @@ 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;
|
||||||
@ -186,18 +226,21 @@ 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});
|
||||||
|
|
||||||
@ -389,6 +432,8 @@ 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()
|
||||||
@ -404,6 +449,19 @@ 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()
|
||||||
@ -433,6 +491,12 @@ 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,6 +54,9 @@ 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};
|
||||||
|
|
||||||
@ -70,6 +73,9 @@ 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};
|
||||||
|
|
||||||
@ -89,6 +95,9 @@ 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};
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ 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";
|
||||||
@ -42,6 +45,9 @@ 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";
|
||||||
@ -51,6 +57,9 @@ 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";
|
||||||
@ -151,5 +160,10 @@ 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,6 +44,7 @@ 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,7 +38,6 @@ 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());
|
||||||
@ -54,14 +53,13 @@ void CompletionProgressHandler::showProgress(TextEditor::TextEditorWidget *widge
|
|||||||
|
|
||||||
void CompletionProgressHandler::hideProgress()
|
void CompletionProgressHandler::hideProgress()
|
||||||
{
|
{
|
||||||
m_isActive = false;
|
Utils::ToolTip::hideImmediately();
|
||||||
Utils::ToolTip::hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionProgressHandler::identifyMatch(
|
void CompletionProgressHandler::identifyMatch(
|
||||||
TextEditor::TextEditorWidget *editorWidget, int pos, ReportPriority report)
|
TextEditor::TextEditorWidget *editorWidget, int pos, ReportPriority report)
|
||||||
{
|
{
|
||||||
if (!m_isActive || !editorWidget) {
|
if (!editorWidget) {
|
||||||
report(Priority_None);
|
report(Priority_None);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -72,7 +70,7 @@ void CompletionProgressHandler::identifyMatch(
|
|||||||
void CompletionProgressHandler::operateTooltip(
|
void CompletionProgressHandler::operateTooltip(
|
||||||
TextEditor::TextEditorWidget *editorWidget, const QPoint &point)
|
TextEditor::TextEditorWidget *editorWidget, const QPoint &point)
|
||||||
{
|
{
|
||||||
if (!m_isActive || !editorWidget)
|
if (!editorWidget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto progressWidget = new ProgressWidget(editorWidget);
|
auto progressWidget = new ProgressWidget(editorWidget);
|
||||||
|
@ -38,7 +38,6 @@ 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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user