From 55e2b24b8d72405332bd2956e0270b16d9c83745 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:15:35 +0100 Subject: [PATCH] Upgrade plugin to Qt Creator 15 * :bug: fix: Change plugin configs * :bug: fix: Update Button aspect api * :bug: fix: Temproary fix for LLMSuggestions * :bug: fix: Update github actions * :bookmark: chore: Upgrade version in README --- .github/workflows/build_cmake.yml | 173 +++++++++++++++--------------- CMakeLists.txt | 3 - LLMSuggestion.cpp | 72 ++++++------- LLMSuggestion.hpp | 15 ++- QodeAssist.json.in | 5 +- QodeAssistClient.cpp | 13 ++- README.md | 3 +- settings/ButtonAspect.hpp | 2 +- 8 files changed, 144 insertions(+), 142 deletions(-) diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 9d3198e..cdf4726 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -9,11 +9,12 @@ on: pull_request: branches: - main + env: PLUGIN_NAME: QodeAssist - QT_VERSION: 6.7.3 - QT_CREATOR_VERSION: 14.0.2 - QT_CREATOR_SNAPSHOT: NO + QT_VERSION: 6.8.1 + QT_CREATOR_VERSION: 15.0.0 + QT_CREATOR_VERSION_INTERNAL: 15.0.0 MACOS_DEPLOYMENT_TARGET: "11.0" CMAKE_VERSION: "3.29.6" NINJA_VERSION: "1.12.1" @@ -30,74 +31,44 @@ jobs: - { name: "Windows Latest MSVC", artifact: "Windows-x64", os: windows-latest, + platform: windows_x64, cc: "cl", cxx: "cl", environment_script: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat", } - { name: "Ubuntu Latest GCC", artifact: "Linux-x64", os: ubuntu-latest, + platform: linux_x64, cc: "gcc", cxx: "g++" } - { name: "macOS Latest Clang", artifact: "macOS-universal", os: macos-latest, + platform: mac_x64, cc: "clang", cxx: "clang++" } steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Checkout submodules id: git shell: cmake -P {0} run: | if (${{github.ref}} MATCHES "tags/v(.*)") - file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}\n") + file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}") else() - file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${{github.run_id}}\n") + file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${{github.run_id}}") endif() - name: Download Ninja and CMake - shell: cmake -P {0} - run: | - set(cmake_version "$ENV{CMAKE_VERSION}") - set(ninja_version "$ENV{NINJA_VERSION}") - - if ("${{ runner.os }}" STREQUAL "Windows") - set(ninja_suffix "win.zip") - set(cmake_suffix "windows-x86_64.zip") - set(cmake_dir "cmake-${cmake_version}-windows-x86_64/bin") - elseif ("${{ runner.os }}" STREQUAL "Linux") - set(ninja_suffix "linux.zip") - set(cmake_suffix "linux-x86_64.tar.gz") - set(cmake_dir "cmake-${cmake_version}-linux-x86_64/bin") - elseif ("${{ runner.os }}" STREQUAL "macOS") - set(ninja_suffix "mac.zip") - set(cmake_suffix "macos-universal.tar.gz") - set(cmake_dir "cmake-${cmake_version}-macos-universal/CMake.app/Contents/bin") - endif() - - set(ninja_url "https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-${ninja_suffix}") - file(DOWNLOAD "${ninja_url}" ./ninja.zip SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ninja.zip) - - set(cmake_url "https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-${cmake_suffix}") - file(DOWNLOAD "${cmake_url}" ./cmake.zip SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./cmake.zip) - - # Add to PATH environment variable - file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/${cmake_dir}" cmake_dir) - set(path_separator ":") - if ("${{ runner.os }}" STREQUAL "Windows") - set(path_separator ";") - endif() - file(APPEND "$ENV{GITHUB_PATH}" "$ENV{GITHUB_WORKSPACE}${path_separator}${cmake_dir}") - - if (NOT "${{ runner.os }}" STREQUAL "Windows") - execute_process( - COMMAND chmod +x ninja - COMMAND chmod +x ${cmake_dir}/cmake - ) - endif() + uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ env.CMAKE_VERSION }} + ninjaVersion: ${{ env.NINJA_VERSION }} - name: Install system libs shell: cmake -P {0} @@ -107,7 +78,7 @@ jobs: COMMAND sudo apt update ) execute_process( - COMMAND sudo apt install libgl1-mesa-dev libcups2-dev + COMMAND sudo apt install libgl1-mesa-dev RESULT_VARIABLE result ) if (NOT result EQUAL 0) @@ -124,9 +95,9 @@ jobs: string(REPLACE "." "" qt_version_dotless "${qt_version}") if ("${{ runner.os }}" STREQUAL "Windows") set(url_os "windows_x86") - set(qt_package_arch_suffix "win64_msvc2019_64") - set(qt_dir_prefix "${qt_version}/msvc2019_64") - set(qt_package_suffix "-Windows-Windows_10_22H2-MSVC2019-Windows-Windows_10_22H2-X86_64") + set(qt_package_arch_suffix "win64_msvc2022_64") + set(qt_dir_prefix "${qt_version}/msvc2022_64") + set(qt_package_suffix "-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64") elseif ("${{ runner.os }}" STREQUAL "Linux") set(url_os "linux_x64") if (qt_version VERSION_LESS "6.7.0") @@ -135,15 +106,15 @@ jobs: set(qt_package_arch_suffix "linux_gcc_64") endif() set(qt_dir_prefix "${qt_version}/gcc_64") - set(qt_package_suffix "-Linux-RHEL_8_8-GCC-Linux-RHEL_8_8-X86_64") + set(qt_package_suffix "-Linux-RHEL_8_10-GCC-Linux-RHEL_8_10-X86_64") elseif ("${{ runner.os }}" STREQUAL "macOS") set(url_os "mac_x64") set(qt_package_arch_suffix "clang_64") set(qt_dir_prefix "${qt_version}/macos") - set(qt_package_suffix "-MacOS-MacOS_13-Clang-MacOS-MacOS_13-X86_64-ARM64") + set(qt_package_suffix "-MacOS-MacOS_14-Clang-MacOS-MacOS_14-X86_64-ARM64") endif() - set(qt_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/qt6_${qt_version_dotless}") + set(qt_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/qt6_${qt_version_dotless}/qt6_${qt_version_dotless}") file(DOWNLOAD "${qt_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS) file(READ ./Updates.xml updates_xml) @@ -153,7 +124,7 @@ jobs: file(MAKE_DIRECTORY qt6) # Save the path for other steps - file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt6/${qt_dir_prefix}" qt_dir) + file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt6" qt_dir) file(APPEND "$ENV{GITHUB_OUTPUT}" "qt_dir=${qt_dir}") message("Downloading Qt to ${qt_dir}") @@ -172,11 +143,17 @@ jobs: foreach(package qt5compat qtshadertools) downloadAndExtract( - "${qt_base_url}/qt.qt6.${qt_version_dotless}.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z" + "${qt_base_url}/qt.qt6.${qt_version_dotless}.addons.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z" ${package}.7z ) endforeach() + function(downloadAndExtractLibicu url archive) + message("Downloading ${url}") + file(DOWNLOAD "${url}" ./${archive} SHOW_PROGRESS) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../../${archive} WORKING_DIRECTORY qt6/lib) + endfunction() + # uic depends on libicu*.so if ("${{ runner.os }}" STREQUAL "Linux") if (qt_version VERSION_LESS "6.7.0") @@ -184,47 +161,25 @@ jobs: else() set(uic_suffix "Rhel8.6-x86_64") endif() - downloadAndExtract( + downloadAndExtractLibicu( "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-${uic_suffix}.7z" icu.7z ) endif() - name: Download Qt Creator + uses: qt-creator/install-dev-package@v1.2 + with: + version: ${{ env.QT_CREATOR_VERSION }} + unzip-to: 'qtcreator' + + - name: Extract Qt Creator id: qt_creator shell: cmake -P {0} run: | - string(REGEX MATCH "([0-9]+.[0-9]+).[0-9]+" outvar "$ENV{QT_CREATOR_VERSION}") - - set(qtc_base_url "https://download.qt.io/official_releases/qtcreator/${CMAKE_MATCH_1}/$ENV{QT_CREATOR_VERSION}/installer_source") - set(qtc_snapshot "$ENV{QT_CREATOR_SNAPSHOT}") - if (qtc_snapshot) - set(qtc_base_url "https://download.qt.io/snapshots/qtcreator/${CMAKE_MATCH_1}/$ENV{QT_CREATOR_VERSION}/installer_source/${qtc_snapshot}") - endif() - - if ("${{ runner.os }}" STREQUAL "Windows") - set(qtc_platform "windows_x64") - elseif ("${{ runner.os }}" STREQUAL "Linux") - set(qtc_platform "linux_x64") - elseif ("${{ runner.os }}" STREQUAL "macOS") - set(qtc_platform "mac_x64") - endif() - file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qtcreator" qtc_dir) - # Save the path for other steps file(APPEND "$ENV{GITHUB_OUTPUT}" "qtc_dir=${qtc_dir}") - file(MAKE_DIRECTORY qtcreator) - - message("Downloading Qt Creator from ${qtc_base_url}/${qtc_platform}") - - foreach(package qtcreator qtcreator_dev) - file(DOWNLOAD - "${qtc_base_url}/${qtc_platform}/${package}.7z" ./${package}.7z SHOW_PROGRESS) - execute_process(COMMAND - ${CMAKE_COMMAND} -E tar xvf ../${package}.7z WORKING_DIRECTORY qtcreator) - endforeach() - - name: Build shell: cmake -P {0} run: | @@ -283,11 +238,59 @@ jobs: path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z name: ${{ env.PLUGIN_NAME}}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z - release: + # The json is the same for all platforms, but we need to save one + - name: Upload plugin json + if: matrix.config.os == 'ubuntu-latest' + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PLUGIN_NAME }}-origin-json + path: ./build/build/${{ env.PLUGIN_NAME }}.json + + update_json: if: contains(github.ref, 'tags/v') runs-on: ubuntu-latest needs: build + steps: + - uses: actions/checkout@v4 + - 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: - name: Download artifacts uses: actions/download-artifact@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index f09abd2..2a623f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,7 @@ add_qtc_plugin(QodeAssist templates/StarCoder2Fim.hpp templates/DeepSeekCoderFim.hpp templates/CustomFimTemplate.hpp - - templates/Qwen.hpp - templates/Ollama.hpp templates/BasicChat.hpp templates/Llama3.hpp diff --git a/LLMSuggestion.cpp b/LLMSuggestion.cpp index 1fd50f5..b529f46 100644 --- a/LLMSuggestion.cpp +++ b/LLMSuggestion.cpp @@ -27,20 +27,17 @@ namespace QodeAssist { -LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin) - : m_completion(completion) +LLMSuggestion::LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QTextDocument *origin) + : TextEditor::TextSuggestion(data, origin) , m_linesCount(0) + , m_suggestion(data) { - int startPos = completion.range().start().toPositionInDocument(origin); - int endPos = completion.range().end().toPositionInDocument(origin); + int startPos = data.range.begin.toPositionInDocument(origin); + int endPos = data.range.end.toPositionInDocument(origin); startPos = qBound(0, startPos, origin->characterCount() - 1); endPos = qBound(startPos, endPos, origin->characterCount() - 1); - m_start = QTextCursor(origin); - m_start.setPosition(startPos); - m_start.setKeepPositionOnInsert(true); - QTextCursor cursor(origin); cursor.setPosition(startPos); cursor.setPosition(endPos, QTextCursor::KeepAnchor); @@ -51,39 +48,30 @@ LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin int startPosInBlock = startPos - block.position(); int endPosInBlock = endPos - block.position(); - blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, completion.text()); + blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text); - document()->setPlainText(blockText); - - setCurrentPosition(m_start.position()); + replacementDocument()->setPlainText(blockText); } bool LLMSuggestion::apply() { - QTextCursor cursor = m_completion.range().toSelection(m_start.document()); - cursor.beginEditBlock(); - cursor.removeSelectedText(); - cursor.insertText(m_completion.text()); - cursor.endEditBlock(); - return true; + return TextEditor::TextSuggestion::apply(); } bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget) { - return applyNextLine(widget); + return TextEditor::TextSuggestion::applyWord(widget); } -bool LLMSuggestion::applyNextLine(TextEditor::TextEditorWidget *widget) +bool LLMSuggestion::applyLine(TextEditor::TextEditorWidget *widget) { - const QString text = m_completion.text(); - QStringList lines = text.split('\n'); + return TextEditor::TextSuggestion::applyLine(widget); +} - if (m_linesCount < lines.size()) - m_linesCount++; - - showTooltip(widget, m_linesCount); - - return m_linesCount == lines.size() && !Utils::ToolTip::isVisible(); +void LLMSuggestion::reset() +{ + reset(); + m_linesCount = 0; } void LLMSuggestion::onCounterFinished(int count) @@ -101,24 +89,24 @@ void LLMSuggestion::onCounterFinished(int count) cursor.endEditBlock(); } -void LLMSuggestion::reset() -{ - m_start.removeSelectedText(); -} +// void LLMSuggestion::reset() +// { +// m_start.removeSelectedText(); +// } -int LLMSuggestion::position() -{ - return m_start.position(); -} +// int LLMSuggestion::position() +// { +// return m_start.position(); +// } void LLMSuggestion::showTooltip(TextEditor::TextEditorWidget *widget, int count) { - Utils::ToolTip::hide(); - QPoint pos = widget->mapToGlobal(widget->cursorRect().topRight()); - pos += QPoint(-10, -50); - m_counterTooltip = new CounterTooltip(count); - Utils::ToolTip::show(pos, m_counterTooltip, widget); - connect(m_counterTooltip, &CounterTooltip::finished, this, &LLMSuggestion::onCounterFinished); + // Utils::ToolTip::hide(); + // QPoint pos = widget->mapToGlobal(widget->cursorRect().topRight()); + // pos += QPoint(-10, -50); + // m_counterTooltip = new CounterTooltip(count); + // Utils::ToolTip::show(pos, m_counterTooltip, widget); + // connect(m_counterTooltip, &CounterTooltip::finished, this, &LLMSuggestion::onCounterFinished); } } // namespace QodeAssist diff --git a/LLMSuggestion.hpp b/LLMSuggestion.hpp index fcf716c..e23f46e 100644 --- a/LLMSuggestion.hpp +++ b/LLMSuggestion.hpp @@ -31,15 +31,12 @@ class LLMSuggestion final : public QObject, public TextEditor::TextSuggestion { Q_OBJECT public: - LLMSuggestion(const Completion &completion, QTextDocument *origin); + LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QTextDocument *origin); - bool apply() final; - bool applyWord(TextEditor::TextEditorWidget *widget) final; - bool applyNextLine(TextEditor::TextEditorWidget *widget); - void reset() final; - int position() final; - - const Completion &completion() const { return m_completion; } + bool apply() override; + bool applyWord(TextEditor::TextEditorWidget *widget) override; + bool applyLine(TextEditor::TextEditorWidget *widget) override; + void reset(); void showTooltip(TextEditor::TextEditorWidget *widget, int count); void onCounterFinished(int count); @@ -50,6 +47,8 @@ private: int m_linesCount; CounterTooltip *m_counterTooltip = nullptr; + int m_startPosition; + TextEditor::TextSuggestion::Data m_suggestion; }; } // namespace QodeAssist diff --git a/QodeAssist.json.in b/QodeAssist.json.in index 059db54..f2af566 100644 --- a/QodeAssist.json.in +++ b/QodeAssist.json.in @@ -1,8 +1,10 @@ { + "Id" : "qodeassist", "Name" : "QodeAssist", - "Version" : "0.3.10", + "Version" : "0.4.0", "CompatVersion" : "${IDE_VERSION_COMPAT}", "Vendor" : "Petr Mironychev", + "VendorId" : "petrmironychev", "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", "License" : "GNU General Public License Usage @@ -12,5 +14,6 @@ Alternatively, this file may be used under the terms of the GNU General Public L "- One of the supported LLM providers installed (e.g., Ollama or LM Studio)", "- A compatible large language model downloaded for your chosen provider (e.g., CodeLlama, StarCoder2)"], "Url" : "https://github.com/Palm1r/QodeAssist", + "DocumentationUrl" : "", ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/QodeAssistClient.cpp b/QodeAssistClient.cpp index 26eb763..03223ad 100644 --- a/QodeAssistClient.cpp +++ b/QodeAssistClient.cpp @@ -213,8 +213,19 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r } if (completions.isEmpty()) return; + + auto suggestions = Utils::transform(completions, [](const Completion &c) { + auto toTextPos = [](const LanguageServerProtocol::Position pos) { + return Text::Position{pos.line() + 1, pos.character()}; + }; + + Text::Range range{toTextPos(c.range().start()), toTextPos(c.range().end())}; + Text::Position pos{toTextPos(c.position())}; + return TextSuggestion::Data{range, pos, c.text()}; + }); + editor->insertSuggestion( - std::make_unique(completions.first(), editor->document())); + std::make_unique(suggestions.first(), editor->document())); } } diff --git a/README.md b/README.md index 81b22ea..a9d3434 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build plugin](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml/badge.svg?branch=main)](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Palm1r/QodeAssist/total?color=41%2C173%2C71) ![GitHub Tag](https://img.shields.io/github/v/tag/Palm1r/QodeAssist) -![Static Badge](https://img.shields.io/badge/QtCreator-14.0.2-brightgreen) +![Static Badge](https://img.shields.io/badge/QtCreator-15.0.0-brightgreen) 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. @@ -144,6 +144,7 @@ ollama run deepseek-coder-v2 ## QtCreator Version Compatibility +- QtCreator 15.0.0 - 0.4.x - QtCreator 14.0.2 - 0.2.3 - 0.3.x - QtCreator 14.0.1 - 0.2.2 plugin version and below diff --git a/settings/ButtonAspect.hpp b/settings/ButtonAspect.hpp index 100ff6a..95de288 100644 --- a/settings/ButtonAspect.hpp +++ b/settings/ButtonAspect.hpp @@ -32,7 +32,7 @@ public: : Utils::BaseAspect(container) {} - void addToLayout(Layouting::Layout &parent) override + void addToLayoutImpl(Layouting::Layout &parent) override { auto button = new QPushButton(m_buttonText); connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);