From ff5f9af9e70638320406d8c95166fdb87d4e3408 Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Fri, 6 Mar 2009 11:22:16 +0100
Subject: [PATCH] Fixes:    fakevime: some basic implementation for << and >>

---
 src/plugins/fakevim/fakevimhandler.cpp | 198 +++++++++++++++++++------
 1 file changed, 156 insertions(+), 42 deletions(-)

diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 7c534deb9b3..094e08603cc 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -35,6 +35,21 @@
 // Instead emit signals and let the FakeVimPlugin channel the information to
 // Qt Creator. The idea is to keep this file here in a "clean" state that
 // allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
+//
+//
+// Some conventions:
+//
+// Use 1 based line numbers and 0 based column numbers. Even though
+// the 1 based line are not nice it matches vim's and QTextEdit's 'line'
+// concepts.
+//
+// Do not pass QTextCursor etc around unless really needed. Convert
+// early to  line/column.
+//
+// There is always a "current" cursor (m_tc). A current "region of interest"
+// spans between m_anchor (== anchor()) and  m_tc.position() (== position())
+// The value of m_tc.anchor() is not used.
+
 
 #include <QtCore/QDebug>
 #include <QtCore/QFile>
@@ -64,6 +79,13 @@
 #   define KEY_DEBUG(s)
 #endif
 
+//#define DEBUG_UNDO  1
+#if DEBUG_UNDO
+#   define UNDO_DEBUG(s) qDebug() << s
+#else
+#   define UNDO_DEBUG(s)
+#endif
+
 using namespace FakeVim::Internal;
 using namespace FakeVim::Constants;
 
@@ -77,7 +99,6 @@ using namespace FakeVim::Constants;
 #define Left           QTextCursor::Left
 #define EndOfDocument  QTextCursor::End
 
-
 ///////////////////////////////////////////////////////////////////////
 //
 // FakeVimHandler
@@ -102,13 +123,15 @@ enum Mode
 enum SubMode
 {
     NoSubMode,
-    RegisterSubMode,
-    ChangeSubMode,
-    DeleteSubMode,
-    FilterSubMode,
+    RegisterSubMode,   // used for "
+    ChangeSubMode,     // used for c
+    DeleteSubMode,     // used for d
+    FilterSubMode,     // used for !
     ReplaceSubMode,    // used for R and r
-    YankSubMode,
-    IndentSubMode,
+    YankSubMode,       // used for y
+    ShiftLeftSubMode,  // used for <
+    ShiftRightSubMode, // used for >
+    IndentSubMode,     // used for =
     ZSubMode,
 };
 
@@ -250,6 +273,8 @@ private:
     void moveRight(int n = 1) { m_tc.movePosition(Right, MoveAnchor, n); }
     void moveLeft(int n = 1) { m_tc.movePosition(Left, MoveAnchor, n); }
     void setAnchor() { m_anchor = m_tc.position(); }
+    void setAnchor(int position) { m_anchor = position; }
+    void setPosition(int position) { m_tc.setPosition(position, MoveAnchor); }
 
     void handleFfTt(int key);
 
@@ -302,6 +327,9 @@ public:
     QString m_lastInsertion;
 
     // undo handling
+    void recordShiftRegionLeft(int repeat = 1);
+    void recordShiftRegionRight(int repeat = 1);
+
     void recordOperation(const EditOperation &op);
     void recordInsert(int position, const QString &data);
     void recordRemove(int position, const QString &data);
@@ -310,7 +338,7 @@ public:
     void recordRemoveNextChar();
     void recordInsertText(const QString &data);
     QString recordRemoveSelectedText();
-    void recordMove();
+    void recordPosition();
     void recordBeginGroup();
     void recordEndGroup();
     int anchor() const { return m_anchor; }
