diff --git a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc index a35b0f7e19c..fc6838b161e 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc @@ -130,6 +130,9 @@ \li ["x]grr to replace the current line. \l{https://github.com/tommcdo/vim-exchange}{vim-exchange} \endlist + \li \l{https://github.com/vim-scripts/argtextobj.vim}{argtextobj.vim}: + Defines the \c ia and \c aa text objects for function parameters. + \endlist \endlist \section2 Insert Mode diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp index a5bf90cf563..6048e083f7d 100644 --- a/src/plugins/fakevim/fakevim_test.cpp +++ b/src/plugins/fakevim/fakevim_test.cpp @@ -4317,6 +4317,30 @@ void FakeVimPlugin::test_vim_exchange_emulation() KEYS(".", "def" N "abc"); } +void FakeVimPlugin::test_vim_arg_text_obj_emulation() +{ + TestData data; + setup(&data); + data.doCommand("set argtextobj"); + + data.setText("foo(int" X " i, double d, float f)"); + KEYS("dia", "foo(" X ", double d, float f)"); + KEYS("wdia", "foo(, " X ", float f)"); + KEYS("wdia", "foo(, , " X ")"); + + data.setText("foo(int" X " i, double d, float f, long l)"); + KEYS("daa", "foo(" X "double d, float f, long l)"); + KEYS("WWdaa", "foo(double d" X ", long l)"); + KEYS("Wdaa", "foo(double d)"); + KEYS("daa", "foo()"); + + data.setText("foo(std::map map)"); + KEYS("dia", "foo()"); + + data.setText("foo(const C c" X " = C(bar, baz))"); + KEYS("dia", "foo()"); +} + void FakeVimPlugin::test_macros() { TestData data; diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp index 51377387833..62ab12d7099 100644 --- a/src/plugins/fakevim/fakevimactions.cpp +++ b/src/plugins/fakevim/fakevimactions.cpp @@ -119,6 +119,7 @@ FakeVimSettings::FakeVimSettings() createAction(ConfigEmulateVimCommentary, false, "commentary"); createAction(ConfigEmulateReplaceWithRegister, false, "ReplaceWithRegister"); createAction(ConfigEmulateExchange, false, "exchange"); + createAction(ConfigEmulateArgTextObj, false, "argtextobj"); } FakeVimSettings::~FakeVimSettings() diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h index b1de54a60e4..ad78761ae58 100644 --- a/src/plugins/fakevim/fakevimactions.h +++ b/src/plugins/fakevim/fakevimactions.h @@ -113,6 +113,7 @@ enum FakeVimSettingsCode ConfigEmulateVimCommentary, ConfigEmulateReplaceWithRegister, ConfigEmulateExchange, + ConfigEmulateArgTextObj, ConfigBlinkingCursor }; diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 61a98431e1e..fe3fc44f1a5 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -2127,6 +2127,7 @@ public: // return true only if cursor is in a block delimited with correct characters bool selectBlockTextObject(bool inner, QChar left, QChar right); bool selectQuotedStringTextObject(bool inner, const QString "e); + bool selectArgumentTextObject(bool inner); void commitInsertState(); void invalidateInsertState(); @@ -3925,6 +3926,8 @@ bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input) handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}'); else if (input.is('"') || input.is('\'') || input.is('`')) handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar()); + else if (input.is('a') && hasConfig(ConfigEmulateArgTextObj)) + handled = selectArgumentTextObject(g.subsubdata.is('i')); else handled = false; g.subsubmode = NoSubSubMode; @@ -8847,6 +8850,89 @@ bool FakeVimHandler::Private::selectQuotedStringTextObject(bool inner, return true; } +bool FakeVimHandler::Private::selectArgumentTextObject(bool inner) +{ + // We are just interested whether we're currently inside angled brackets, + // but selectBlockTextObject also moves the cursor, so set it back to + // its original position afterwards + QTextCursor prevCursor = m_cursor; + const bool insideTemplateParameter = selectBlockTextObject(true, '<', '>'); + m_cursor = prevCursor; + + int openAngleBracketCount = insideTemplateParameter ? 1 : 0; + + QTextCursor tcStart(m_cursor); + while (true) { + if (tcStart.atStart()) + return true; + + const QChar currentChar = characterAt(tcStart.position()); + + if (openAngleBracketCount == 0 + && (currentChar == '(' || currentChar == ',')) + break; + + if (currentChar == '<') + openAngleBracketCount--; + else if (currentChar == '>') + openAngleBracketCount++; + + tcStart.setPosition(tcStart.position() - 1); + } + + QTextCursor tcEnd(m_cursor); + openAngleBracketCount = insideTemplateParameter ? 1 : 0; + int openParanthesisCount = 0; + + while (true) { + if (tcEnd.atEnd()) { + return true; + } + + const QChar currentChar = characterAt(tcEnd.position()); + if (openAngleBracketCount == 0 + && openParanthesisCount == 0 + && (currentChar == ')' || currentChar == ',')) + break; + + if (currentChar == '<') + openAngleBracketCount++; + else if (currentChar == '>') + openAngleBracketCount--; + else if (currentChar == '(') + openParanthesisCount++; + else if (currentChar == ')') + openParanthesisCount--; + + + tcEnd.setPosition(tcEnd.position() + 1); + } + + + if (!inner && characterAt(tcEnd.position()) == ',' && characterAt(tcStart.position()) == '(') { + tcEnd.setPosition(tcEnd.position() + 1); + if (characterAt(tcEnd.position()) == ' ') + tcEnd.setPosition(tcEnd.position() + 1); + } + + // Never include the opening paranthesis + if (characterAt(tcStart.position()) == '(') { + tcStart.setPosition(tcStart.position() + 1); + } else if (inner) { + tcStart.setPosition(tcStart.position() + 1); + if (characterAt(tcStart.position()) == ' ') + tcStart.setPosition(tcStart.position() + 1); + } + + if (isVisualMode()) + tcEnd.setPosition(tcEnd.position() - 1); + + g.movetype = MoveExclusive; + + setAnchorAndPosition(tcStart.position(), tcEnd.position()); + return true; +} + Mark FakeVimHandler::Private::mark(QChar code) const { if (isVisualMode()) { diff --git a/src/plugins/fakevim/fakevimoptions.ui b/src/plugins/fakevim/fakevimoptions.ui index c7d2d2e688a..7bbdb72e615 100644 --- a/src/plugins/fakevim/fakevimoptions.ui +++ b/src/plugins/fakevim/fakevimoptions.ui @@ -162,6 +162,13 @@ Plugin Emulation + + + + ReplaceWithRegister + + + @@ -172,10 +179,10 @@ - - + + - ReplaceWithRegister + argtextobj.vim diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index a2f34d268bd..4712b6da697 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -432,6 +432,7 @@ QWidget *FakeVimOptionPage::widget() m_group.insert(theFakeVimSetting(ConfigEmulateVimCommentary), m_ui.checkBoxVimCommentary); m_group.insert(theFakeVimSetting(ConfigEmulateReplaceWithRegister), m_ui.checkBoxReplaceWithRegister); m_group.insert(theFakeVimSetting(ConfigEmulateExchange), m_ui.checkBoxExchange); + m_group.insert(theFakeVimSetting(ConfigEmulateArgTextObj), m_ui.checkBoxArgTextObj); connect(m_ui.pushButtonCopyTextEditorSettings, &QAbstractButton::clicked, this, &FakeVimOptionPage::copyTextEditorSettings); diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h index 37950c37d40..84414c20e0b 100644 --- a/src/plugins/fakevim/fakevimplugin.h +++ b/src/plugins/fakevim/fakevimplugin.h @@ -162,6 +162,7 @@ private slots: void test_vim_commentary_file_names(); void test_vim_replace_with_register_emulation(); void test_vim_exchange_emulation(); + void test_vim_arg_text_obj_emulation(); void test_macros();