From 8994bb2d4af2c38b55eee16fadb4801243fe95e3 Mon Sep 17 00:00:00 2001 From: Lukas Holecek <hluk@email.cz> Date: Tue, 23 Oct 2012 16:35:13 +0200 Subject: [PATCH] fakevim: Added move and yank Ex commands Change-Id: I4138a2035e338b665d91704b7c53117c4d924472 Reviewed-by: hjk <qthjk@ovi.com> --- src/plugins/fakevim/fakevim_test.cpp | 43 ++++++ src/plugins/fakevim/fakevimhandler.cpp | 179 ++++++++++++++++++------- src/plugins/fakevim/fakevimplugin.h | 2 + 3 files changed, 174 insertions(+), 50 deletions(-) diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp index 176448486ac..3c9455683fd 100644 --- a/src/plugins/fakevim/fakevim_test.cpp +++ b/src/plugins/fakevim/fakevim_test.cpp @@ -1321,6 +1321,49 @@ void FakeVimPlugin::test_vim_substitute() COMMAND("%s/\\( \\S \\)*//g", "abc" N "def" N X "ghi"); } +void FakeVimPlugin::test_vim_yank() +{ + TestData data; + setup(&data); + + data.setText("abc" N "def"); + COMMAND("y x", X "abc" N "def"); + KEYS("\"xp", "abc" N X "abc" N "def"); + COMMAND("u", X "abc" N "def"); + COMMAND("redo", X "abc" N "abc" N "def"); + + KEYS("uw", "abc" N X "def"); + COMMAND("1y y", "abc" N X "def"); + KEYS("\"yP", "abc" N X "abc" N "def"); + COMMAND("u", "abc" N X "def"); + + COMMAND("-1,$y x", "abc" N X "def"); + KEYS("\"xP", "abc" N X "abc" N "def" N "def"); + COMMAND("u", "abc" N X "def"); + + COMMAND("$-1y", "abc" N X "def"); + KEYS("P", "abc" N X "abc" N "def"); + COMMAND("u", "abc" N X "def"); +} + +void FakeVimPlugin::test_vim_move() +{ + TestData data; + setup(&data); + + data.setText("abc" N "def" N "ghi" N "jkl"); + COMMAND("m +1", "def" N X "abc" N "ghi" N "jkl"); + COMMAND("u", X "abc" N "def" N "ghi" N "jkl"); + COMMAND("redo", X "def" N "abc" N "ghi" N "jkl"); + COMMAND("m -2", X "def" N "abc" N "ghi" N "jkl"); + COMMAND("2m0", X "abc" N "def" N "ghi" N "jkl"); + + COMMAND("m $-2", "def" N X "abc" N "ghi" N "jkl"); + KEYS("`'", X "def" N "abc" N "ghi" N "jkl"); + KEYS("Vj:m+2<cr>", "ghi" N "def" N X "abc" N "jkl"); + KEYS("u", X "def" N "abc" N "ghi" N "jkl"); +} + void FakeVimPlugin::test_advanced_commands() { TestData data; diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index c6e35978f2c..3ff27b25b2b 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -708,14 +708,14 @@ public: bool operator==(const Input &a) const { - return m_key == a.m_key && m_modifiers == a.m_modifiers && m_text == a.m_text; + return m_key == a.m_key && m_modifiers == a.m_modifiers; } bool operator!=(const Input &a) const { return !operator==(a); } bool operator<(const Input &a) const { - return m_key < a.m_key || m_modifiers < a.m_modifiers || m_text < a.m_text; + return m_key < a.m_key || m_modifiers < a.m_modifiers; } QString text() const { return m_text; } @@ -1284,7 +1284,7 @@ public: // The following use all zero-based counting. int cursorLineOnScreen() const; int cursorLine() const; - int cursorBlockNumber() const; + int cursorBlockNumber() const; // "." address int physicalCursorColumn() const; // as stored in the data int logicalCursorColumn() const; // as visible on screen int physicalToLogicalColumn(int physical, const QString &text) const; @@ -1583,6 +1583,8 @@ public: bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin? bool handleExBangCommand(const ExCommand &cmd); bool handleExDeleteCommand(const ExCommand &cmd); + bool handleExYankCommand(const ExCommand &cmd); + bool handleExMoveCommand(const ExCommand &cmd); bool handleExGotoCommand(const ExCommand &cmd); bool handleExHistoryCommand(const ExCommand &cmd); bool handleExRegisterCommand(const ExCommand &cmd); @@ -4019,22 +4021,17 @@ int FakeVimHandler::Private::readLineCode(QString &cmd) //qDebug() << "CMD: " << cmd; if (cmd.isEmpty()) return -1; - QChar c = cmd.at(0); - cmd = cmd.mid(1); - if (c == '.') { - if (cmd.isEmpty()) - return cursorBlockNumber(); - QChar c1 = cmd.at(0); - if (c1 == '+' || c1 == '-') { - // Repeat for things like .+4 - cmd = cmd.mid(1); - return cursorBlockNumber() + readLineCode(cmd); - } - return cursorBlockNumber(); - } - if (c == '$') - return document()->blockCount() - 1; - if (c == '\'' && !cmd.isEmpty()) { + + int result = -1; + const QChar &c = cmd[0]; + if (c == '.') { // current line + result = cursorBlockNumber(); + cmd.remove(0, 1); + } else if (c == '$') { // last line + result = document()->blockCount() - 1; + cmd.remove(0, 1); + } else if (c == '\'') { // mark + cmd.remove(0, 1); if (cmd.isEmpty()) { showMessage(MessageError, msgMarkNotSet(QString())); return -1; @@ -4042,35 +4039,42 @@ int FakeVimHandler::Private::readLineCode(QString &cmd) CursorPosition m = mark(cmd.at(0).unicode()); if (!m.isValid()) { showMessage(MessageError, msgMarkNotSet(cmd.at(0))); - cmd = cmd.mid(1); return -1; } - cmd = cmd.mid(1); - return m.line; - } - if (c == '-') { - int n = readLineCode(cmd); - return cursorBlockNumber() - (n == -1 ? 1 : n); - } - if (c == '+') { - int n = readLineCode(cmd); - return cursorBlockNumber() + (n == -1 ? 1 : n); - } - if (c.isDigit()) { - int n = c.unicode() - '0'; - while (!cmd.isEmpty()) { - c = cmd.at(0); - if (!c.isDigit()) - break; - cmd = cmd.mid(1); - n = n * 10 + (c.unicode() - '0'); + cmd.remove(0, 1); + result = m.line; + } else if (c.isDigit()) { // line with given number + result = 0; + } else if (c == '-' || c == '+') { // add or subtract from current line number + result = cursorBlockNumber(); + } else { + return -1; + } + // FIXME: /.../ ?...? \/ \? \& + + // basic arithmetic ("-3+5" or "++" means "+2" etc.) + int n = 0; + bool add = true; + int i = 0; + for (; i < cmd.size(); ++i) { + const QChar &c = cmd[i]; + if (c == '-' || c == '+') { + if (n != 0) + result = result + (add ? n - 1 : -(n - 1)); + add = c == '+'; + result = result + (add ? 1 : -1); + n = 0; + } else if (c.isDigit()) { + n = n * 10 + c.digitValue(); + } else if (!c.isSpace()) { + break; } - //qDebug() << "N: " << n; - return n - 1; } - // Parsing failed. - cmd = c + cmd; - return -1; + if (n != 0) + result = result + (add ? n - 1 : -(n - 1)); + cmd.remove(0, i); + + return result; } void FakeVimHandler::Private::setCurrentRange(const Range &range) @@ -4085,6 +4089,7 @@ Range FakeVimHandler::Private::rangeFromCurrentLine() const QTextBlock block = cursor().block(); range.beginPos = block.position(); range.endPos = range.beginPos + block.length() - 1; + range.rangemode = RangeLineMode; return range; } @@ -4410,7 +4415,7 @@ bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd) if (!cmd.matches("d", "delete")) return false; - Range range = cmd.range.endPos == 0 ? rangeFromCurrentLine() : cmd.range; + Range range = cmd.range.endPos <= 0 ? rangeFromCurrentLine() : cmd.range; setCurrentRange(range); QString reg = cmd.args; QString text = selectText(range); @@ -4422,6 +4427,69 @@ bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd) return true; } +bool FakeVimHandler::Private::handleExYankCommand(const ExCommand &cmd) +{ + // :[range]y[ank] [x] + if (!cmd.matches("y", "yank")) + return false; + + Range range = cmd.range.endPos <= 0 ? rangeFromCurrentLine() : cmd.range; + const int r = cmd.args.isEmpty() ? m_register : cmd.args.at(0).unicode(); + yankText(range, r); + + return true; +} + +bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd) +{ + // :[range]m[ove] {address} + QRegExp re("^m(?=ove)?([\\d+-$%./'\\\\?].*|$)"); // address can follow immediately + if (re.indexIn(cmd.cmd) == -1) + return false; + + QString lineCode = re.cap(1) + cmd.args; + + Range range = cmd.range.endPos <= 0 ? rangeFromCurrentLine() : cmd.range; + const int startLine = document()->findBlock(range.beginPos).blockNumber(); + const int endLine = document()->findBlock(range.endPos).blockNumber(); + const int lines = endLine - startLine + 1; + + const int line = lineCode == "0" ? -1 : readLineCode(lineCode); + if (line >= startLine && line < endLine) { + showMessage(MessageError, FakeVimHandler::tr("Move lines into themselves")); + return true; + } + + setUndoPosition(); + recordJump(); + + setCurrentRange(range); + QString text = selectText(range); + removeText(currentRange()); + + bool insertAtEnd = line == document()->blockCount(); + const int targetLine = (line < startLine) ? line : line - lines; + QTextBlock block = document()->findBlockByNumber(insertAtEnd ? targetLine : targetLine + 1); + int pos = block.position(); + setAnchorAndPosition(pos, pos); + if (insertAtEnd) { + moveToEndOfLine(); + insertText(QString('\n')); + } + insertText(text); + if (insertAtEnd) + cursor().deleteChar(); + + moveUp(1); + if (hasConfig(ConfigStartOfLine)) + moveToFirstNonBlankOnLine(); + + if (lines > 2) + showMessage(MessageInfo, FakeVimHandler::tr("%1 lines moved").arg(lines)); + + return true; +} + bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd) { // :w, :x, :wq, ... @@ -4703,7 +4771,7 @@ bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd) if (line.startsWith(QLatin1Char('%'))) line.replace(0, 1, "1,$"); - const int beginLine = readLineCode(line); + int beginLine = readLineCode(line); int endLine = -1; if (line.startsWith(',')) { line = line.mid(1); @@ -4724,6 +4792,8 @@ bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd) || handleExHistoryCommand(cmd) || handleExRegisterCommand(cmd) || handleExDeleteCommand(cmd) + || handleExYankCommand(cmd) + || handleExMoveCommand(cmd) || handleExMapCommand(cmd) || handleExNohlsearchCommand(cmd) || handleExNormalCommand(cmd) @@ -5236,7 +5306,7 @@ int FakeVimHandler::Private::cursorLine() const int FakeVimHandler::Private::cursorBlockNumber() const { - return cursor().block().blockNumber(); + return document()->findBlock(qMin(anchor(), position())).blockNumber(); } int FakeVimHandler::Private::physicalCursorColumn() const @@ -5423,6 +5493,11 @@ QString FakeVimHandler::Private::selectText(const Range &range) const void FakeVimHandler::Private::yankText(const Range &range, int reg) { setRegister(reg, selectText(range), range.rangemode); + + const int lines = document()->findBlock(range.endPos).blockNumber() + - document()->findBlock(range.beginPos).blockNumber() + 1; + if (lines > 2) + showMessage(MessageInfo, FakeVimHandler::tr("%1 lines yanked").arg(lines)); } void FakeVimHandler::Private::transformText(const Range &range, @@ -6381,13 +6456,17 @@ void FakeVimHandler::Private::setRegister(int reg, const QString &contents, Rang bool copyToSelection; getRegisterType(reg, ©ToClipboard, ©ToSelection); + QString contents2 = contents; + if (mode == RangeLineMode && !contents2.endsWith('\n')) + contents2.append('\n'); + if (copyToClipboard || copyToSelection) { if (copyToClipboard) - setClipboardData(contents, mode, QClipboard::Clipboard); + setClipboardData(contents2, mode, QClipboard::Clipboard); if (copyToSelection) - setClipboardData(contents, mode, QClipboard::Selection); + setClipboardData(contents2, mode, QClipboard::Selection); } else { - g.registers[reg].contents = contents; + g.registers[reg].contents = contents2; g.registers[reg].rangemode = mode; } } diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h index a664c352be5..4525fe4faec 100644 --- a/src/plugins/fakevim/fakevimplugin.h +++ b/src/plugins/fakevim/fakevimplugin.h @@ -81,6 +81,8 @@ private slots: void test_vim_code_autoindent(); void test_vim_code_folding(); void test_vim_substitute(); + void test_vim_yank(); + void test_vim_move(); void test_advanced_commands(); void test_map(); -- GitLab