From 9f0919c4a3875d8baffc7b6f2b5f7d2e25e198c3 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 27 Apr 2023 11:03:12 +0200 Subject: [PATCH] FakeVim: Block Suggestions Block suggestions when FakeVim is enabled and the mode is not "Insert" or "Replace". Change-Id: I778eb25d9570b76e42652f9d938a8c580033c462 Reviewed-by: David Schulz --- src/plugins/copilot/copilotclient.cpp | 2 +- src/plugins/fakevim/fakevimhandler.cpp | 10 ++-- src/plugins/fakevim/fakevimhandler.h | 2 +- src/plugins/fakevim/fakevimplugin.cpp | 73 +++++++++++++++++++------- src/plugins/texteditor/texteditor.cpp | 18 +++++++ src/plugins/texteditor/texteditor.h | 5 ++ 6 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp index 5ea53b1d7c8..88ce40cd2a8 100644 --- a/src/plugins/copilot/copilotclient.cpp +++ b/src/plugins/copilot/copilotclient.cpp @@ -92,7 +92,7 @@ void CopilotClient::openDocument(TextDocument *document) const int cursorPosition = widget->textCursor().position(); if (cursorPosition < position || cursorPosition > position + charsAdded) return; - scheduleRequest(textEditor->editorWidget()); + scheduleRequest(widget); }); } diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 7403f9e454b..05ea400bbba 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -5197,7 +5197,7 @@ void FakeVimHandler::Private::handleReplaceMode(const Input &input) moveDown(); } else if (input.isKey(Key_Insert)) { g.mode = InsertMode; - q->modeChanged(); + q->modeChanged(isInsertMode()); } else if (input.isControl('o')) { enterCommandMode(ReplaceMode); } else { @@ -5395,7 +5395,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input) removeText(range); } else if (input.isKey(Key_Insert)) { g.mode = ReplaceMode; - q->modeChanged(); + q->modeChanged(isInsertMode()); } else if (input.isKey(Key_Left)) { moveLeft(); } else if (input.isShift(Key_Left) || input.isControl(Key_Left)) { @@ -8578,7 +8578,7 @@ void FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode) clearLastInsertion(); } - q->modeChanged(); + q->modeChanged(isInsertMode()); } void FakeVimHandler::Private::enterVisualInsertMode(QChar command) @@ -8655,7 +8655,7 @@ void FakeVimHandler::Private::enterCommandMode(Mode returnToMode) m_positionPastEnd = false; m_anchorPastEnd = false; - q->modeChanged(); + q->modeChanged(isInsertMode()); } void FakeVimHandler::Private::enterExMode(const QString &contents) @@ -8671,7 +8671,7 @@ void FakeVimHandler::Private::enterExMode(const QString &contents) g.subsubmode = NoSubSubMode; unfocus(); - q->modeChanged(); + q->modeChanged(isInsertMode()); } void FakeVimHandler::Private::recordJump(int position) diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 1d13fe301ad..30ee5599ef8 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -157,7 +157,7 @@ public: Signal completionRequested; Signal tabPreviousRequested; Signal tabNextRequested; - Signal modeChanged; + Signal modeChanged; public: class Private; diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 4d3ec87ed6c..e41d83be2c0 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -539,7 +539,22 @@ signals: void delayedQuitAllRequested(bool forced); public: - QHash m_editorToHandler; + struct HandlerAndData + { +#ifdef Q_OS_WIN + // We need to declare a constructor here, otherwise the MSVC 17.5.x compiler fails to parse + // the "{nullptr}" initializer in the definition below. + // This seems to be a compiler bug, + // see: https://developercommunity.visualstudio.com/t/10351118 + HandlerAndData() + : handler(nullptr) + {} +#endif + FakeVimHandler *handler{nullptr}; + TextEditorWidget::SuggestionBlocker suggestionBlocker; + }; + + QHash m_editorToHandler; void setActionChecked(Id id, bool check); @@ -1253,7 +1268,7 @@ void FakeVimPluginPrivate::initialize() void FakeVimPluginPrivate::userActionTriggered(int key) { IEditor *editor = EditorManager::currentEditor(); - FakeVimHandler *handler = m_editorToHandler[editor]; + FakeVimHandler *handler = m_editorToHandler[editor].handler; if (handler) { // If disabled, enable FakeVim mode just for single user command. bool enableFakeVim = !fakeVimSettings()->useFakeVim.value(); @@ -1569,14 +1584,14 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) // the handler might have triggered the deletion of the editor: // make sure that it can return before being deleted itself new DeferredDeleter(widget, handler); - m_editorToHandler[editor] = handler; + m_editorToHandler[editor].handler = handler; handler->extraInformationChanged.connect([this](const QString &text) { EditorManager::splitSideBySide(); QString title = "stdout.txt"; IEditor *iedit = EditorManager::openEditorWithContents(Id(), &title, text.toUtf8()); EditorManager::activateEditor(iedit); - FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr); + FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler; QTC_ASSERT(handler, return); handler->handleCommand("0"); }); @@ -1591,7 +1606,13 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) tew->setExtraSelections(TextEditorWidget::FakeVimSelection, selection); }); - handler->modeChanged.connect([tew]() { + handler->modeChanged.connect([tew, this, editor](bool insertMode) { + HandlerAndData &handlerAndData = m_editorToHandler[editor]; + + // We don't want to show suggestions unless we are in insert mode. + if (insertMode != (handlerAndData.suggestionBlocker == nullptr)) + handlerAndData.suggestionBlocker = insertMode ? nullptr : tew->blockSuggestions(); + if (tew) tew->clearSuggestion(); }); @@ -1840,7 +1861,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) handler->requestJumpToGlobalMark.connect( [this](QChar mark, bool backTickMode, const QString &fileName) { if (IEditor *iedit = EditorManager::openEditor(FilePath::fromString(fileName))) { - if (FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr)) + if (FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler) handler->jumpToLocalMark(mark, backTickMode); } }); @@ -1887,7 +1908,7 @@ void FakeVimPluginPrivate::editorAboutToClose(IEditor *editor) void FakeVimPluginPrivate::currentEditorAboutToChange(IEditor *editor) { - if (FakeVimHandler *handler = m_editorToHandler.value(editor, 0)) + if (FakeVimHandler *handler = m_editorToHandler.value(editor, {}).handler) handler->enterCommandMode(); } @@ -1905,9 +1926,9 @@ void FakeVimPluginPrivate::documentRenamed( void FakeVimPluginPrivate::renameFileNameInEditors(const FilePath &oldPath, const FilePath &newPath) { - for (FakeVimHandler *handler : m_editorToHandler) { - if (handler->currentFileName() == oldPath.toString()) - handler->setCurrentFileName(newPath.toString()); + for (const HandlerAndData &handlerAndData : m_editorToHandler) { + if (handlerAndData.handler->currentFileName() == oldPath.toString()) + handlerAndData.handler->setCurrentFileName(newPath.toString()); } } @@ -1926,16 +1947,19 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on) //ICore *core = ICore::instance(); //core->updateAdditionalContexts(Context(FAKEVIM_CONTEXT), // Context()); - for (FakeVimHandler *handler : m_editorToHandler) - handler->setupWidget(); + for (const HandlerAndData &handlerAndData : m_editorToHandler) + handlerAndData.handler->setupWidget(); } else { //ICore *core = ICore::instance(); //core->updateAdditionalContexts(Context(), // Context(FAKEVIM_CONTEXT)); resetCommandBuffer(); - for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it) { - if (auto textDocument = qobject_cast(it.key()->document())) - it.value()->restoreWidget(textDocument->tabSettings().m_tabSize); + for (auto it = m_editorToHandler.begin(); it != m_editorToHandler.end(); ++it) { + if (auto textDocument = qobject_cast(it.key()->document())) { + HandlerAndData &handlerAndData = it.value(); + handlerAndData.handler->restoreWidget(textDocument->tabSettings().m_tabSize); + handlerAndData.suggestionBlocker.reset(); + } } } } @@ -1970,11 +1994,22 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle if (editor) editor->setFocus(); + auto editorFromHandler = [this, handler]() -> Core::IEditor * { + auto itEditor = std::find_if(m_editorToHandler.cbegin(), + m_editorToHandler.cend(), + [handler](const HandlerAndData &handlerAndData) { + return handlerAndData.handler == handler; + }); + if (itEditor != m_editorToHandler.cend()) + return itEditor.key(); + return nullptr; + }; + *handled = true; if ((cmd.matches("w", "write") || cmd.cmd == "wq") && cmd.args.isEmpty()) { // :w[rite] bool saved = false; - IEditor *editor = m_editorToHandler.key(handler); + IEditor *editor = editorFromHandler(); const QString fileName = handler->currentFileName(); if (editor && editor->document()->filePath().toString() == fileName) { triggerAction(Core::Constants::SAVE); @@ -1986,7 +2021,7 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle handler->showMessage(MessageInfo, Tr::tr("\"%1\" %2 %3L, %4C written") .arg(fileName).arg(' ').arg(ba.count('\n')).arg(ba.size())); if (cmd.cmd == "wq") - emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler)); + emit delayedQuitRequested(cmd.hasBang, editor); } } } @@ -2005,7 +2040,7 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle emit delayedQuitAllRequested(cmd.hasBang); } else if (cmd.matches("q", "quit")) { // :q[uit] - emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler)); + emit delayedQuitRequested(cmd.hasBang, editorFromHandler()); } else if (cmd.matches("qa", "qall")) { // :qa[ll] emit delayedQuitAllRequested(cmd.hasBang); @@ -2167,7 +2202,7 @@ void FakeVimPlugin::setupTest(QString *title, FakeVimHandler **handler, QWidget IEditor *iedit = EditorManager::openEditorWithContents(Id(), title); EditorManager::activateEditor(iedit); *edit = iedit->widget(); - *handler = dd->m_editorToHandler.value(iedit, 0); + *handler = dd->m_editorToHandler.value(iedit, {}).handler; (*handler)->setupWidget(); (*handler)->handleCommand("set startofline"); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 4dea4ac2a41..eb4b3129f62 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -657,6 +657,7 @@ public: uint m_optionalActionMask = TextEditorActionHandler::None; bool m_contentsChanged = false; bool m_lastCursorChangeWasInteresting = false; + std::shared_ptr m_suggestionBlocker; QSharedPointer m_document; QList m_documentConnections; @@ -905,6 +906,7 @@ void TextEditorWidgetFind::cancelCurrentSelectAll() TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) : q(parent) + , m_suggestionBlocker((void *) this, [](void *) {}) , m_overlay(new TextEditorOverlay(q)) , m_snippetOverlay(new SnippetOverlay(q)) , m_searchResultOverlay(new TextEditorOverlay(q)) @@ -1654,6 +1656,10 @@ void TextEditorWidgetPrivate::handleMoveBlockSelection(QTextCursor::MoveOperatio void TextEditorWidgetPrivate::insertSuggestion(std::unique_ptr &&suggestion) { clearCurrentSuggestion(); + + if (m_suggestionBlocker.use_count() > 1) + return; + auto cursor = q->textCursor(); cursor.setPosition(suggestion->position()); m_suggestionBlock = cursor.block(); @@ -6017,6 +6023,18 @@ bool TextEditorWidget::suggestionVisible() const return currentSuggestion(); } +bool TextEditorWidget::suggestionsBlocked() const +{ + return d->m_suggestionBlocker.use_count() > 1; +} + +TextEditorWidget::SuggestionBlocker TextEditorWidget::blockSuggestions() +{ + if (!suggestionsBlocked()) + clearSuggestion(); + return d->m_suggestionBlocker; +} + #ifdef WITH_TESTS void TextEditorWidget::processTooltipRequest(const QTextCursor &c) { diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 56308d796ed..2f2a9f81054 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -475,6 +475,11 @@ public: void clearSuggestion(); TextSuggestion *currentSuggestion() const; bool suggestionVisible() const; + bool suggestionsBlocked() const; + + using SuggestionBlocker = std::shared_ptr; + // Returns an object that blocks suggestions until it is destroyed. + SuggestionBlocker blockSuggestions(); #ifdef WITH_TESTS void processTooltipRequest(const QTextCursor &c);