Browse Source

vim-mode: Ctrl+O to support autoindent and autolist

Le Tan 8 years ago
parent
commit
f6a91d04a8
5 changed files with 156 additions and 100 deletions
  1. 94 0
      src/utils/veditutils.cpp
  2. 19 0
      src/utils/veditutils.h
  3. 33 41
      src/utils/vvim.cpp
  4. 10 57
      src/vmdeditoperations.cpp
  5. 0 2
      src/vmdeditoperations.h

+ 94 - 0
src/utils/veditutils.cpp

@@ -3,6 +3,8 @@
 #include <QTextDocument>
 #include <QDebug>
 
+#include "vutils.h"
+
 void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text)
 {
     QTextCursor cursor(p_block);
@@ -39,3 +41,95 @@ void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text)
 
     p_cursor.movePosition(QTextCursor::StartOfBlock);
 }
+
+bool VEditUtils::insertBlockWithIndent(QTextCursor &p_cursor)
+{
+    V_ASSERT(!p_cursor.hasSelection());
+    p_cursor.insertBlock();
+    return indentBlockAsPreviousBlock(p_cursor);
+}
+
+bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
+{
+    bool ret = false;
+    QTextBlock block = p_cursor.block();
+    QTextBlock preBlock = block.previous();
+    if (!preBlock.isValid()) {
+        return false;
+    }
+
+    QString text = preBlock.text();
+    QRegExp regExp("^\\s*(-|\\d+\\.)\\s");
+    int regIdx = regExp.indexIn(text);
+    if (regIdx != -1) {
+        ret = true;
+        V_ASSERT(regExp.captureCount() == 1);
+        QString markText = regExp.capturedTexts()[1];
+        if (markText == "-") {
+            // Insert - in front.
+            p_cursor.insertText("- ");
+        } else {
+            // markText is like "123.".
+            V_ASSERT(markText.endsWith('.'));
+            bool ok = false;
+            int num = markText.left(markText.size() - 1).toInt(&ok, 10);
+            V_ASSERT(ok);
+            num++;
+            p_cursor.insertText(QString::number(num, 10) + ". ");
+        }
+    }
+
+    return ret;
+
+}
+
+bool VEditUtils::indentBlockAsPreviousBlock(QTextCursor &p_cursor)
+{
+    bool changed = false;
+    QTextBlock block = p_cursor.block();
+    if (block.blockNumber() == 0) {
+        // The first block.
+        return false;
+    }
+
+    QTextBlock preBlock = block.previous();
+    QString text = preBlock.text();
+    QRegExp regExp("(^\\s*)");
+    regExp.indexIn(text);
+    V_ASSERT(regExp.captureCount() == 1);
+    QString leadingSpaces = regExp.capturedTexts()[1];
+
+    moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
+    if (!p_cursor.atBlockStart()) {
+        p_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
+        p_cursor.removeSelectedText();
+        changed = true;
+    }
+
+    if (!leadingSpaces.isEmpty()) {
+        p_cursor.insertText(leadingSpaces);
+        changed = true;
+    }
+
+    p_cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
+
+    return changed;
+}
+
+void VEditUtils::moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
+                                                  QTextCursor::MoveMode p_mode)
+{
+    QTextBlock block = p_cursor.block();
+    QString text = block.text();
+    int idx = 0;
+    for (; idx < text.size(); ++idx) {
+        if (text[idx].isSpace()) {
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    p_cursor.setPosition(block.position() + idx, p_mode);
+}
+

+ 19 - 0
src/utils/veditutils.h

@@ -17,6 +17,25 @@ public:
     // Need to call setTextCursor() to make it take effect.
     static void removeBlock(QTextCursor &p_cursor, QString *p_text = NULL);
 
+    // Move @p_cursor to the first non-space character of current block.
+    // Need to call setTextCursor() to make it take effect.
+    static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
+                                                 QTextCursor::MoveMode p_mode);
+    // Indent current block as previous block.
+    // Return true if some changes have been made.
+    static bool indentBlockAsPreviousBlock(QTextCursor &p_cursor);
+
+    // Insert a new block at current position with the same indentation as
+    // current block. Should clear the selection before calling this.
+    // Returns true if non-empty indentation has been inserted.
+    // Need to call setTextCursor() to make it take effect.
+    static bool insertBlockWithIndent(QTextCursor &p_cursor);
+
+    // Fetch the list mark of previous block, and insert it at current position.
+    // Returns true if list mark has been inserted.
+    // Need to call setTextCursor() to make it take effect.
+    static bool insertListMarkAsPreviousBlock(QTextCursor &p_cursor);
+
 private:
     VEditUtils() {}
 };

+ 33 - 41
src/utils/vvim.cpp

@@ -5,9 +5,12 @@
 #include <QString>
 #include <QScrollBar>
 #include <QDebug>
+#include "vconfigmanager.h"
 #include "vedit.h"
 #include "utils/veditutils.h"
 
+extern VConfigManager vconfig;
+
 const QChar VVim::c_unnamedRegister = QChar('"');
 const QChar VVim::c_blackHoleRegister = QChar('_');
 const QChar VVim::c_selectionRegister = QChar('+');
@@ -35,25 +38,6 @@ static void setCursorPositionInBlock(QTextCursor &p_cursor, int p_positionInBloc
     }
 }
 
-// Move @p_cursor to the first non-space character of current block.
-// Need to setTextCursor() after calling this.
-static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
-                                             QTextCursor::MoveMode p_mode)
-{
-    QTextBlock block = p_cursor.block();
-    QString text = block.text();
-    int idx = 0;
-    for (; idx < text.size(); ++idx) {
-        if (text[idx].isSpace()) {
-            continue;
-        } else {
-            break;
-        }
-    }
-
-    p_cursor.setPosition(block.position() + idx, p_mode);
-}
-
 // Find the start and end of the WORD @p_cursor locates in (within a single block).
 // @p_start and @p_end will be the global position of the start and end of the WORD.
 // @p_start will equals to @p_end if @p_cursor is a space.
@@ -336,7 +320,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             QTextCursor cursor = m_editor->textCursor();
             if (m_mode == VimMode::Normal) {
                 // Insert at the first non-space character.
-                moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor);
+                VEditUtils::moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor);
             } else if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
                 // Insert at the start of line.
                 cursor.movePosition(QTextCursor::StartOfBlock,
@@ -391,31 +375,39 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
 
     case Qt::Key_O:
     {
-        if (modifiers == Qt::NoModifier) {
-            // Insert a new block under current block and enter insert mode.
-            if (m_mode == VimMode::Normal) {
-                QTextCursor cursor = m_editor->textCursor();
-                cursor.movePosition(QTextCursor::EndOfBlock,
-                                    QTextCursor::MoveAnchor,
-                                    1);
-                cursor.insertBlock();
-                m_editor->setTextCursor(cursor);
-                setMode(VimMode::Insert);
-            }
-        } else if (modifiers == Qt::ShiftModifier) {
-            // Insert a new block above current block and enter insert mode.
+        if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) {
+            // Insert a new block under/above current block and enter insert mode.
+            bool insertAbove = modifiers == Qt::ShiftModifier;
             if (m_mode == VimMode::Normal) {
                 QTextCursor cursor = m_editor->textCursor();
-                cursor.movePosition(QTextCursor::StartOfBlock,
+                cursor.beginEditBlock();
+                cursor.movePosition(insertAbove ? QTextCursor::StartOfBlock
+                                                : QTextCursor::EndOfBlock,
                                     QTextCursor::MoveAnchor,
                                     1);
+
                 cursor.insertBlock();
-                cursor.movePosition(QTextCursor::PreviousBlock,
-                                    QTextCursor::MoveAnchor,
-                                    1);
+
+                if (insertAbove) {
+                    cursor.movePosition(QTextCursor::PreviousBlock,
+                                        QTextCursor::MoveAnchor,
+                                        1);
+                }
+
+                if (vconfig.getAutoIndent()) {
+                    VEditUtils::indentBlockAsPreviousBlock(cursor);
+                    if (vconfig.getAutoList()) {
+                        VEditUtils::insertListMarkAsPreviousBlock(cursor);
+                    }
+                }
+
+                cursor.endEditBlock();
                 m_editor->setTextCursor(cursor);
+
                 setMode(VimMode::Insert);
             }
+
+            break;
         }
 
         break;
@@ -1163,7 +1155,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
         // p_repeat is not considered in this command.
         // If all the block is space, just move to the end of block; otherwise,
         // move to the first non-space character.
-        moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
+        VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
         hasMoved = true;
         break;
     }
@@ -1182,7 +1174,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
             p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
         }
 
-        moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
+        VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
         hasMoved = true;
         break;
     }