@@ -561,7 +589,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
     if (m_submode == FilterSubMode) {
         int beginLine = lineForPosition(anchor());
         int endLine = lineForPosition(position());
-        m_tc.setPosition(qMin(anchor(), position()));
+        setPosition(qMin(anchor(), position()));
         enterExMode();
         m_commandBuffer = QString(".,+%1!").arg(qAbs(endLine - beginLine));
         m_commandHistory.append(QString());
@@ -599,7 +627,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
             moveLeft();
     } else if (m_submode == YankSubMode) {
         m_registers[m_register] = selectedText();
-        m_tc.setPosition(m_savedYankPosition);
+        setPosition(m_savedYankPosition);
         m_submode = NoSubMode;
     } else if (m_submode == ReplaceSubMode) {
         m_submode = NoSubMode;
@@ -611,10 +639,19 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
             qSwap(start, end);
         QTextBlock startBlock = doc->findBlock(start);
         indentRegion(doc->findBlock(start), doc->findBlock(end).next());
-        m_tc.setPosition(startBlock.position());
+        setPosition(startBlock.position());
         moveToFirstNonBlankOnLine();
         m_submode = NoSubMode;
+    } else if (m_submode == ShiftRightSubMode) {
+        recordShiftRegionRight(1);
+        m_submode = NoSubMode;
+        updateMiniBuffer();
+    } else if (m_submode == ShiftLeftSubMode) {
+        recordShiftRegionLeft(1);
+        m_submode = NoSubMode;
+        updateMiniBuffer();
     }
+
     m_moveType = MoveInclusive;
     m_mvcount.clear();
     m_opcount.clear();
@@ -781,6 +818,18 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
         moveDown(count());
         m_moveType = MoveLineWise;
         finishMovement("y");
