From ceaedb088314ddbbcc416748f8cc0319ebf21aa8 Mon Sep 17 00:00:00 2001
From: Lukas Holecek <hluk@email.cz>
Date: Sat, 29 Sep 2012 19:09:08 +0200
Subject: [PATCH] fakevim: Correct Undo/Redo position for large editing
 operations

Change-Id: Ie5d354a0e7f59c61646d14da79bf7059821c12e5
Reviewed-by: hjk <qthjk@ovi.com>
---
 src/plugins/fakevim/fakevim_test.cpp   | 25 +++++++--
 src/plugins/fakevim/fakevimhandler.cpp | 78 ++++++++++++++++++--------
 2 files changed, 74 insertions(+), 29 deletions(-)

diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp
index f75e65a7554..92c3fe80987 100644
--- a/src/plugins/fakevim/fakevim_test.cpp
+++ b/src/plugins/fakevim/fakevim_test.cpp
@@ -714,20 +714,19 @@ void FakeVimPlugin::test_vim_undo_redo()
 
 void FakeVimPlugin::test_advanced_commands()
 {
-    // TODO: Fix undo/redo position for substitute command.
     TestData data;
     setup(&data);
 
     // subcommands
-    data.setText("abc" N "  xxx" N "def");
-    COMMAND("%s/xxx/ZZZ/g|%s/ZZZ/OOO/g", "abc" N "  OOO" N "def");
+    data.setText("abc" N "  xxx" N "  xxx" N "def");
+    COMMAND("%s/xxx/ZZZ/g|%s/ZZZ/OOO/g", "abc" N "  OOO" N "  " X "OOO" N "def");
 
     // undo/redo all subcommands
-    COMMAND(":undo", "abc" N "  xxx" N "def");
-    COMMAND(":redo", "abc" N "  OOO" N "def");
+    COMMAND(":undo", "abc" N X "  xxx" N "  xxx" N "def");
+    COMMAND(":redo", "abc" N X "  OOO" N "  OOO" N "def");
 
     // redundant characters
-    COMMAND(":::   %s/\\S\\S\\S/ZZZ/g   |   ::::   %s/ZZZ/XXX/g ", "XXX" N "  XXX" N "XXX");
+    COMMAND(":::   %s/\\S\\S\\S/ZZZ/g   |   ::::   %s/ZZZ/XXX/g ", "XXX" N "  XXX" N "  XXX" N X "XXX");
 }
 
 void FakeVimPlugin::test_map()
@@ -840,6 +839,20 @@ void FakeVimPlugin::test_map()
     KEYS("<C-r>", X "def xyz ghi");
     data.doCommand("unmap  X");
 
+    data.setText("abc" N "  def" N "  ghi");
+    data.doCommand("map X jdd");
+    KEYS("X", "abc" N "  " X "ghi");
+    KEYS("u", "abc" N X "  def" N "  ghi");
+    KEYS("<c-r>", "abc" N X "  ghi");
+    data.doCommand("unmap  X");
+
+    data.setText("abc" N "def" N "ghi");
+    data.doCommand("map X jAxxx<cr>yyy<esc>");
+    KEYS("X", "abc" N "defxxx" N "yy" X "y" N "ghi");
+    KEYS("u", "abc" N "de" X "f" N "ghi");
+    KEYS("<c-r>", "abc" N "def" X "xxx" N "yyy" N "ghi");
+    data.doCommand("unmap  X");
+
     NOT_IMPLEMENTED
     // <C-o>
     data.setText("abc def");
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 3fccc3697dd..f3b0b3cc690 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -1315,21 +1315,11 @@ public:
     QTextDocument *document() const { return EDITOR(document()); }
     QChar characterAtCursor() const
         { return document()->characterAt(position()); }