@@ -1192,7 +1184,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
         // Jump to the first non-space character of the start of the document.
         V_ASSERT(p_repeat == -1);
         p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
-        moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
+        VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
         hasMoved = true;
         break;
     }
@@ -1202,7 +1194,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
         // Jump to the first non-space character of the end of the document.
         V_ASSERT(p_repeat == -1);
         p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
-        moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
+        VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
         hasMoved = true;
         break;
     }

+ 10 - 57
src/vmdeditoperations.cpp

@@ -22,6 +22,7 @@
 #include "vmdedit.h"
 #include "vconfigmanager.h"
 #include "utils/vvim.h"
+#include "utils/veditutils.h"
 
 extern VConfigManager vconfig;
 
@@ -718,14 +719,21 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
     if (vconfig.getAutoIndent()) {
         handled = true;
 
+        QTextCursor cursor = m_editor->textCursor();
         bool textInserted = false;
+        cursor.beginEditBlock();
+        cursor.removeSelectedText();
+
         // Indent the new line as previous line.
-        textInserted = insertNewBlockWithIndent();
+        textInserted = VEditUtils::insertBlockWithIndent(cursor);
 
         // Continue the list from previous line.
         if (vconfig.getAutoList()) {
-            textInserted = insertListMarkAsPreviousLine() || textInserted;
+            textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor) || textInserted;
         }
+
+        cursor.endEditBlock();
+        m_editor->setTextCursor(cursor);
         if (textInserted) {
             m_autoIndentPos = m_editor->textCursor().position();
         }
@@ -734,61 +742,6 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
     return handled;
 }
 
-bool VMdEditOperations::insertNewBlockWithIndent()
-{
-    QTextCursor cursor = m_editor->textCursor();
-    bool ret = false;
-
-    cursor.beginEditBlock();
-    cursor.removeSelectedText();
-    QTextBlock block = cursor.block();
-    QString text = block.text();
-    QRegExp regExp("(^\\s*)");
-    regExp.indexIn(text);
-    V_ASSERT(regExp.captureCount() == 1);
-    QString leadingSpaces = regExp.capturedTexts()[1];
-    cursor.insertBlock();
-    if (!leadingSpaces.isEmpty()) {
-        cursor.insertText(leadingSpaces);
-        ret = true;
-    }
-    cursor.endEditBlock();
-    m_editor->setTextCursor(cursor);
-    return ret;
-}
-
-bool VMdEditOperations::insertListMarkAsPreviousLine()
-{
-    bool ret = false;
-    QTextCursor cursor = m_editor->textCursor();
-    QTextBlock block = cursor.block();
-    QTextBlock preBlock = block.previous();
-    QString text = preBlock.text();
-    QRegExp regExp("^\\s*(-|\\d+\\.)\\s");
-    int regIdx = regExp.indexIn(text);
-    if (regIdx != -1) {
-        ret = true;
-        V_ASSERT(regExp.captureCount() == 1);
-        cursor.beginEditBlock();
-        QString markText = regExp.capturedTexts()[1];
-        if (markText == "-") {
-            // Insert - in front.
-            cursor.insertText("- ");
-        } else {
-            // markText is like "123.".
-            V_ASSERT(markText.endsWith('.'));
-            bool ok = false;
-            int num = markText.left(markText.size() - 1).toInt(&ok, 10);
-            V_ASSERT(ok);
-            num++;
-            cursor.insertText(QString::number(num, 10) + ". ");
-        }
-        cursor.endEditBlock();
-        m_editor->setTextCursor(cursor);
-    }
-    return ret;
-}
-
 bool VMdEditOperations::isListBlock(const QTextBlock &p_block, int *p_seq)
 {
     QString text = p_block.text();

+ 0 - 2
src/vmdeditoperations.h

@@ -42,8 +42,6 @@ private:
     bool handleKeyReturn(QKeyEvent *p_event);
     bool handleKeyBracketLeft(QKeyEvent *p_event);
     bool insertTitle(int p_level);
-    bool insertNewBlockWithIndent();
-    bool insertListMarkAsPreviousLine();
     void deleteIndentAndListMark();
 
     // Check if @p_block is a auto list block.