+    } else if (m_submode == ShiftLeftSubMode && key == '<') {
+        setAnchor();
+        moveDown(count() - 1);
+        m_moveType = MoveLineWise;
+        m_dotCommand = QString("%1<<").arg(count());
+        finishMovement();
+    } else if (m_submode == ShiftRightSubMode && key == '>') {
+        setAnchor();
+        moveDown(count() - 1);
+        m_moveType = MoveLineWise;
+        m_dotCommand = QString("%1>>").arg(count());
+        finishMovement();
     } else if (m_submode == IndentSubMode && key == '=') {
         indentRegion(m_tc.block(), m_tc.block().next());
         finishMovement();
@@ -788,21 +837,21 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
         //qDebug() << "Z_MODE " << cursorLineInDocument() << linesOnScreen();
         if (key == Key_Return || key == 't') { // cursor line to top of window
             if (!m_mvcount.isEmpty())
-                m_tc.setPosition(firstPositionInLine(count()));
+                setPosition(firstPositionInLine(count()));
             scrollToLineInDocument(cursorLineInDocument());
             if (key == Key_Return)
                 moveToFirstNonBlankOnLine();
             finishMovement();
         } else if (key == '.' || key == 'z') { // cursor line to center of window
             if (!m_mvcount.isEmpty())
-                m_tc.setPosition(firstPositionInLine(count()));
+                setPosition(firstPositionInLine(count()));
             scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
             if (key == '.')
                 moveToFirstNonBlankOnLine();
             finishMovement();
         } else if (key == '-' || key == 'b') { // cursor line to bottom of window
             if (!m_mvcount.isEmpty())
-                m_tc.setPosition(firstPositionInLine(count()));
+                setPosition(firstPositionInLine(count()));
             scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() - 1);
             if (key == '-')
                 moveToFirstNonBlankOnLine();
@@ -839,7 +888,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
     } else if (m_subsubmode == BackTickSubSubMode
             || m_subsubmode == TickSubSubMode) {
         if (m_marks.contains(key)) {
-            m_tc.setPosition(m_marks[key]);
+            setPosition(m_marks[key]);
             if (m_subsubmode == TickSubSubMode)
                 moveToFirstNonBlankOnLine();
             finishMovement();
@@ -943,6 +992,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
                 handleKey(c.unicode(), c.unicode(), QString(c));
         enterCommandMode();
         m_dotCommand = savedCommand;
+    } else if (key == '<') {
+        m_savedYankPosition = m_tc.position();
+        m_submode = ShiftLeftSubMode;
+    } else if (key == '>') {
+        m_savedYankPosition = m_tc.position();
+        m_submode = ShiftRightSubMode;
     } else if (key == '=') {
         m_submode = IndentSubMode;
     } else if (key == '%') {
@@ -1065,7 +1120,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
     } else if (key == control('i')) {
         if (!m_jumpListRedo.isEmpty()) {
             m_jumpListUndo.append(position());
-            m_tc.setPosition(m_jumpListRedo.takeLast());
+            setPosition(m_jumpListRedo.takeLast());
         }
     } else if (key == 'j' || key == Key_Down) {
         //qDebug() << "DESIRED COLUMN" << m_desiredColumn;
@@ -1135,7 +1190,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
         recordJump();
     } else if (key == 'o' || key == 'O') {
         recordBeginGroup();
-        recordMove();
+        recordPosition();
         enterInsertMode();
         moveToFirstNonBlankOnLine();
         int numSpaces = leftDist();
@@ -1151,7 +1206,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
     } else if (key == control('o')) {
         if (!m_jumpListUndo.isEmpty()) {
             m_jumpListRedo.append(position());
-            m_tc.setPosition(m_jumpListUndo.takeLast());
+            setPosition(m_jumpListUndo.takeLast());
         }
     } else if (key == 'p' || key == 'P') {
         recordBeginGroup();
@@ -1160,7 +1215,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
         //qDebug() << "REGISTERS: " << m_registers << "MOVE: " << m_moveType;
         //qDebug() << "LINES: " << n << text << m_register;
         if (n > 0) {
-            recordMove();
+            recordPosition();
             moveToStartOfLine();
             m_desiredColumn = 0;
             for (int i = count(); --i >= 0; ) {
@@ -1270,7 +1325,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified,
         int endLine = lineForPosition(m_marks['>']);
         selectRange(beginLine, endLine);
         m_registers[m_register] = selectedText();
-        m_tc.setPosition(qMin(position(), anchor()));
+        setPosition(qMin(position(), anchor()));
         moveToStartOfLine();
         leaveVisualMode();
         updateSelection();
@@ -1540,13 +1595,17 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
 
 void FakeVimHandler::Private::selectRange(int beginLine, int endLine)
 {
-    m_anchor = firstPositionInLine(beginLine);
-    if (endLine == linesInDocument()) {
-        m_tc.setPosition(firstPositionInLine(endLine), MoveAnchor);
-        m_tc.movePosition(EndOfLine, MoveAnchor);
-    } else {
-        m_tc.setPosition(firstPositionInLine(endLine + 1), MoveAnchor);
-    }
+    if (beginLine == -1)
+        beginLine = cursorLineInDocument();
+    if (endLine == -1)
+        endLine = cursorLineInDocument();
+    if (beginLine > endLine)
+        qSwap(beginLine, endLine);
+    setAnchor(firstPositionInLine(beginLine));
+    if (endLine == linesInDocument())
+       setPosition(lastPositionInLine(endLine));
+    else
+       setPosition(firstPositionInLine(endLine + 1));
 }
 
 void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
@@ -1577,16 +1636,12 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
     static QRegExp reHistory("^his(tory)?( (.*))?$");
 
     if (cmd.isEmpty()) {
-        m_tc.setPosition(firstPositionInLine(beginLine));
+        setPosition(firstPositionInLine(beginLine));
         showBlackMessage(QString());
         enterCommandMode();
     } else if (cmd == "q!" || cmd == "q") { // :q
         quit();
     } else if (reDelete.indexIn(cmd) != -1) { // :d
-        if (beginLine == -1)
-            beginLine = cursorLineInDocument();
-        if (endLine == -1)
-            endLine = cursorLineInDocument();
         selectRange(beginLine, endLine);
         QString reg = reDelete.cap(2);
         QString text = recordRemoveSelectedText(); 
@@ -1650,10 +1705,6 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
         showBlackMessage(tr("\"%1\" %2L, %3C")
             .arg(m_currentFileName).arg(data.count('\n')).arg(data.size()));
     } else if (cmd.startsWith("!")) {
-        if (beginLine == -1)
-            beginLine = cursorLineInDocument();
-        if (endLine == -1)
-            endLine = cursorLineInDocument();
         selectRange(beginLine, endLine);
         QString command = cmd.mid(1).trimmed();
         recordBeginGroup();
@@ -1669,7 +1720,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
         recordEndGroup();
         leaveVisualMode();
 
-        m_tc.setPosition(firstPositionInLine(beginLine));
+        setPosition(firstPositionInLine(beginLine));
         EditOperation op;
         // FIXME: broken for "upward selection"
         op.position = m_tc.position();
@@ -1680,6 +1731,14 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
         enterCommandMode();
         //qDebug() << "FILTER: " << command;
         showBlackMessage(tr("%1 lines filtered").arg(text.count('\n')));
+    } else if (cmd.startsWith(">")) {
+        m_anchor = firstPositionInLine(beginLine);
+        setPosition(firstPositionInLine(endLine));
+        recordShiftRegionRight(1);
+        leaveVisualMode();
+        enterCommandMode();
+        showBlackMessage(tr("%1 lines >ed %2 time")
+            .arg(endLine - beginLine + 1).arg(1));
     } else if (cmd == "red" || cmd == "redo") { // :redo
         redo();
         enterCommandMode();
@@ -1798,6 +1857,61 @@ void FakeVimHandler::Private::indentCurrentLine(QChar typedChar)
     indentRegion(m_tc.block(), m_tc.block().next(), typedChar);
 }
 
+void FakeVimHandler::Private::recordShiftRegionRight(int repeat)
+{
+    int savedPos = anchor();
+    int beginLine = lineForPosition(anchor());
+    int endLine = lineForPosition(position());
+    if (beginLine > endLine)
+        qSwap(beginLine, endLine);
+    int len = m_config[ConfigShiftWidth].toInt() * repeat;
+    QString indent(len, ' ');
+    recordBeginGroup();
+    recordPosition();
+    for (int line = beginLine; line <= endLine; ++line) {
+        setPosition(firstPositionInLine(line));
+        recordInsertText(indent);
+    }
+    recordEndGroup();
+    setPosition(savedPos);
+}
+
+void FakeVimHandler::Private::recordShiftRegionLeft(int repeat)
+{
+    int savedPos = anchor();
+    int beginLine = lineForPosition(anchor());
+    int endLine = lineForPosition(position());
+    if (beginLine > endLine)
+        qSwap(beginLine, endLine);
+    int shift = m_config[ConfigShiftWidth].toInt() * repeat;
+    int tab = m_config[ConfigTabStop].toInt();
+
+    recordBeginGroup();
+    recordPosition();
+
+    for (int line = beginLine; line <= endLine; ++line) {
+        int pos = firstPositionInLine(line);
+        setPosition(pos);
+        setAnchor(pos);
+        QString text = m_tc.block().text();
+        int amount = 0;
+        int i = 0;
+        for (; i < text.size() && amount <= shift; ++i) {
+            if (text.at(i) == ' ')
+                amount++;
+            else if (text.at(i) == '\t')
+                amount += tab; // FIXME: take position into consideration
+            else
+                break;
+        }
+        setPosition(pos + i);
+        text = recordRemoveSelectedText();
+        setPosition(pos);
+    }
+    recordEndGroup();
+    setPosition(savedPos);
+}
+
 void FakeVimHandler::Private::moveToDesiredColumn()
 {
    if (m_desiredColumn == -1 || m_tc.block().length() <= m_desiredColumn)
@@ -2125,7 +2239,7 @@ QString FakeVimHandler::Private::recordRemoveSelectedText()
 
 void FakeVimHandler::Private::recordRemoveNextChar()
 {
-    m_anchor = position(); 
+    setAnchor();
     moveRight();
     recordRemoveSelectedText();
 }
@@ -2139,7 +2253,7 @@ void FakeVimHandler::Private::recordInsertText(const QString &data)
     m_tc.insertText(data);
 }
 
-void FakeVimHandler::Private::recordMove()
+void FakeVimHandler::Private::recordPosition()
 {
     EditOperation op;
     op.position = m_tc.position();
@@ -2151,7 +2265,7 @@ void FakeVimHandler::Private::recordMove()
 
 void FakeVimHandler::Private::recordOperation(const EditOperation &op)
 {
-    //qDebug() << "OP: " << op;
+    UNDO_DEBUG("OP: " << op);
     // No need to record operations that actually do not change anything.
     if (op.from.isEmpty() && op.to.isEmpty() && op.itemCount == 0)
         return;
@@ -2160,7 +2274,7 @@ void FakeVimHandler::Private::recordOperation(const EditOperation &op)
         return;
     m_undoStack.push(op);
     m_redoStack.clear();
-    //qDebug() << "\nSTACK: " << m_undoStack;
+    UNDO_DEBUG("\nSTACK: " << m_undoStack);
 }
 
 void FakeVimHandler::Private::recordInsert(int position, const QString &data)
@@ -2221,7 +2335,7 @@ void FakeVimHandler::Private::recordJump()
 {
     m_jumpListUndo.append(position());
     m_jumpListRedo.clear();
-    //qDebug() << m_jumpListUndo;
+    UNDO_DEBUG("jumps: " << m_jumpListUndo);
 }
 
 ///////////////////////////////////////////////////////////////////////
-- 
GitLab