From a974b0aa82e5340ee04fea192c137569b0a42717 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:19:37 +0200 Subject: [PATCH 1/4] Move to get models by request to provider --- QodeAssist.json.in | 2 +- QodeAssistSettings.cpp | 19 ++-------- QodeAssistSettings.hpp | 2 - README.md | 8 ++-- providers/LMStudioProvider.cpp | 60 ++++++++++-------------------- providers/OllamaProvider.cpp | 58 ++++++++++------------------- providers/OpenAICompatProvider.cpp | 1 - 7 files changed, 48 insertions(+), 102 deletions(-) diff --git a/QodeAssist.json.in b/QodeAssist.json.in index 264266e..9b060ef 100644 --- a/QodeAssist.json.in +++ b/QodeAssist.json.in @@ -1,6 +1,6 @@ { "Name" : "QodeAssist", - "Version" : "0.0.7", + "Version" : "0.0.8", "CompatVersion" : "${IDE_VERSION_COMPAT}", "Vendor" : "Petr Mironychev", "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", diff --git a/QodeAssistSettings.cpp b/QodeAssistSettings.cpp index b280afe..4997af6 100644 --- a/QodeAssistSettings.cpp +++ b/QodeAssistSettings.cpp @@ -77,7 +77,7 @@ QodeAssistSettings::QodeAssistSettings() temperature.setDefaultValue(0.2); temperature.setRange(0.0, 10.0); - selectModels.m_buttonText = Tr::tr("Select Models"); + selectModels.m_buttonText = Tr::tr("Select Model"); ollamaLivetime.setSettingsKey(Constants::OLLAMA_LIVETIME); ollamaLivetime.setLabelText( @@ -145,9 +145,6 @@ QodeAssistSettings::QodeAssistSettings() frequencyPenalty.setDefaultValue(0.0); frequencyPenalty.setRange(-2.0, 2.0); - providerPaths.setSettingsKey(Constants::PROVIDER_PATHS); - providerPaths.setLabelText(Tr::tr("Provider Paths:")); - startSuggestionTimer.setSettingsKey(Constants::START_SUGGESTION_TIMER); startSuggestionTimer.setLabelText(Tr::tr("Start Suggestion Timer:")); startSuggestionTimer.setRange(10, 10000); @@ -219,7 +216,7 @@ QodeAssistSettings::QodeAssistSettings() enableLogging, Row{Stretch{1}, resetToDefaults}}}}, Group{title(Tr::tr("LLM Providers")), - Form{Column{llmProviders, Row{url, endPoint}, providerPaths}}}, + Form{Column{llmProviders, Row{url, endPoint}}}}, Group{title(Tr::tr("LLM Model Settings")), Form{Column{Row{selectModels, modelName}}}}, Group{title(Tr::tr("FIM Prompt Settings")), @@ -306,7 +303,7 @@ QStringList QodeAssistSettings::getInstalledModels() { auto *provider = LLMProvidersManager::instance().getCurrentProvider(); if (provider) { - auto env = getEnvironmentWithProviderPaths(); + Utils::Environment env = Utils::Environment::systemEnvironment(); return provider->getInstalledModels(env); } return {}; @@ -331,16 +328,6 @@ void QodeAssistSettings::showModelSelectionDialog() } } -Utils::Environment QodeAssistSettings::getEnvironmentWithProviderPaths() const -{ - Utils::Environment env = Utils::Environment::systemEnvironment(); - const QStringList additionalPaths = providerPaths.volatileValue(); - for (const QString &path : additionalPaths) { - env.prependOrSetPath(path); - } - return env; -} - void QodeAssistSettings::resetSettingsToDefaults() { QMessageBox::StandardButton reply; diff --git a/QodeAssistSettings.hpp b/QodeAssistSettings.hpp index f974874..641f9d2 100644 --- a/QodeAssistSettings.hpp +++ b/QodeAssistSettings.hpp @@ -88,8 +88,6 @@ public: Utils::BoolAspect useFrequencyPenalty{this}; Utils::DoubleAspect frequencyPenalty{this}; - Utils::StringListAspect providerPaths{this}; - Utils::IntegerAspect startSuggestionTimer{this}; Utils::IntegerAspect maxFileThreshold{this}; diff --git a/README.md b/README.md index 32f1559..a463a31 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ QodeAssist has been tested with the following language models, all trained for F Ollama: - [starcoder2](https://ollama.com/library/starcoder2) - [codellama](https://ollama.com/library/codellama) +- DeepSeek-Coder-V2-Lite-Base LM studio: - [second-state/StarCoder2-7B-GGUF](https://huggingface.co/second-state/StarCoder2-7B-GGUF) @@ -30,7 +31,7 @@ If you've successfully used a model that's not listed here, please let us know b - [ ] Add chat functionality - [ ] Support for more providers and models -## Installation Plugin +## Plugin installation using Ollama as an example 1. Install QtCreator 14.0 2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation. @@ -50,9 +51,8 @@ ollama run starcoder2:7b 1. Open Qt Creator settings 2. Navigate to the "Qode Assist" tab 3. Choose your LLM provider (e.g., Ollama) - - If you haven't added the provider to your system PATH, specify the path to the provider executable in the "Provider Paths" field -4. Select the installed model - - If you need to enter the model name manually, it indicates that the plugin cannot locate the provider's executable file. However, this doesn't affect the plugin's functionality – it will still work correctly. This autoselector input option is provided for your convenience, allowing you to easily select and use different models +4. Select the installed model by the "Select Model" button + - For LM Studio you will see current load model 5. Choose the prompt template that corresponds to your model 6. Apply the settings diff --git a/providers/LMStudioProvider.cpp b/providers/LMStudioProvider.cpp index 946787d..4aa5df3 100644 --- a/providers/LMStudioProvider.cpp +++ b/providers/LMStudioProvider.cpp @@ -19,14 +19,15 @@ #include "LMStudioProvider.hpp" +#include #include #include #include #include -#include #include "PromptTemplateManager.hpp" #include "QodeAssistSettings.hpp" +#include "QodeAssistUtils.hpp" namespace QodeAssist::Providers { @@ -113,53 +114,32 @@ bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulated QList LMStudioProvider::getInstalledModels(const Utils::Environment &env) { - QProcess process; - process.setEnvironment(env.toStringList()); - QString lmsConsoleName; -#ifdef Q_OS_WIN - lmsConsoleName = "lms.exe"; -#else - lmsConsoleName = "lms"; -#endif - auto lmsPath = env.searchInPath(lmsConsoleName).toString(); + QList models; + QNetworkAccessManager manager; + QNetworkRequest request(QUrl(url() + "/v1/models")); - if (!QFileInfo::exists(lmsPath)) { - qWarning() << "LMS executable not found at" << lmsPath; - return {}; - } + QNetworkReply *reply = manager.get(request); - process.start(lmsPath, QStringList() << "ls"); - if (!process.waitForStarted()) { - qWarning() << "Failed to start LMS process:" << process.errorString(); - return {}; - } + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); - if (!process.waitForFinished()) { - qWarning() << "LMS process did not finish:" << process.errorString(); - return {}; - } + if (reply->error() == QNetworkReply::NoError) { + QByteArray responseData = reply->readAll(); + QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData); + QJsonObject jsonObject = jsonResponse.object(); + QJsonArray modelArray = jsonObject["data"].toArray(); - QStringList models; - if (process.exitCode() == 0) { - QString output = QString::fromUtf8(process.readAllStandardOutput()); - QStringList lines = output.split('\n', Qt::SkipEmptyParts); - - // Skip the header lines - for (int i = 2; i < lines.size(); ++i) { - QString line = lines[i].trimmed(); - if (!line.isEmpty()) { - // The model name is the first column - QString modelName = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts) - .first(); - models.append(modelName); - } + for (const QJsonValue &value : modelArray) { + QJsonObject modelObject = value.toObject(); + QString modelId = modelObject["id"].toString(); + models.append(modelId); } - qDebug() << "Models:" << models; } else { - // Handle error - qWarning() << "Error running 'lms list':" << process.errorString(); + logMessage(QString("Error fetching models: %1").arg(reply->errorString())); } + reply->deleteLater(); return models; } diff --git a/providers/OllamaProvider.cpp b/providers/OllamaProvider.cpp index 4da417e..41aaaa7 100644 --- a/providers/OllamaProvider.cpp +++ b/providers/OllamaProvider.cpp @@ -23,10 +23,11 @@ #include #include #include -#include +#include #include "PromptTemplateManager.hpp" #include "QodeAssistSettings.hpp" +#include "QodeAssistUtils.hpp" namespace QodeAssist::Providers { @@ -96,50 +97,31 @@ bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedRe QList OllamaProvider::getInstalledModels(const Utils::Environment &env) { - QProcess process; - process.setEnvironment(env.toStringList()); - QString ollamaConsoleName; -#ifdef Q_OS_WIN - ollamaConsoleName = "ollama.exe"; -#else - ollamaConsoleName = "ollama"; -#endif + QList models; + QNetworkAccessManager manager; + QNetworkRequest request(QUrl(url() + "/api/tags")); + QNetworkReply *reply = manager.get(request); - auto ollamaPath = env.searchInPath(ollamaConsoleName).toString(); + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); - if (!QFileInfo::exists(ollamaPath)) { - qWarning() << "Ollama executable not found at" << ollamaPath; - return {}; - } + if (reply->error() == QNetworkReply::NoError) { + QByteArray responseData = reply->readAll(); + QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData); + QJsonObject jsonObject = jsonResponse.object(); + QJsonArray modelArray = jsonObject["models"].toArray(); - process.start(ollamaPath, QStringList() << "list"); - if (!process.waitForStarted()) { - qWarning() << "Failed to start Ollama process:" << process.errorString(); - return {}; - } - - if (!process.waitForFinished()) { - qWarning() << "Ollama process did not finish:" << process.errorString(); - return {}; - } - - QStringList models; - if (process.exitCode() == 0) { - QString output = QString::fromUtf8(process.readAllStandardOutput()); - QStringList lines = output.split('\n', Qt::SkipEmptyParts); - - for (int i = 1; i < lines.size(); ++i) { - QString line = lines[i].trimmed(); - if (!line.isEmpty()) { - QString modelName = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts) - .first(); - models.append(modelName); - } + for (const QJsonValue &value : modelArray) { + QJsonObject modelObject = value.toObject(); + QString modelName = modelObject["name"].toString(); + models.append(modelName); } } else { - qWarning() << "Error running 'ollama list':" << process.errorString(); + logMessage(QString("Error fetching models: %1").arg(reply->errorString())); } + reply->deleteLater(); return models; } diff --git a/providers/OpenAICompatProvider.cpp b/providers/OpenAICompatProvider.cpp index 51fdc54..64d0b3a 100644 --- a/providers/OpenAICompatProvider.cpp +++ b/providers/OpenAICompatProvider.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include "PromptTemplateManager.hpp" #include "QodeAssistSettings.hpp" From 4d9adf75ffccb654e3e647febd791a5d615f8935 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:41:46 +0200 Subject: [PATCH 2/4] Add line inserting --- LLMSuggestion.cpp | 28 ++++++++++++++++++++++++++-- LLMSuggestion.hpp | 1 + QodeAssistHoverHandler.cpp | 16 +++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/LLMSuggestion.cpp b/LLMSuggestion.cpp index ab37c93..39f215c 100644 --- a/LLMSuggestion.cpp +++ b/LLMSuggestion.cpp @@ -19,6 +19,9 @@ #include "LLMSuggestion.hpp" +#include +#include + namespace QodeAssist { LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin) @@ -63,8 +66,29 @@ bool LLMSuggestion::apply() bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget) { - Q_UNUSED(widget) - return apply(); + return applyNextLine(widget); +} + +bool LLMSuggestion::applyNextLine(TextEditor::TextEditorWidget *widget) +{ + QTextCursor currentCursor = widget->textCursor(); + const QString text = m_completion.text(); + + int endPos = currentCursor.position(); + while (endPos < text.length() && !text[endPos].isSpace()) { + ++endPos; + } + + QString wordToInsert = text.left(endPos); + + wordToInsert = wordToInsert.split('\n').first(); + + if (!wordToInsert.isEmpty()) { + currentCursor.insertText(wordToInsert); + widget->setTextCursor(currentCursor); + } + + return false; } void LLMSuggestion::reset() diff --git a/LLMSuggestion.hpp b/LLMSuggestion.hpp index c5536f3..de4a9cd 100644 --- a/LLMSuggestion.hpp +++ b/LLMSuggestion.hpp @@ -32,6 +32,7 @@ public: bool apply() final; bool applyWord(TextEditor::TextEditorWidget *widget) final; + bool applyNextLine(TextEditor::TextEditorWidget *widget); void reset() final; int position() final; diff --git a/QodeAssistHoverHandler.cpp b/QodeAssistHoverHandler.cpp index 8db717f..849f8f7 100644 --- a/QodeAssistHoverHandler.cpp +++ b/QodeAssistHoverHandler.cpp @@ -54,18 +54,32 @@ public: { auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString())); connect(apply, &QAction::triggered, this, &QodeAssistCompletionToolTip::apply); + + auto applyWord = addAction(Tr::tr("Apply Next Line (%1)") + .arg(QKeySequence(QKeySequence::MoveToNextLine).toString())); + connect(applyWord, &QAction::triggered, this, &QodeAssistCompletionToolTip::applyWord); + qDebug() << "applyWord sequence" << applyWord->shortcut(); } private: void apply() { - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { + if (auto *suggestion = dynamic_cast(m_editor->currentSuggestion())) { if (!suggestion->apply()) return; } ToolTip::hide(); } + void applyWord() + { + if (auto *suggestion = dynamic_cast(m_editor->currentSuggestion())) { + if (!suggestion->applyWord(m_editor)) + return; + } + ToolTip::hide(); + } + TextEditorWidget *m_editor; }; From 753365ea5297f31c2f5e6bfda9dc4861e4af3c31 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:54:14 +0200 Subject: [PATCH 3/4] Add multiline insert support --- CMakeLists.txt | 2 +- LLMSuggestion.cpp | 44 ++++-- LLMSuggestion.hpp | 13 +- LSPCompletion.hpp | 4 + QodeAssistClient.cpp | 6 - QodeAssistClient.hpp | 2 - QodeAssistHoverHandler.cpp | 128 ------------------ utils/CounterTooltip.cpp | 48 +++++++ .../CounterTooltip.hpp | 37 ++--- 9 files changed, 115 insertions(+), 169 deletions(-) delete mode 100644 QodeAssistHoverHandler.cpp create mode 100644 utils/CounterTooltip.cpp rename QodeAssistHoverHandler.hpp => utils/CounterTooltip.hpp (52%) diff --git a/CMakeLists.txt b/CMakeLists.txt index deb32f3..0b5d121 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,9 @@ add_qtc_plugin(QodeAssist QodeAssist.qrc LSPCompletion.hpp LLMSuggestion.hpp LLMSuggestion.cpp - QodeAssistHoverHandler.hpp QodeAssistHoverHandler.cpp QodeAssistClient.hpp QodeAssistClient.cpp QodeAssistUtils.hpp DocumentContextReader.hpp DocumentContextReader.cpp QodeAssistData.hpp + utils/CounterTooltip.hpp utils/CounterTooltip.cpp ) diff --git a/LLMSuggestion.cpp b/LLMSuggestion.cpp index 39f215c..1fd50f5 100644 --- a/LLMSuggestion.cpp +++ b/LLMSuggestion.cpp @@ -19,13 +19,17 @@ #include "LLMSuggestion.hpp" +#include +#include #include #include +#include namespace QodeAssist { LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin) : m_completion(completion) + , m_linesCount(0) { int startPos = completion.range().start().toPositionInDocument(origin); int endPos = completion.range().end().toPositionInDocument(origin); @@ -71,24 +75,30 @@ bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget) bool LLMSuggestion::applyNextLine(TextEditor::TextEditorWidget *widget) { - QTextCursor currentCursor = widget->textCursor(); const QString text = m_completion.text(); + QStringList lines = text.split('\n'); - int endPos = currentCursor.position(); - while (endPos < text.length() && !text[endPos].isSpace()) { - ++endPos; - } + if (m_linesCount < lines.size()) + m_linesCount++; - QString wordToInsert = text.left(endPos); + showTooltip(widget, m_linesCount); - wordToInsert = wordToInsert.split('\n').first(); + return m_linesCount == lines.size() && !Utils::ToolTip::isVisible(); +} - if (!wordToInsert.isEmpty()) { - currentCursor.insertText(wordToInsert); - widget->setTextCursor(currentCursor); - } +void LLMSuggestion::onCounterFinished(int count) +{ + Utils::ToolTip::hide(); + m_linesCount = 0; + QTextCursor cursor = m_completion.range().toSelection(m_start.document()); + cursor.beginEditBlock(); + cursor.removeSelectedText(); - return false; + QStringList lines = m_completion.text().split('\n'); + QString textToInsert = lines.mid(0, count).join('\n'); + + cursor.insertText(textToInsert); + cursor.endEditBlock(); } void LLMSuggestion::reset() @@ -101,4 +111,14 @@ 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); +} + } // namespace QodeAssist diff --git a/LLMSuggestion.hpp b/LLMSuggestion.hpp index de4a9cd..fcf716c 100644 --- a/LLMSuggestion.hpp +++ b/LLMSuggestion.hpp @@ -19,14 +19,17 @@ #pragma once +#include +#include "LSPCompletion.hpp" #include -#include "LSPCompletion.hpp" +#include "utils/CounterTooltip.hpp" namespace QodeAssist { -class LLMSuggestion final : public TextEditor::TextSuggestion +class LLMSuggestion final : public QObject, public TextEditor::TextSuggestion { + Q_OBJECT public: LLMSuggestion(const Completion &completion, QTextDocument *origin); @@ -38,9 +41,15 @@ public: const Completion &completion() const { return m_completion; } + void showTooltip(TextEditor::TextEditorWidget *widget, int count); + void onCounterFinished(int count); + private: Completion m_completion; QTextCursor m_start; + int m_linesCount; + + CounterTooltip *m_counterTooltip = nullptr; }; } // namespace QodeAssist diff --git a/LSPCompletion.hpp b/LSPCompletion.hpp index ea80fd7..7209999 100644 --- a/LSPCompletion.hpp +++ b/LSPCompletion.hpp @@ -43,6 +43,10 @@ public: { return typedValue(LanguageServerProtocol::positionKey); } + void setRange(const LanguageServerProtocol::Range &range) + { + insert(LanguageServerProtocol::rangeKey, range); + } LanguageServerProtocol::Range range() const { return typedValue(LanguageServerProtocol::rangeKey); diff --git a/QodeAssistClient.cpp b/QodeAssistClient.cpp index 260d122..296c519 100644 --- a/QodeAssistClient.cpp +++ b/QodeAssistClient.cpp @@ -190,7 +190,6 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r return; editor->insertSuggestion( std::make_unique(completions.first(), editor->document())); - editor->addHoverHandler(&m_hoverHandler); } } @@ -237,11 +236,6 @@ void QodeAssistClient::cleanupConnections() disconnect(m_documentOpenedConnection); disconnect(m_documentClosedConnection); - for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) { - if (auto textEditor = qobject_cast(editor)) - textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler); - } - qDeleteAll(m_scheduledRequests); m_scheduledRequests.clear(); } diff --git a/QodeAssistClient.hpp b/QodeAssistClient.hpp index 73c1c7b..0271839 100644 --- a/QodeAssistClient.hpp +++ b/QodeAssistClient.hpp @@ -27,7 +27,6 @@ #include #include "LSPCompletion.hpp" -#include "QodeAssistHoverHandler.hpp" namespace QodeAssist { @@ -54,7 +53,6 @@ private: QHash m_runningRequests; QHash m_scheduledRequests; - QodeAssistHoverHandler m_hoverHandler; QMetaObject::Connection m_documentOpenedConnection; QMetaObject::Connection m_documentClosedConnection; }; diff --git a/QodeAssistHoverHandler.cpp b/QodeAssistHoverHandler.cpp deleted file mode 100644 index 849f8f7..0000000 --- a/QodeAssistHoverHandler.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2023 The Qt Company Ltd. - * Copyright (C) 2024 Petr Mironychev - * - * This file is part of Qode Assist. - * - * The Qt Company portions: - * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - * - * Petr Mironychev portions: - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "QodeAssistHoverHandler.hpp" - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "LLMSuggestion.hpp" -#include "LSPCompletion.hpp" -#include "QodeAssisttr.h" - -using namespace TextEditor; -using namespace LanguageServerProtocol; -using namespace Utils; - -namespace QodeAssist { - -class QodeAssistCompletionToolTip : public QToolBar -{ -public: - QodeAssistCompletionToolTip(TextEditorWidget *editor) - : m_editor(editor) - { - auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString())); - connect(apply, &QAction::triggered, this, &QodeAssistCompletionToolTip::apply); - - auto applyWord = addAction(Tr::tr("Apply Next Line (%1)") - .arg(QKeySequence(QKeySequence::MoveToNextLine).toString())); - connect(applyWord, &QAction::triggered, this, &QodeAssistCompletionToolTip::applyWord); - qDebug() << "applyWord sequence" << applyWord->shortcut(); - } - -private: - void apply() - { - if (auto *suggestion = dynamic_cast(m_editor->currentSuggestion())) { - if (!suggestion->apply()) - return; - } - ToolTip::hide(); - } - - void applyWord() - { - if (auto *suggestion = dynamic_cast(m_editor->currentSuggestion())) { - if (!suggestion->applyWord(m_editor)) - return; - } - ToolTip::hide(); - } - - TextEditorWidget *m_editor; -}; - -void QodeAssistHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget, - int pos, - ReportPriority report) -{ - QScopeGuard cleanup([&] { report(Priority_None); }); - if (!editorWidget->suggestionVisible()) - return; - - QTextCursor cursor(editorWidget->document()); - cursor.setPosition(pos); - m_block = cursor.block(); - auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); - - if (!suggestion) - return; - - const Completion completion = suggestion->completion(); - if (completion.text().isEmpty()) - return; - - cleanup.dismiss(); - report(Priority_Suggestion); -} - -void QodeAssistHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget, - const QPoint &point) -{ - Q_UNUSED(point) - auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); - - if (!suggestion) - return; - - auto tooltipWidget = new QodeAssistCompletionToolTip(editorWidget); - - const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor()); - QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft()) - - Utils::ToolTip::offsetFromPosition(); - pos.ry() -= tooltipWidget->sizeHint().height(); - ToolTip::show(pos, tooltipWidget, editorWidget); -} - -} // namespace QodeAssist diff --git a/utils/CounterTooltip.cpp b/utils/CounterTooltip.cpp new file mode 100644 index 0000000..ec5a699 --- /dev/null +++ b/utils/CounterTooltip.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 Petr Mironychev + * + * This file is part of QodeAssist. + * + * QodeAssist is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QodeAssist is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QodeAssist. If not, see . + */ + +#include "CounterTooltip.hpp" + +namespace QodeAssist { + +CounterTooltip::CounterTooltip(int count) + : m_count(count) +{ + m_label = new QLabel(this); + addWidget(m_label); + updateLabel(); + + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + m_timer->setInterval(2000); + + connect(m_timer, &QTimer::timeout, this, [this] { emit finished(m_count); }); + + m_timer->start(); +} + +CounterTooltip::~CounterTooltip() {} + +void CounterTooltip::updateLabel() +{ + const auto hotkey = QKeySequence(QKeySequence::MoveToNextWord).toString(); + m_label->setText(QString("Insert Next %1 line(s) (%2)").arg(m_count).arg(hotkey)); +} + +} // namespace QodeAssist diff --git a/QodeAssistHoverHandler.hpp b/utils/CounterTooltip.hpp similarity index 52% rename from QodeAssistHoverHandler.hpp rename to utils/CounterTooltip.hpp index 30733a2..5e53e00 100644 --- a/QodeAssistHoverHandler.hpp +++ b/utils/CounterTooltip.hpp @@ -1,13 +1,8 @@ -/* - * Copyright (C) 2023 The Qt Company Ltd. +/* * Copyright (C) 2024 Petr Mironychev * - * This file is part of Qode Assist. + * This file is part of QodeAssist. * - * The Qt Company portions: - * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - * - * Petr Mironychev portions: * QodeAssist is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -24,24 +19,30 @@ #pragma once -#include -#include +#include +#include +#include +#include namespace QodeAssist { -class QodeAssistHoverHandler : public TextEditor::BaseHoverHandler +class CounterTooltip : public QToolBar { -public: - QodeAssistHoverHandler() = default; + Q_OBJECT -protected: - void identifyMatch(TextEditor::TextEditorWidget *editorWidget, - int pos, - ReportPriority report) final; - void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final; +public: + CounterTooltip(int count); + ~CounterTooltip(); + +signals: + void finished(int count); private: - QTextBlock m_block; + void updateLabel(); + + QLabel *m_label; + QTimer *m_timer; + int m_count; }; } // namespace QodeAssist From ada65e9ef715a9624c49822c09a939b4e92126a8 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:02:23 +0200 Subject: [PATCH 4/4] Add hotkeys tip --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a463a31..5c80c62 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ ollama run starcoder2:7b You're all set! QodeAssist is now ready to use in Qt Creator. +## Hotkeys +- To insert the full suggestion, you can use the TAB key +- To insert line by line, you can use the "Move cursor word right" shortcut: + - On Mac: Option + Right Arrow + - On Windows: Alt + Right Arrow + ## Support the development of QodeAssist If you find QodeAssist helpful, there are several ways you can support the project: