FakeVim: Accept suggestion with Tab Key

* Changed signals to callbacks as only one receiver was ever added
* Added "tabPressedInInsertMode" callback to allow accepting
  a suggestion with the Tab Key

Fixes: QTCREATORBUG-28830
Change-Id: Ie70ba595b8802b6100fff495164d8e0471b1354c
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-04-27 12:18:07 +02:00
parent 8f345bbc35
commit 015d12ccf3
4 changed files with 103 additions and 89 deletions

View File

@@ -5459,6 +5459,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
movePageUp();
} else if (input.isKey(Key_Tab)) {
if (q->tabPressedInInsertMode()) {
m_buffer->insertState.insertingSpaces = true;
if (s.expandTab.value()) {
const int ts = s.tabStop.value();
@@ -5469,6 +5470,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
insertInInsertMode(input.raw());
}
m_buffer->insertState.insertingSpaces = false;
}
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
const int shift = s.shiftWidth.value();

View File

@@ -61,23 +61,30 @@ enum MessageLevel
MessageShowCmd // partial command
};
template <typename Type>
class Signal
template<typename>
class Callback;
template<typename R, typename... Params>
class Callback<R(Params...)>
{
public:
using Callable = std::function<Type>;
static constexpr auto IsVoidReturnType = std::is_same_v<R, void>;
using Function = std::function<R(Params...)>;
void set(const Function &callable) { m_callable = callable; }
void connect(const Callable &callable) { m_callables.push_back(callable); }
template <typename ...Args>
void operator()(Args ...args) const
R operator()(Params... params)
{
for (const Callable &callable : m_callables)
callable(args...);
if (!m_callable)
return R();
if constexpr (IsVoidReturnType)
m_callable(std::forward<Params>(params)...);
else
return m_callable(std::forward<Params>(params)...);
}
private:
std::vector<Callable> m_callables;
Function m_callable;
};
class FakeVimHandler : public QObject
@@ -131,33 +138,35 @@ public:
bool eventFilter(QObject *ob, QEvent *ev) override;
Signal<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)> commandBufferChanged;
Signal<void(const QString &msg)> statusDataChanged;
Signal<void(const QString &msg)> extraInformationChanged;
Signal<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
Signal<void(const QString &needle)> highlightMatches;
Signal<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
Signal<void(bool *result, QChar c)> checkForElectricCharacter;
Signal<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
Signal<void(const QString &needle, bool forward)> simpleCompletionRequested;
Signal<void(const QString &key, int count)> windowCommandRequested;
Signal<void(bool reverse)> findRequested;
Signal<void(bool reverse)> findNextRequested;
Signal<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
Signal<void()> requestDisableBlockSelection;
Signal<void(const QTextCursor &cursor)> requestSetBlockSelection;
Signal<void(QTextCursor *cursor)> requestBlockSelection;
Signal<void(bool *on)> requestHasBlockSelection;
Signal<void(int depth)> foldToggle;
Signal<void(bool fold)> foldAll;
Signal<void(int depth, bool dofold)> fold;
Signal<void(int count, bool current)> foldGoTo;
Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
Signal<void()> completionRequested;
Signal<void()> tabPreviousRequested;
Signal<void()> tabNextRequested;
Signal<void(bool insertMode)> modeChanged;
Callback<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)>
commandBufferChanged;
Callback<void(const QString &msg)> statusDataChanged;
Callback<void(const QString &msg)> extraInformationChanged;
Callback<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
Callback<void(const QString &needle)> highlightMatches;
Callback<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
Callback<void(bool *result, QChar c)> checkForElectricCharacter;
Callback<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
Callback<void(const QString &needle, bool forward)> simpleCompletionRequested;
Callback<void(const QString &key, int count)> windowCommandRequested;
Callback<void(bool reverse)> findRequested;
Callback<void(bool reverse)> findNextRequested;
Callback<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
Callback<void()> requestDisableBlockSelection;
Callback<void(const QTextCursor &cursor)> requestSetBlockSelection;
Callback<void(QTextCursor *cursor)> requestBlockSelection;
Callback<void(bool *on)> requestHasBlockSelection;
Callback<void(int depth)> foldToggle;
Callback<void(bool fold)> foldAll;
Callback<void(int depth, bool dofold)> fold;
Callback<void(int count, bool current)> foldGoTo;
Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
Callback<void()> completionRequested;
Callback<void()> tabPreviousRequested;
Callback<void()> tabNextRequested;
Callback<void(bool insertMode)> modeChanged;
Callback<bool()> tabPressedInInsertMode;
public:
class Private;

