From 42199024ff3bf59ca0f26a95673e28884160b51d Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:56:23 +0200 Subject: [PATCH] refactor: Improvement code completion auto trigger --- QodeAssistClient.cpp | 345 ++++++++++++++-------------- QodeAssistClient.hpp | 9 +- qodeassist.cpp | 6 +- settings/CodeCompletionSettings.cpp | 60 ++++- settings/CodeCompletionSettings.hpp | 4 + settings/SettingsConstants.hpp | 3 + 6 files changed, 228 insertions(+), 199 deletions(-) diff --git a/QodeAssistClient.cpp b/QodeAssistClient.cpp index 552762c..a7bc8c8 100644 --- a/QodeAssistClient.cpp +++ b/QodeAssistClient.cpp @@ -54,6 +54,90 @@ using namespace Core; namespace QodeAssist { +namespace { +Utils::Text::Position toTextPos(const Utils::Text::Position &pos) +{ + return Utils::Text::Position{pos.line, pos.column}; +} + +bool isIdentifierChar(QChar c) +{ + return c.isLetterOrNumber() || c == QLatin1Char('_'); +} + +bool isInsideIdentifier(const QTextCursor &cursor) +{ + const QTextBlock block = cursor.block(); + const int col = cursor.positionInBlock(); + const QString text = block.text(); + if (col <= 0 || col > text.size()) + return false; + if (!isIdentifierChar(text.at(col - 1))) + return false; + return col < text.size() && isIdentifierChar(text.at(col)); +} + +bool isAfterMemberAccess(const QTextCursor &cursor) +{ + const QTextBlock block = cursor.block(); + const int col = cursor.positionInBlock(); + const QString text = block.text(); + if (col <= 0) + return false; + + int i = col - 1; + while (i >= 0 && isIdentifierChar(text.at(i))) + --i; + + if (i < 0) + return false; + + const QChar c = text.at(i); + if (c == QLatin1Char('.')) + return true; + if (c == QLatin1Char('>') && i >= 1 && text.at(i - 1) == QLatin1Char('-')) + return true; + if (c == QLatin1Char(':') && i >= 1 && text.at(i - 1) == QLatin1Char(':')) + return true; + return false; +} + +bool isFreshIndentedLine(const QTextCursor &cursor) +{ + const QTextBlock block = cursor.block(); + const int col = cursor.positionInBlock(); + if (col == 0) + return false; + const QString leftText = block.text().left(col); + for (const QChar &ch : leftText) { + if (!ch.isSpace()) + return false; + } + return true; +} + +bool isAfterEagerTrigger(const QTextCursor &cursor) +{ + const QTextBlock block = cursor.block(); + const int col = cursor.positionInBlock(); + const QString text = block.text(); + int i = col - 1; + while (i >= 0 && text.at(i).isSpace()) + --i; + if (i < 0) + return false; + const QChar c = text.at(i); + return c == QLatin1Char('{') || c == QLatin1Char('(') || c == QLatin1Char(',') + || c == QLatin1Char('=') || c == QLatin1Char('[') || c == QLatin1Char(';') + || c == QLatin1Char(':') || c == QLatin1Char('>'); +} + +bool isManualMode() +{ + return Settings::codeCompletionSettings().completionMode.stringValue() == "Manual"; +} +} // anonymous namespace + QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface) : LanguageClient::Client(clientInterface) , m_llmClient(clientInterface) @@ -69,10 +153,6 @@ QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface) m_typingTimer.start(); - m_hintHideTimer.setSingleShot(true); - m_hintHideTimer.setInterval(Settings::codeCompletionSettings().hintHideTimeout()); - connect(&m_hintHideTimer, &QTimer::timeout, this, [this]() { m_hintHandler.hideHint(); }); - m_refactorHoverHandler = new RefactorSuggestionHoverHandler(); m_refactorWidgetHandler = new RefactorWidgetHandler(this); } @@ -108,6 +188,9 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document) if (!Settings::codeCompletionSettings().autoCompletion()) return; + if (isManualMode()) + return; + auto project = ProjectManager::projectForFile(document->filePath()); if (!isEnabled(project)) return; @@ -131,38 +214,29 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document) if (charsRemoved > 0 || charsAdded <= 0) { m_recentCharCount = 0; m_typingTimer.restart(); - // 0 = Hint-based, 1 = Automatic - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - if (triggerMode != 1) { - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); - } return; } QTextCursor cursor = widget->textCursor(); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); - QString lastChar = cursor.selectedText(); + const QString lastChar = cursor.selectedText(); + if (lastChar.isEmpty()) + return; - if (lastChar.isEmpty() || lastChar[0].isPunct()) { + const QChar lastCh = lastChar[0]; + if (lastCh == QLatin1Char('\n') || lastCh == QChar::ParagraphSeparator + || lastCh == QChar::LineSeparator) { m_recentCharCount = 0; m_typingTimer.restart(); - // 0 = Hint-based, 1 = Automatic - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - if (triggerMode != 1) { - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); - } return; } - bool isSpaceOrTab = lastChar[0].isSpace(); - bool ignoreWhitespace + const bool isSpaceOrTab = lastCh.isSpace(); + const bool ignoreWhitespace = Settings::codeCompletionSettings().ignoreWhitespaceInCharCount(); - if (!ignoreWhitespace || !isSpaceOrTab) { + if (!ignoreWhitespace || !isSpaceOrTab) m_recentCharCount += charsAdded; - } if (m_typingTimer.elapsed() > Settings::codeCompletionSettings().autoCompletionTypingInterval()) { @@ -170,13 +244,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document) m_typingTimer.restart(); } - // 0 = Hint-based, 1 = Automatic - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - if (triggerMode == 1) { - handleAutoRequestTrigger(widget, charsAdded, isSpaceOrTab); - } else { - handleHintBasedTrigger(widget, charsAdded, isSpaceOrTab, cursor); - } + handleAutoRequestTrigger(widget); }); } @@ -205,11 +273,9 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor) if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible()) return; - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - - if (Settings::codeCompletionSettings().abortAssistOnRequest() && triggerMode == 0) { + const auto &settings = Settings::codeCompletionSettings(); + if (settings.abortAssistOnRequest() && !settings.respectQtcPopup()) editor->abortAssist(); - } const FilePath filePath = editor->textDocument()->filePath(); GetCompletionRequest request{ @@ -270,33 +336,25 @@ void QodeAssistClient::requestQuickRefactor( void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor) { - cancelRunningRequest(editor); + if (m_runningRequests.contains(editor)) + return; auto it = m_scheduledRequests.find(editor); if (it == m_scheduledRequests.end()) { auto timer = new QTimer(this); timer->setSingleShot(true); connect(timer, &QTimer::timeout, this, [this, editor]() { - if (editor - && editor->textCursor().position() - == m_scheduledRequests[editor]->property("cursorPosition").toInt() - && m_recentCharCount - > Settings::codeCompletionSettings().autoCompletionCharThreshold()) - requestCompletions(editor); + if (!editor || m_runningRequests.contains(editor)) + return; + if (editor->textCursor().position() + != m_scheduledRequests[editor]->property("cursorPosition").toInt()) + return; + requestCompletions(editor); }); connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() { delete m_scheduledRequests.take(editor); cancelRunningRequest(editor); }); - connect(editor, &TextEditorWidget::cursorPositionChanged, this, [this, editor] { - cancelRunningRequest(editor); - // 0 = Hint-based, 1 = Automatic - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - if (triggerMode != 1) { - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); - } - }); it = m_scheduledRequests.insert(editor, timer); } @@ -307,11 +365,9 @@ void QodeAssistClient::handleCompletions( const GetCompletionRequest::Response &response, TextEditor::TextEditorWidget *editor) { m_progressHandler.hideProgress(); - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - - if (Settings::codeCompletionSettings().abortAssistOnRequest() && triggerMode == 1) { + const auto &settings = Settings::codeCompletionSettings(); + if (settings.abortAssistOnRequest() && !settings.respectQtcPopup()) editor->abortAssist(); - } if (response.error()) { log(*response.error()); @@ -325,12 +381,25 @@ void QodeAssistClient::handleCompletions( requestPosition = requestParams->position().toPositionInDocument(editor->document()); const MultiTextCursor cursors = editor->multiTextCursor(); - if (cursors.hasMultipleCursors()) + if (cursors.hasMultipleCursors() || cursors.hasSelection()) return; - if (cursors.hasSelection() || cursors.mainCursor().position() != requestPosition) + const int currentPosition = cursors.mainCursor().position(); + if (requestPosition < 0 || currentPosition < requestPosition) return; + QString typedSinceRequest; + if (currentPosition > requestPosition) { + QTextCursor diffCursor(editor->document()); + diffCursor.setPosition(requestPosition); + diffCursor.setPosition(currentPosition, QTextCursor::KeepAnchor); + typedSinceRequest = diffCursor.selectedText(); + if (typedSinceRequest.contains(QChar::ParagraphSeparator) + || typedSinceRequest.contains(QLatin1Char('\n'))) { + return; + } + } + if (const std::optional result = response.result()) { auto isValidCompletion = [](const Completion &completion) { return completion.isValid() && !completion.text().trimmed().isEmpty(); @@ -338,34 +407,58 @@ void QodeAssistClient::handleCompletions( QList completions = Utils::filtered(result->completions().toListOrEmpty(), isValidCompletion); + QList matchedCompletions; + matchedCompletions.reserve(completions.size()); for (Completion &completion : completions) { const LanguageServerProtocol::Range range = completion.range(); if (range.start().line() != range.end().line()) continue; - const QString completionText = completion.text(); + QString completionText = completion.text(); const int end = int(completionText.size()) - 1; int delta = 0; while (delta <= end && completionText[end - delta].isSpace()) ++delta; - if (delta > 0) - completion.setText(completionText.chopped(delta)); + completionText.chop(delta); + + if (!typedSinceRequest.isEmpty()) { + if (!completionText.startsWith(typedSinceRequest)) + continue; + completionText = completionText.mid(typedSinceRequest.size()); + if (completionText.isEmpty()) + continue; + } + + completion.setText(completionText); + matchedCompletions.append(completion); } - auto suggestions = Utils::transform(completions, [](const Completion &c) { + + if (matchedCompletions.isEmpty()) { + LOG_MESSAGE("No valid completions received"); + return; + } + + const Text::Position anchor = typedSinceRequest.isEmpty() + ? Text::Position{} + : Text::Position::fromPositionInDocument(editor->document(), currentPosition); + const bool useAnchor = !typedSinceRequest.isEmpty(); + + auto suggestions = Utils::transform(matchedCompletions, + [useAnchor, &anchor](const Completion &c) { auto toTextPos = [](const LanguageServerProtocol::Position pos) { return Text::Position{pos.line() + 1, pos.character()}; }; + if (useAnchor) { + return TextSuggestion::Data{Text::Range{anchor, anchor}, anchor, c.text()}; + } + Text::Range range{toTextPos(c.range().start()), toTextPos(c.range().end())}; Text::Position pos{toTextPos(c.position())}; return TextSuggestion::Data{range, pos, c.text()}; }); - if (completions.isEmpty()) { - LOG_MESSAGE("No valid completions received"); - return; - } editor->insertSuggestion(std::make_unique(suggestions, editor->document())); } } @@ -376,12 +469,6 @@ void QodeAssistClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor if (it == m_runningRequests.constEnd()) return; m_progressHandler.hideProgress(); - // 0 = Hint-based, 1 = Automatic - const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); - if (triggerMode != 1) { - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); - } cancelRequest(it->id()); m_runningRequests.erase(it); } @@ -423,17 +510,6 @@ void QodeAssistClient::cleanupConnections() m_scheduledRequests.clear(); } -bool QodeAssistClient::isHintVisible() const -{ - return m_hintHandler.isHintVisible(); -} - -void QodeAssistClient::hideHintAndRequestCompletion(TextEditor::TextEditorWidget *editor) -{ - m_hintHandler.hideHint(); - requestCompletions(editor); -} - void QodeAssistClient::handleRefactoringResult(const RefactorResult &result) { m_progressHandler.hideProgress(); @@ -465,13 +541,6 @@ void QodeAssistClient::handleRefactoringResult(const RefactorResult &result) } } -namespace { -Utils::Text::Position toTextPos(const Utils::Text::Position &pos) -{ - return Utils::Text::Position{pos.line, pos.column}; -} -} // anonymous namespace - void QodeAssistClient::displayRefactoringSuggestion(const RefactorResult &result) { TextEditorWidget *editorWidget = result.editor; @@ -604,58 +673,20 @@ void QodeAssistClient::applyRefactoringEdit(TextEditor::TextEditorWidget *editor editCursor.endEditBlock(); } -void QodeAssistClient::handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget, - int charsAdded, - bool isSpaceOrTab) +void QodeAssistClient::handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget) { - Q_UNUSED(isSpaceOrTab); + const QTextCursor cursor = widget->textCursor(); + const auto &settings = Settings::codeCompletionSettings(); + const bool smart = settings.smartContextTrigger(); - if (m_recentCharCount - > Settings::codeCompletionSettings().autoCompletionCharThreshold()) { + if (smart && (isInsideIdentifier(cursor) || isAfterMemberAccess(cursor))) + return; + + const bool eager = smart && (isFreshIndentedLine(cursor) || isAfterEagerTrigger(cursor)); + const int charThreshold = settings.autoCompletionCharThreshold(); + + if (eager || m_recentCharCount > charThreshold) scheduleRequest(widget); - } -} - -void QodeAssistClient::handleHintBasedTrigger(TextEditor::TextEditorWidget *widget, - int charsAdded, - bool isSpaceOrTab, - QTextCursor &cursor) -{ - Q_UNUSED(charsAdded); - - const int hintThreshold = Settings::codeCompletionSettings().hintCharThreshold(); - if (m_recentCharCount >= hintThreshold && !isSpaceOrTab) { - const QRect cursorRect = widget->cursorRect(cursor); - QPoint globalPos = widget->viewport()->mapToGlobal(cursorRect.topLeft()); - QPoint localPos = widget->mapFromGlobal(globalPos); - - int fontSize = widget->font().pixelSize(); - if (fontSize <= 0) { - fontSize = widget->fontMetrics().height(); - } - - QTextCursor textCursor = widget->textCursor(); - - if (m_recentCharCount <= hintThreshold) { - textCursor - .movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, m_recentCharCount); - } else { - textCursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, hintThreshold); - } - - int x = localPos.x() + cursorRect.height(); - int y = localPos.y() + cursorRect.height() / 4; - - QPoint hintPos(x, y); - - if (!m_hintHandler.isHintVisible()) { - m_hintHandler.showHint(widget, hintPos, fontSize); - } else { - m_hintHandler.updateHintPosition(widget, hintPos); - } - - m_hintHideTimer.start(); - } } bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event) @@ -667,46 +698,6 @@ bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event) if (event->type() == QEvent::KeyPress) { auto *keyEvent = static_cast(event); - // Check hint trigger key (0=Space, 1=Ctrl+Space, 2=Alt+Space, 3=Ctrl+Enter, 4=Tab, 5=Enter) - if (m_hintHandler.isHintVisible()) { - const int triggerKeyIndex = Settings::codeCompletionSettings().hintTriggerKey(); - bool isMatchingKey = false; - const Qt::KeyboardModifiers modifiers = keyEvent->modifiers(); - - switch (triggerKeyIndex) { - case 0: // Space - isMatchingKey = (keyEvent->key() == Qt::Key_Space - && (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)); - break; - case 1: // Ctrl+Space - isMatchingKey = (keyEvent->key() == Qt::Key_Space - && (modifiers & Qt::ControlModifier)); - break; - case 2: // Alt+Space - isMatchingKey = (keyEvent->key() == Qt::Key_Space - && (modifiers & Qt::AltModifier)); - break; - case 3: // Ctrl+Enter - isMatchingKey = ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) - && (modifiers & Qt::ControlModifier)); - break; - case 4: // Tab - isMatchingKey = (keyEvent->key() == Qt::Key_Tab); - break; - case 5: // Enter - isMatchingKey = ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) - && (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)); - break; - } - - if (isMatchingKey) { - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); - requestCompletions(editor); - return true; - } - } - if (keyEvent->key() == Qt::Key_Escape) { if (m_runningRequests.contains(editor)) { cancelRunningRequest(editor); @@ -724,8 +715,6 @@ bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event) } m_progressHandler.hideProgress(); - m_hintHideTimer.stop(); - m_hintHandler.hideHint(); } } diff --git a/QodeAssistClient.hpp b/QodeAssistClient.hpp index 82a92ee..aa50615 100644 --- a/QodeAssistClient.hpp +++ b/QodeAssistClient.hpp @@ -12,7 +12,6 @@ #include "RefactorSuggestionHoverHandler.hpp" #include "widgets/CompletionProgressHandler.hpp" #include "widgets/CompletionErrorHandler.hpp" -#include "widgets/CompletionHintHandler.hpp" #include "widgets/EditorChatButtonHandler.hpp" #include "widgets/RefactorWidgetHandler.hpp" #include @@ -34,9 +33,6 @@ public: void requestCompletions(TextEditor::TextEditorWidget *editor); void requestQuickRefactor( TextEditor::TextEditorWidget *editor, const QString &instructions = QString()); - - bool isHintVisible() const; - void hideHintAndRequestCompletion(TextEditor::TextEditorWidget *editor); protected: bool eventFilter(QObject *watched, QEvent *event) override; @@ -55,8 +51,7 @@ private: void displayRefactoringWidget(const RefactorResult &result); void applyRefactoringEdit(TextEditor::TextEditorWidget *editor, const Utils::Text::Range &range, const QString &text); - void handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget, int charsAdded, bool isSpaceOrTab); - void handleHintBasedTrigger(TextEditor::TextEditorWidget *widget, int charsAdded, bool isSpaceOrTab, QTextCursor &cursor); + void handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget); QHash m_runningRequests; QHash m_scheduledRequests; @@ -65,10 +60,8 @@ private: QElapsedTimer m_typingTimer; int m_recentCharCount; - QTimer m_hintHideTimer; CompletionProgressHandler m_progressHandler; CompletionErrorHandler m_errorHandler; - CompletionHintHandler m_hintHandler; EditorChatButtonHandler m_chatButtonHandler; QuickRefactorHandler *m_refactorHandler{nullptr}; RefactorSuggestionHoverHandler *m_refactorHoverHandler{nullptr}; diff --git a/qodeassist.cpp b/qodeassist.cpp index 0df4d42..0620a37 100644 --- a/qodeassist.cpp +++ b/qodeassist.cpp @@ -136,11 +136,7 @@ public: requestAction.addOnTriggered(this, [this] { if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) { if (m_qodeAssistClient && m_qodeAssistClient->reachable()) { - if (m_qodeAssistClient->isHintVisible()) { - m_qodeAssistClient->hideHintAndRequestCompletion(editor); - } else { - m_qodeAssistClient->requestCompletions(editor); - } + m_qodeAssistClient->requestCompletions(editor); } else qWarning() << "The QodeAssist is not ready. Please check your connection and " "settings."; diff --git a/settings/CodeCompletionSettings.cpp b/settings/CodeCompletionSettings.cpp index 585d3b6..392df35 100644 --- a/settings/CodeCompletionSettings.cpp +++ b/settings/CodeCompletionSettings.cpp @@ -59,6 +59,34 @@ CodeCompletionSettings::CodeCompletionSettings() Tr::tr("Hint-based: Shows a hint when typing, press Tab to request completion\n" "Automatic: Automatically requests completion after typing threshold")); + completionMode.setLabelText(Tr::tr("Completion mode:")); + completionMode.setSettingsKey(Constants::CC_COMPLETION_MODE); + completionMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); + completionMode.addOption("Automatic"); + completionMode.addOption("Manual"); + completionMode.setDefaultValue("Automatic"); + completionMode.setToolTip( + Tr::tr("Automatic: requests completion while typing (with smart context gates).\n" + "Manual: no auto-triggering; invoke via the 'Request QodeAssist Suggestion' " + "shortcut (default Ctrl+Alt+Q, reconfigurable in Preferences > Keyboard).")); + + smartContextTrigger.setSettingsKey(Constants::CC_SMART_CONTEXT_TRIGGER); + smartContextTrigger.setLabelText(Tr::tr("Smart context-aware triggering")); + smartContextTrigger.setDefaultValue(true); + smartContextTrigger.setToolTip( + Tr::tr("When enabled, auto-completion is suppressed in places where Qt Creator's built-in " + "completion is usually stronger (middle of an identifier, right after '.', '->', " + "'::') and is triggered more eagerly after structural characters like '(', ',', " + "'{', '=' and on fresh indented lines.")); + + respectQtcPopup.setSettingsKey(Constants::CC_RESPECT_QTC_POPUP); + respectQtcPopup.setLabelText(Tr::tr("Don't dismiss Qt Creator's completion popup")); + respectQtcPopup.setDefaultValue(true); + respectQtcPopup.setToolTip( + Tr::tr("When enabled, an AI completion arriving while Qt Creator's own completion popup " + "is already visible will not force it closed. The LLM suggestion still appears " + "inline.")); + startSuggestionTimer.setSettingsKey(Constants::СС_START_SUGGESTION_TIMER); startSuggestionTimer.setLabelText(Tr::tr("with delay(ms)")); startSuggestionTimer.setToolTip( @@ -308,6 +336,8 @@ CodeCompletionSettings::CodeCompletionSettings() readSettings(); + migrateCompletionMode(); + readFileParts.setValue(!readFullFile.value()); setupConnections(); @@ -354,30 +384,26 @@ CodeCompletionSettings::CodeCompletionSettings() autoCompletion, multiLineCompletion, Row{modelOutputHandler, Stretch{1}}, - Row{completionTriggerMode, Stretch{1}}, + Row{completionMode, Stretch{1}}, showProgressWidget, useOpenFilesContext, + respectQtcPopup, abortAssistOnRequest, ignoreWhitespaceInCharCount}; auto autoTriggerSettings = Column{ + smartContextTrigger, Row{autoCompletionCharThreshold, autoCompletionTypingInterval, startSuggestionTimer, Stretch{1}}}; - auto hintTriggerSettings = Column{ - Row{hintCharThreshold, hintHideTimeout, Stretch{1}}, - Row{hintTriggerKey, Stretch{1}}}; - return Column{Row{Stretch{1}, resetToDefaults}, Space{8}, Group{title(TrConstants::AUTO_COMPLETION_SETTINGS), Column{Group{title(Tr::tr("General Settings")), generalSettings}, Space{8}, - Group{title(Tr::tr("Automatic Trigger Mode")), autoTriggerSettings}, - Space{8}, - Group{title(Tr::tr("Hint-based Trigger Mode")), hintTriggerSettings}}}, + Group{title(Tr::tr("Automatic Trigger Mode")), autoTriggerSettings}}}, Space{8}, Group{title(Tr::tr("General Parameters")), Column{ @@ -460,6 +486,9 @@ void CodeCompletionSettings::resetSettingsToDefaults() resetAspect(useOpenFilesContext); resetAspect(modelOutputHandler); resetAspect(completionTriggerMode); + resetAspect(completionMode); + resetAspect(smartContextTrigger); + resetAspect(respectQtcPopup); resetAspect(hintCharThreshold); resetAspect(hintHideTimeout); resetAspect(hintTriggerKey); @@ -469,6 +498,21 @@ void CodeCompletionSettings::resetSettingsToDefaults() } } +void CodeCompletionSettings::migrateCompletionMode() +{ + auto *qtcSettings = Core::ICore::settings(); + if (!qtcSettings || qtcSettings->contains(Constants::CC_COMPLETION_MODE)) + return; + + const QString oldMode = completionTriggerMode.stringValue(); + if (oldMode.startsWith("Hint")) + completionMode.setStringValue("Manual"); + else + completionMode.setStringValue("Automatic"); + + writeSettings(); +} + QString CodeCompletionSettings::processMessageToFIM(const QString &prefix, const QString &suffix) const { QString result = userMessageTemplateForCC(); diff --git a/settings/CodeCompletionSettings.hpp b/settings/CodeCompletionSettings.hpp index f26588c..c12841d 100644 --- a/settings/CodeCompletionSettings.hpp +++ b/settings/CodeCompletionSettings.hpp @@ -21,6 +21,9 @@ public: Utils::BoolAspect multiLineCompletion{this}; Utils::SelectionAspect modelOutputHandler{this}; Utils::SelectionAspect completionTriggerMode{this}; + Utils::SelectionAspect completionMode{this}; + Utils::BoolAspect smartContextTrigger{this}; + Utils::BoolAspect respectQtcPopup{this}; Utils::IntegerAspect startSuggestionTimer{this}; Utils::IntegerAspect autoCompletionCharThreshold{this}; @@ -78,6 +81,7 @@ public: private: void setupConnections(); void resetSettingsToDefaults(); + void migrateCompletionMode(); }; CodeCompletionSettings &codeCompletionSettings(); diff --git a/settings/SettingsConstants.hpp b/settings/SettingsConstants.hpp index bfae286..c7ad6fb 100644 --- a/settings/SettingsConstants.hpp +++ b/settings/SettingsConstants.hpp @@ -65,6 +65,9 @@ const char СС_START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer"; const char СС_AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThreshold"; const char СС_AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval"; const char CC_COMPLETION_TRIGGER_MODE[] = "QodeAssist.ccCompletionTriggerMode"; +const char CC_COMPLETION_MODE[] = "QodeAssist.ccCompletionMode"; +const char CC_SMART_CONTEXT_TRIGGER[] = "QodeAssist.ccSmartContextTrigger"; +const char CC_RESPECT_QTC_POPUP[] = "QodeAssist.ccRespectQtcPopup"; const char CC_HINT_CHAR_THRESHOLD[] = "QodeAssist.ccHintCharThreshold"; const char CC_HINT_HIDE_TIMEOUT[] = "QodeAssist.ccHintHideTimeout"; const char CC_HINT_TRIGGER_KEY[] = "QodeAssist.ccHintTriggerKey";