diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp index 92c3fe80987d0454184dfac04753a1d3f6764763..1bb564abe42373ab0fa4d506ff68b9687439fb75 100644 --- a/src/plugins/fakevim/fakevim_test.cpp +++ b/src/plugins/fakevim/fakevim_test.cpp @@ -122,14 +122,12 @@ struct TestData void setText(const QString &text) { - QTextCursor tc = cursor(); QString str = text; int i = str.indexOf(cursorString); if (!cursorString.isEmpty() && i != -1) str.remove(i, 1); edit->document()->setPlainText(str); - tc.setPosition(qMax(0, i)); - edit->setTextCursor(tc); + handler->setTextCursorPosition(i); } void doCommand(const QString &cmd) { handler->handleCommand(cmd); } @@ -404,19 +402,50 @@ void FakeVimPlugin::test_vim_block_selection() void FakeVimPlugin::test_vim_repeat() { - NOT_IMPLEMENTED - TestData data; setup(&data); - data.setText("test text"); - KEYS("ciwWORD", "WOR" X "D text"); + // delete line + data.setText("abc" N "def" N "ghi"); + KEYS("dd", X "def" N "ghi"); + KEYS(".", X "ghi"); + + // delete to next word + data.setText("abc def ghi jkl"); + KEYS("dw", X "def ghi jkl"); + KEYS("w.", "def " X "jkl"); + KEYS("gg.", X "jkl"); + + // change in word + data.setText("WORD text"); + KEYS("ciwWORD<esc>", "WOR" X "D text"); KEYS("w.", "WORD WOR" X "D"); /* QTCREATORBUG-7248 */ data.setText("test tex" X "t"); - KEYS("vbcWORD", "test " "WOR" X "D"); - KEYS("bb.", X "WORD WORD"); + KEYS("vbcWORD<esc>", "test " "WOR" X "D"); + KEYS("bb.", "WOR" X "D WORD"); + + // delete selected range + data.setText("abc def ghi jkl"); + KEYS("viwd", X " def ghi jkl"); + KEYS(".", X "f ghi jkl"); + KEYS(".", X "hi jkl"); + + // delete two lines + data.setText("abc" N "def" N "ghi" N "jkl" N "mno"); + KEYS("Vjx", X "ghi" N "jkl" N "mno"); + KEYS(".", X "mno"); + + // delete three lines + data.setText("abc" N "def" N "ghi" N "jkl" N "mno" N "pqr" N "stu"); + KEYS("d2j", X "jkl" N "mno" N "pqr" N "stu"); + KEYS(".", X "stu"); + + // replace block selection + data.setText("abcd" N "d" X "efg" N "ghij" N "jklm"); + KEYS("<c-v>jlrX", "abcd" N "d" X "XXg" N "gXXj" N "jklm"); + KEYS("gg.", "XXcd" N "XXXg" N "gXXj" N "jklm"); } void FakeVimPlugin::test_vim_search() diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index f3b0b3cc690e0341263f25fe6b137ef9ffc71dba..852a07e7e4f9091bcdaad4e2244ee8814a88c7e3 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -1157,8 +1157,8 @@ public: EventResult handleCloseSquareSubMode(const Input &); EventResult handleSearchSubSubMode(const Input &); EventResult handleCommandSubSubMode(const Input &); - void finishMovement(const QString &dotCommand = QString()); - void finishMovement(const QString &dotCommand, int count); + void finishMovement(const QString &dotCommandMovement = QString()); + void finishMovement(const QString &dotCommandMovement, int count); void resetCommandMode(); void search(const SearchData &sd, bool showMessages = true); void searchNext(bool forward = true); @@ -1438,6 +1438,7 @@ public: void replay(const QString &text, int count); void setDotCommand(const QString &cmd) { g.dotCommand = cmd; } void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); } + QString visualDotCommand() const; // extra data for ';' QString m_semicolonCount; @@ -2156,12 +2157,12 @@ void FakeVimHandler::Private::moveToStartOfLine() #endif } -void FakeVimHandler::Private::finishMovement(const QString &dotCommand, int count) +void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement, int count) { - finishMovement(dotCommand.arg(count)); + finishMovement(dotCommandMovement.arg(count)); } -void FakeVimHandler::Private::finishMovement(const QString &dotCommand) +void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement) { //dump("FINISH MOVEMENT"); if (m_submode == FilterSubMode) { @@ -2250,8 +2251,8 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) if (m_rangemode == RangeLineMode) m_rangemode = RangeLineModeExclusive; removeText(currentRange()); - if (!dotCommand.isEmpty()) - setDotCommand(QLatin1Char('c') + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand(QLatin1Char('c') + dotCommandMovement); if (m_movetype == MoveLineWise) insertAutomaticIndentation(true); endEditBlock(); @@ -2261,8 +2262,8 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) setUndoPosition(); Range range = currentRange(); removeText(range); - if (!dotCommand.isEmpty()) - setDotCommand(QLatin1Char('d') + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand(QLatin1Char('d') + dotCommandMovement); if (m_movetype == MoveLineWise) handleStartOfLine(); m_submode = NoSubMode; @@ -2287,16 +2288,16 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) } else if (m_submode == TransformSubMode) { if (m_subsubmode == InvertCaseSubSubMode) { invertCase(currentRange()); - if (!dotCommand.isEmpty()) - setDotCommand(QLatin1Char('~') + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand(QLatin1Char('~') + dotCommandMovement); } else if (m_subsubmode == UpCaseSubSubMode) { upCase(currentRange()); - if (!dotCommand.isEmpty()) - setDotCommand("gU" + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand("gU" + dotCommandMovement); } else if (m_subsubmode == DownCaseSubSubMode) { downCase(currentRange()); - if (!dotCommand.isEmpty()) - setDotCommand("gu" + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand("gu" + dotCommandMovement); } m_submode = NoSubMode; m_subsubmode = NoSubSubMode; @@ -2309,22 +2310,22 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) setUndoPosition(); indentSelectedText(); m_submode = NoSubMode; - if (!dotCommand.isEmpty()) - setDotCommand('=' + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand('=' + dotCommandMovement); } else if (m_submode == ShiftRightSubMode) { recordJump(); setUndoPosition(); shiftRegionRight(1); m_submode = NoSubMode; - if (!dotCommand.isEmpty()) - setDotCommand('>' + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand('>' + dotCommandMovement); } else if (m_submode == ShiftLeftSubMode) { recordJump(); setUndoPosition(); shiftRegionLeft(1); m_submode = NoSubMode; - if (!dotCommand.isEmpty()) - setDotCommand('<' + dotCommand); + if (!dotCommandMovement.isEmpty()) + setDotCommand('<' + dotCommandMovement); } resetCommandMode(); @@ -2589,6 +2590,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input) m_submode = NoSubMode; m_rangemode = RangeLineMode; } else if (m_submode == ReplaceSubMode) { + setDotCommand(visualDotCommand() + 'r' + input.asChar()); if (isVisualMode()) { setUndoPosition(); if (isVisualLineMode()) @@ -2837,7 +2839,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) // << input; QString savedCommand = g.dotCommand; g.dotCommand.clear(); - replay(savedCommand, count()); + replay(savedCommand, 1); enterCommandMode(); g.dotCommand = savedCommand; } else if (input.is('<')) { @@ -2872,6 +2874,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) setUndoPosition(); breakEditBlock(); enterInsertMode(); + setDotCommand(QString(QLatin1Char('a'))); m_lastInsertion.clear(); if (!atEndOfLine()) moveRight(); @@ -2887,6 +2890,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) updateMiniBuffer(); } else if (input.isControl('a')) { changeNumberTextObject(true); + setDotCommand("%1<c-a>", count()); } else if (input.is('b') || input.isShift(Key_Left)) { m_movetype = MoveExclusive; moveToNextWordStart(count(), false, false); @@ -2905,6 +2909,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) m_submode = ChangeSubMode; } else if ((input.is('c') || input.is('C') || input.is('s') || input.is('R')) && (isVisualCharMode() || isVisualLineMode())) { + setDotCommand(visualDotCommand() + input.asChar()); if ((input.is('c')|| input.is('s')) && isVisualCharMode()) { leaveVisualMode(); m_rangemode = RangeCharMode; @@ -2942,6 +2947,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) } else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete)) && isVisualMode()) { setUndoPosition(); + setDotCommand(visualDotCommand() + 'x'); if (isVisualCharMode()) { leaveVisualMode(); m_submode = DeleteSubMode; @@ -2972,6 +2978,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) finishMovement(); } else if ((input.is('D') || input.is('X')) && (isVisualCharMode() || isVisualLineMode())) { + setDotCommand(visualDotCommand() + 'X'); leaveVisualMode(); m_rangemode = RangeLineMode; m_submode = NoSubMode; @@ -2979,6 +2986,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input) removeText(currentRange()); moveToFirstNonBlankOnLine(); } else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) { + setDotCommand(visualDotCommand() + 'X'); leaveVisualMode(); m_rangemode = RangeBlockAndTailMode; yankText(currentRange(), m_register); @@ -3102,7 +3110,6 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input) moveDown(count()); finishMovement("%1j", count()); } else if (input.is('J')) { - setDotCommand("%1J", count()); beginEditBlock(); if (m_submode == NoSubMode) { for (int i = qMax(count(), 2) - 1; --i >= 0; ) { @@ -3123,7 +3130,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input) moveLeft(); } endEditBlock(); - finishMovement(); + finishMovement("%1J"); } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) { m_movetype = MoveLineWise; moveUp(count()); @@ -3142,6 +3149,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input) moveUp(qMax(count(), 1)); handleStartOfLine(); finishMovement(); + finishMovement("%1L"); } else if (input.isControl('l')) { // screen redraw. should not be needed } else if (input.is('m')) { @@ -3335,6 +3343,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input) finishMovement(); } else if (input.isControl('x')) { changeNumberTextObject(false); + setDotCommand("%1<c-a>", count()); } else if (input.is('X')) { if (leftDist() > 0) { setAnchor(); @@ -3447,12 +3456,12 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input) moveDown(count() * (linesOnScreen() - 2) - cursorLineOnScreen()); scrollToLine(cursorLine()); handleStartOfLine(); - finishMovement(); + finishMovement("%1f", count()); } else if (input.isKey(Key_PageUp) || input.isControl('b')) { moveUp(count() * (linesOnScreen() - 2) + cursorLineOnScreen()); scrollToLine(cursorLine() + linesOnScreen() - 2); handleStartOfLine(); - finishMovement(); + finishMovement("%1b", count()); } else if (input.isKey(Key_Delete)) { setAnchor(); moveRight(qMin(1, rightDist())); @@ -5923,14 +5932,35 @@ void FakeVimHandler::Private::handleStartOfLine() void FakeVimHandler::Private::replay(const QString &command, int n) { //qDebug() << "REPLAY: " << quoteUnprintable(command); + Inputs inputs(command); for (int i = n; --i >= 0; ) { - foreach (QChar c, command) { - //qDebug() << " REPLAY: " << c.unicode(); - handleDefaultKey(Input(c)); + foreach (Input in, inputs) { + handleDefaultKey(in); } } } +QString FakeVimHandler::Private::visualDotCommand() const +{ + QTextCursor start(cursor()); + QTextCursor end(start); + end.setPosition(end.anchor()); + + if (isVisualCharMode()) + return QString("v%1l").arg(qAbs(start.position() - end.position())); + + if (isVisualLineMode()) + return QString("V%1j").arg(qAbs(start.blockNumber() - end.blockNumber())); + + if (isVisualBlockMode()) { + return QString("<c-v>%1l%2j") + .arg(qAbs(start.positionInBlock() - end.positionInBlock())) + .arg(qAbs(start.blockNumber() - end.blockNumber())); + } + + return QString(); +} + void FakeVimHandler::Private::selectTextObject(bool simple, bool inner) { bool setupAnchor = (position() == anchor()); @@ -6401,6 +6431,16 @@ void FakeVimHandler::miniBufferTextEdited(const QString &text, int cursorPos) d->miniBufferTextEdited(text, cursorPos); } +void FakeVimHandler::setTextCursorPosition(int position) +{ + int pos = qMax(0, qMin(position, d->lastPositionInDocument())); + if (d->isVisualMode()) + d->setPosition(pos); + else + d->setAnchorAndPosition(pos, pos); + d->setTargetColumn(); +} + } // namespace Internal } // namespace FakeVim diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 7c72e755b981dc302c267301df6f53893989bf51..940e6acd17ee82b10d8c0b5a88cd915ea02843c3 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -131,6 +131,9 @@ public slots: void miniBufferTextEdited(const QString &text, int cursorPos); + // Set text cursor position. Keeps anchor if in visual mode. + void setTextCursorPosition(int position); + signals: void commandBufferChanged(const QString &msg, int pos, int messageLevel, QObject *eventFilter); void statusDataChanged(const QString &msg);