View File

@@ -1586,7 +1586,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
new DeferredDeleter(widget, handler);
m_editorToHandler[editor].handler = handler;
handler->extraInformationChanged.connect([this](const QString &text) {
handler->extraInformationChanged.set([this](const QString &text) {
EditorManager::splitSideBySide();
QString title = "stdout.txt";
IEditor *iedit = EditorManager::openEditorWithContents(Id(), &title, text.toUtf8());
@@ -1596,17 +1596,27 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
handler->handleCommand("0");
});
handler->commandBufferChanged
.connect([this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
handler->commandBufferChanged.set(
[this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
showCommandBuffer(handler, contents, cursorPos, anchorPos, messageLevel);
});
handler->selectionChanged.connect([tew](const QList<QTextEdit::ExtraSelection> &selection) {
handler->selectionChanged.set([tew](const QList<QTextEdit::ExtraSelection> &selection) {
if (tew)
tew->setExtraSelections(TextEditorWidget::FakeVimSelection, selection);
});
handler->modeChanged.connect([tew, this, editor](bool insertMode) {
handler->tabPressedInInsertMode.set([tew]() {
auto suggestion = tew->currentSuggestion();
if (suggestion) {
suggestion->apply();
return false;
}
return true;
});
handler->modeChanged.set([tew, this, editor](bool insertMode) {
HandlerAndData &handlerAndData = m_editorToHandler[editor];
// We don't want to show suggestions unless we are in insert mode.
@@ -1617,7 +1627,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
tew->clearSuggestion();
});
handler->highlightMatches.connect([](const QString &needle) {
handler->highlightMatches.set([](const QString &needle) {
for (IEditor *editor : EditorManager::visibleEditors()) {
QWidget *w = editor->widget();
if (auto find = Aggregation::query<IFindSupport>(w))
@@ -1625,7 +1635,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->moveToMatchingParenthesis.connect([](bool *moved, bool *forward, QTextCursor *cursor) {
handler->moveToMatchingParenthesis.set([](bool *moved, bool *forward, QTextCursor *cursor) {
*moved = false;
bool undoFakeEOL = false;
@@ -1658,7 +1668,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->indentRegion.connect([tew](int beginBlock, int endBlock, QChar typedChar) {
handler->indentRegion.set([tew](int beginBlock, int endBlock, QChar typedChar) {
if (!tew)
return;
@@ -1691,17 +1701,17 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->checkForElectricCharacter.connect([tew](bool *result, QChar c) {
handler->checkForElectricCharacter.set([tew](bool *result, QChar c) {
if (tew)
*result = tew->textDocument()->indenter()->isElectricCharacter(c);
});
handler->requestDisableBlockSelection.connect([tew] {
handler->requestDisableBlockSelection.set([tew] {
if (tew)
tew->setTextCursor(tew->textCursor());
});
handler->requestSetBlockSelection.connect([tew](const QTextCursor &cursor) {
handler->requestSetBlockSelection.set([tew](const QTextCursor &cursor) {
if (tew) {
const TabSettings &tabs = tew->textDocument()->tabSettings();
MultiTextCursor mtc;
@@ -1725,7 +1735,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->requestBlockSelection.connect([tew](QTextCursor *cursor) {
handler->requestBlockSelection.set([tew](QTextCursor *cursor) {
if (tew && cursor) {
MultiTextCursor mtc = tew->multiTextCursor();
*cursor = mtc.cursors().first();
@@ -1733,16 +1743,16 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->requestHasBlockSelection.connect([tew](bool *on) {
handler->requestHasBlockSelection.set([tew](bool *on) {
if (tew && on)
*on = tew->multiTextCursor().hasMultipleCursors();
});
handler->simpleCompletionRequested.connect([this, handler](const QString &needle, bool forward) {
handler->simpleCompletionRequested.set([this, handler](const QString &needle, bool forward) {
runData->wordProvider.setActive(needle, forward, handler);
});
handler->windowCommandRequested.connect([this, handler](const QString &map, int count) {
handler->windowCommandRequested.set([this, handler](const QString &map, int count) {
// normalize mapping
const QString key = map.toUpper();
@@ -1772,22 +1782,22 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
qDebug() << "UNKNOWN WINDOW COMMAND: <C-W>" << map;
});
handler->findRequested.connect([](bool reverse) {
handler->findRequested.set([](bool reverse) {
Find::setUseFakeVim(true);
Find::openFindToolBar(reverse ? Find::FindBackwardDirection
: Find::FindForwardDirection);
});
handler->findNextRequested.connect([](bool reverse) {
handler->findNextRequested.set([](bool reverse) {
triggerAction(reverse ? Core::Constants::FIND_PREVIOUS : Core::Constants::FIND_NEXT);
});
handler->foldToggle.connect([this, handler](int depth) {
handler->foldToggle.set([this, handler](int depth) {
QTextBlock block = handler->textCursor().block();
fold(handler, depth, !TextDocumentLayout::isFolded(block));
});
handler->foldAll.connect([handler](bool fold) {
handler->foldAll.set([handler](bool fold) {
QTextDocument *document = handler->textCursor().document();
auto documentLayout = qobject_cast<TextDocumentLayout*>(document->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -1802,11 +1812,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
documentLayout->emitDocumentSizeChanged();
});
handler->fold.connect([this, handler](int depth, bool dofold) {
fold(handler, depth, dofold);
});
handler->fold.set([this, handler](int depth, bool dofold) { fold(handler, depth, dofold); });
handler->foldGoTo.connect([handler](int count, bool current) {
handler->foldGoTo.set([handler](int count, bool current) {
QTextCursor tc = handler->textCursor();
QTextBlock block = tc.block();
@@ -1858,7 +1866,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->requestJumpToGlobalMark.connect(
handler->requestJumpToGlobalMark.set(
[this](QChar mark, bool backTickMode, const QString &fileName) {
if (IEditor *iedit = EditorManager::openEditor(FilePath::fromString(fileName))) {
if (FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler)
@@ -1866,19 +1874,15 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
handler->handleExCommandRequested.connect([this, handler](bool *handled, const ExCommand &cmd) {
handler->handleExCommandRequested.set([this, handler](bool *handled, const ExCommand &cmd) {
handleExCommand(handler, handled, cmd);
});
handler->tabNextRequested.connect([] {
triggerAction(Core::Constants::GOTONEXTINHISTORY);
});
handler->tabNextRequested.set([] { triggerAction(Core::Constants::GOTONEXTINHISTORY); });
handler->tabPreviousRequested.connect([] {
triggerAction(Core::Constants::GOTOPREVINHISTORY);
});
handler->tabPreviousRequested.set([] { triggerAction(Core::Constants::GOTOPREVINHISTORY); });
handler->completionRequested.connect([this, tew] {
handler->completionRequested.set([this, tew] {
if (tew)
tew->invokeAssist(Completion, &runData->wordProvider);
});

View File

@@ -187,12 +187,12 @@ int main(int argc, char *argv[])
// Create FakeVimHandler instance which will emulate Vim behavior in editor widget.
FakeVimHandler handler(editor, nullptr);
handler.commandBufferChanged.connect([&](const QString &msg, int cursorPos, int, int) {
handler.commandBufferChanged.set([&](const QString &msg, int cursorPos, int, int) {
statusData.setStatusMessage(msg, cursorPos);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
handler.selectionChanged.connect([&handler](const QList<QTextEdit::ExtraSelection> &s) {
handler.selectionChanged.set([&handler](const QList<QTextEdit::ExtraSelection> &s) {
QWidget *widget = handler.widget();
if (auto ed = qobject_cast<QPlainTextEdit *>(widget))
ed->setExtraSelections(s);
@@ -200,21 +200,20 @@ int main(int argc, char *argv[])
ed->setExtraSelections(s);
});
handler.extraInformationChanged.connect([&](const QString &info) {
handler.extraInformationChanged.set([&](const QString &info) {
statusData.setStatusInfo(info);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
handler.statusDataChanged.connect([&](const QString &info) {
handler.statusDataChanged.set([&](const QString &info) {
statusData.setStatusInfo(info);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
handler.highlightMatches.connect([&](const QString &needle) {
highlightMatches(handler.widget(), needle);
});
handler.highlightMatches.set(
[&](const QString &needle) { highlightMatches(handler.widget(), needle); });
handler.handleExCommandRequested.connect([](bool *handled, const ExCommand &cmd) {
handler.handleExCommandRequested.set([](bool *handled, const ExCommand &cmd) {
if (cmd.matches("q", "quit") || cmd.matches("qa", "qall")) {
QApplication::quit();
*handled = true;