-    void joinPreviousEditBlock() {
-        UNDO_DEBUG("JOIN");
-        if (m_breakEditBlock)
-            beginEditBlock();
-        else
-            cursor().joinPreviousEditBlock();
-    }
-    void beginEditBlock() {
-        UNDO_DEBUG("BEGIN EDIT BLOCK");
-        cursor().beginEditBlock();
-        setUndoPosition(false);
-        m_breakEditBlock = false;
-    }
-    void endEditBlock()
-        { UNDO_DEBUG("END EDIT BLOCK"); cursor().endEditBlock(); }
+
+    void joinPreviousEditBlock();
+    void beginEditBlock(bool rememberPosition = true);
+    void beginLargeEditBlock() { beginEditBlock(false); }
+    void endEditBlock();
     void breakEditBlock() { m_breakEditBlock = true; }
 
     bool isVisualMode() const { return m_visualMode != NoVisualMode; }
@@ -2005,7 +1995,7 @@ void FakeVimHandler::Private::handleMappedKeys()
         g.pendingInput << inputs << Input() << rest;
         g.mapStates << MappingState(maxMapDepth, inputs.noremap(), inputs.silent());
         g.commandBuffer.setHistoryAutoSave(false);
-        beginEditBlock();
+        beginLargeEditBlock();
     }
     g.currentMap.reset();
 }
@@ -4001,8 +3991,6 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
         replacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
         flags = line.mid(pos2 + 1);
 
-        needle.replace('$', '\n');
-        needle.replace("\\\n", "\\$");
         pattern = vimPatternToQtPattern(needle, hasConfig(ConfigSmartCase));
 
         m_lastSubstituteFlags = flags;
@@ -4016,7 +4004,8 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
     if (flags.contains('i'))
         pattern.setCaseSensitivity(Qt::CaseInsensitive);
 
-    beginEditBlock();
+    int lastLine = -1;
+    int firstLine = -1;
     const bool global = flags.contains('g');
     for (int a = 0; a != count; ++a) {
         const Range range = cmd.range.endPos == 0 ? rangeFromCurrentLine() : cmd.range;
@@ -4046,6 +4035,13 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
                 repl.replace("\\&", "&");
                 text = text.left(pos) + repl + text.mid(pos + matched.size());
                 pos += repl.size();
+
+                firstLine = line;
+                if (lastLine == -1) {
+                    lastLine = line;
+                    beginEditBlock();
+                }
+
                 if (!global)
                     break;
             }
@@ -4053,9 +4049,21 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
                 setLineContents(line, text);
         }
     }
-    moveToStartOfLine();
-    setTargetColumn();
-    endEditBlock();
+
+    if (lastLine != -1) {
+        State &state = m_undo.top();
+        state.line = firstLine;
+        state.position = firstPositionInLine(firstLine);
+
+        QTextCursor tc = cursor();
+        tc.setPosition(firstPositionInLine(lastLine));
+        setCursor(tc);
+        moveToFirstNonBlankOnLine();
+        setTargetColumn();
+
+        endEditBlock();
+    }
+
     return true;
 }
 
@@ -4530,7 +4538,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
     cmd.setContentsFromLine(line);
     //qDebug() << "CMD: " << cmd;
 
-    beginEditBlock();
+    beginLargeEditBlock();
     while (cmd.nextSubcommand()) {
         if (!handleExCommandHelper(cmd)) {
             showMessage(MessageError,
@@ -5668,6 +5676,30 @@ QWidget *FakeVimHandler::Private::editor() const
         : static_cast<QWidget *>(m_plaintextedit);
 }
 
+void FakeVimHandler::Private::joinPreviousEditBlock()
+{
+    UNDO_DEBUG("JOIN");
+    if (m_breakEditBlock)
+        beginEditBlock();
+    else
+        cursor().joinPreviousEditBlock();
+}
+
+void FakeVimHandler::Private::beginEditBlock(bool rememberPosition)
+{
+    UNDO_DEBUG("BEGIN EDIT BLOCK");
+    cursor().beginEditBlock();
+    if (rememberPosition)
+        setUndoPosition(false);
+    m_breakEditBlock = false;
+}
+
+void FakeVimHandler::Private::endEditBlock()
+{
+    UNDO_DEBUG("END EDIT BLOCK");
+    cursor().endEditBlock();
+}
+
 char FakeVimHandler::Private::currentModeCode() const
 {
     if (m_mode == ExMode)
-- 
GitLab