Browse Source

vim-mode: support = to auto indent selected blocks as previous block

Le Tan 8 years ago
parent
commit
d3ff787153
5 changed files with 178 additions and 59 deletions
  1. 51 7
      src/utils/veditutils.cpp
  2. 11 2
      src/utils/veditutils.h
  3. 106 43
      src/utils/vvim.cpp
  4. 6 2
      src/utils/vvim.h
  5. 4 5
      src/vmdeditoperations.cpp

+ 51 - 7
src/utils/veditutils.cpp

@@ -88,14 +88,21 @@ bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
 
 bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
 {
-    bool changed = false;
     QTextBlock block = p_cursor.block();
     QTextBlock refBlock = p_next ? block.next() : block.previous();
-    if (!refBlock.isValid()) {
+    return indentBlockAsBlock(p_cursor, refBlock);
+}
+
+bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, const QTextBlock &p_refBlock)
+{
+    if (!p_refBlock.isValid()) {
         return false;
     }
 
-    QString leadingSpaces = fetchIndentSpaces(refBlock);
+    Q_ASSERT(!p_cursor.hasSelection());
+
+    bool changed = false;
+    QString leadingSpaces = fetchIndentSpaces(p_refBlock);
 
     moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
     if (!p_cursor.atBlockStart()) {
@@ -112,6 +119,42 @@ bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
     return changed;
 }
 
+// Use another QTextCursor to remain the selection.
+void VEditUtils::indentSelectedBlocksAsBlock(const QTextCursor &p_cursor, bool p_next)
+{
+    int nrBlocks = 1;
+    int start = p_cursor.selectionStart();
+    int end = p_cursor.selectionEnd();
+
+    QTextDocument *doc = p_cursor.document();
+    QTextBlock sBlock = doc->findBlock(start);
+    QTextBlock refBlock;
+    if (!p_next) {
+        refBlock = sBlock.previous();
+    }
+
+    if (start != end) {
+        QTextBlock eBlock = doc->findBlock(end);
+        nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
+
+        if (p_next) {
+            refBlock = eBlock.next();
+        }
+    } else {
+        refBlock = sBlock.next();
+    }
+
+    QTextCursor bCursor(sBlock);
+    bCursor.beginEditBlock();
+    for (int i = 0; i < nrBlocks; ++i) {
+        indentBlockAsBlock(bCursor, refBlock);
+
+        bCursor.movePosition(QTextCursor::NextBlock);
+    }
+
+    bCursor.endEditBlock();
+}
+
 bool VEditUtils::hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb)
 {
     int nonSpaceIdxa = 0;
@@ -170,8 +213,7 @@ QString VEditUtils::selectedText(const QTextCursor &p_cursor)
 }
 
 // Use another QTextCursor to remain the selection.
-void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
-                                      const QTextCursor &p_cursor,
+void VEditUtils::indentSelectedBlocks(const QTextCursor &p_cursor,
                                       const QString &p_indentationText,
                                       bool p_isIndent)
 {
@@ -179,9 +221,10 @@ void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
     int start = p_cursor.selectionStart();
     int end = p_cursor.selectionEnd();
 
-    QTextBlock sBlock = p_doc->findBlock(start);
+    QTextDocument *doc = p_cursor.document();
+    QTextBlock sBlock = doc->findBlock(start);
     if (start != end) {
-        QTextBlock eBlock = p_doc->findBlock(end);
+        QTextBlock eBlock = doc->findBlock(end);
         nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
     }
 
@@ -196,6 +239,7 @@ void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
 
         bCursor.movePosition(QTextCursor::NextBlock);
     }
+
     bCursor.endEditBlock();
 }
 

+ 11 - 2
src/utils/veditutils.h

@@ -31,6 +31,9 @@ public:
     // @p_next: indent as next block or previous block.
     static bool indentBlockAsBlock(QTextCursor &p_cursor, bool p_next);
 
+    // Indent current block as @p_refBlock.
+    static bool indentBlockAsBlock(QTextCursor &p_cursor, const QTextBlock &p_refBlock);
+
     // Returns true if two blocks has the same indent.
     static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
 
@@ -56,12 +59,18 @@ public:
     static QString selectedText(const QTextCursor &p_cursor);
 
     // Indent selected blocks. If no selection, indent current block.
+    // Cursor position and selection is not changed.
     // @p_isIndent: whether it is indentation or unindentation.
-    static void indentSelectedBlocks(const QTextDocument *p_doc,
-                                     const QTextCursor &p_cursor,
+    static void indentSelectedBlocks(const QTextCursor &p_cursor,
                                      const QString &p_indentationText,
                                      bool p_isIndent);
 
+    // Indent seleced block as next/previous block.
+    // Cursor position and selection is not changed.
+    // Return true if some changes have been made.
+    // @p_next: indent as next block or previous block.
+    static void indentSelectedBlocksAsBlock(const QTextCursor &p_cursor, bool p_next);
+
     // Indent current block.
     // @p_skipEmpty: skip empty block.
     static void indentBlock(QTextCursor &p_cursor,

+ 106 - 43
src/utils/vvim.cpp

@@ -1688,18 +1688,47 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
                 }
             } else {
                 // The first >/<, an Action.
-                if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
-                    QTextCursor cursor = m_editor->textCursorW();
-                    VEditUtils::indentSelectedBlocks(m_editor->documentW(),
-                                                     cursor,
-                                                     m_editConfig->m_tabSpaces,
-                                                     !unindent);
-                    // Different from Vim:
-                    // Do not exit Visual mode after indentation/unindentation.
+                addActionToken(unindent ? Action::UnIndent : Action::Indent);
+
+                if (checkMode(VimMode::Visual)
+                    || checkMode(VimMode::VisualLine)) {
+                    // Movement will be ignored.
+                    addMovementToken(Movement::Left);
+                    processCommand(m_tokens);
+                    break;
+                }
+
+                goto accept;
+            }
+        }
+
+        break;
+    }
+
+    case Qt::Key_Equal:
+    {
+        if (modifiers == Qt::NoModifier) {
+            // =, AutoIndent.
+            tryGetRepeatToken(m_keys, m_tokens);
+            if (hasActionToken()) {
+                // ==.
+                if (checkActionToken(Action::AutoIndent)) {
+                    addRangeToken(Range::Line);
+                    processCommand(m_tokens);
+                    break;
+                }
+            } else {
+                // The first =, an Action.
+                addActionToken(Action::AutoIndent);
+
+                if (checkMode(VimMode::Visual)
+                    || checkMode(VimMode::VisualLine)) {
+                    // Movement will be ignored.
+                    addMovementToken(Movement::Left);
+                    processCommand(m_tokens);
                     break;
                 }
 
-                addActionToken(unindent ? Action::UnIndent : Action::Indent);
                 goto accept;
             }
         }
@@ -2251,11 +2280,15 @@ void VVim::processCommand(QList<Token> &p_tokens)
         break;
 
     case Action::Indent:
-        processIndentAction(p_tokens, true);
+        processIndentAction(p_tokens, IndentType::Indent);
         break;
 
     case Action::UnIndent:
-        processIndentAction(p_tokens, false);
+        processIndentAction(p_tokens, IndentType::UnIndent);
+        break;
+
+    case Action::AutoIndent:
+        processIndentAction(p_tokens, IndentType::AutoIndent);
         break;
 
     case Action::ToLower:
@@ -4265,7 +4298,7 @@ exit:
     setMode(VimMode::Insert);
 }
 
-void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
+void VVim::processIndentAction(QList<Token> &p_tokens, IndentType p_type)
 {
     Token to = p_tokens.takeFirst();
     int repeat = -1;
@@ -4280,10 +4313,27 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
     }
 
     QTextCursor cursor = m_editor->textCursorW();
-    QTextDocument *doc = m_editor->documentW();
+
+    QString op;
+    switch (p_type) {
+    case IndentType::Indent:
+        op = ">";
+        break;
+
+    case IndentType::UnIndent:
+        op = "<";
+        break;
+
+    case IndentType::AutoIndent:
+        op = "=";
+        break;
+
+    default:
+        Q_ASSERT(false);
+    }
 
     if (to.isRange()) {
-        bool changed = selectRange(cursor, doc, to.m_range, repeat);
+        bool changed = selectRange(cursor, m_editor->documentW(), to.m_range, repeat);
         if (changed) {
             switch (to.m_range) {
             case Range::Line:
@@ -4293,19 +4343,18 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
                     repeat = 1;
                 }
 
-                VEditUtils::indentSelectedBlocks(doc,
-                                                 cursor,
-                                                 m_editConfig->m_tabSpaces,
-                                                 p_isIndent);
-
-                if (p_isIndent) {
-                    message(tr("%1 %2 >ed 1 time").arg(repeat).arg(repeat > 1 ? tr("lines")
-                                                                              : tr("line")));
+                if (p_type == IndentType::AutoIndent) {
+                    VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
                 } else {
-                    message(tr("%1 %2 <ed 1 time").arg(repeat).arg(repeat > 1 ? tr("lines")
-                                                                              : tr("line")));
+                    VEditUtils::indentSelectedBlocks(cursor,
+                                                     m_editConfig->m_tabSpaces,
+                                                     p_type == IndentType::Indent);
                 }
 
+                message(tr("%1 %2 %3ed 1 time").arg(repeat)
+                                               .arg(repeat > 1 ? tr("lines")
+                                                               : tr("line"))
+                                               .arg(op));
                 break;
             }
 
@@ -4346,19 +4395,18 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
             case Range::BackQuoteAround:
             {
                 int nrBlock = VEditUtils::selectedBlockCount(cursor);
-                VEditUtils::indentSelectedBlocks(doc,
-                                                 cursor,
-                                                 m_editConfig->m_tabSpaces,
-                                                 p_isIndent);
-
-                if (p_isIndent) {
-                    message(tr("%1 %2 >ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
-                                                                                : tr("line")));
+                if (p_type == IndentType::AutoIndent) {
+                    VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
                 } else {
-                    message(tr("%1 %2 <ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
-                                                                                : tr("line")));
+                    VEditUtils::indentSelectedBlocks(cursor,
+                                                     m_editConfig->m_tabSpaces,
+                                                     p_type == IndentType::Indent);
                 }
 
+                message(tr("%1 %2 %3ed 1 time").arg(nrBlock)
+                                               .arg(nrBlock > 1 ? tr("lines")
+                                                                : tr("line"))
+                                               .arg(op));
                 break;
             }
 
@@ -4384,24 +4432,39 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
         break;
     }
 
+    if (checkMode(VimMode::VisualLine) || checkMode(VimMode::Visual)) {
+        // Visual mode, omitting repeat and movement.
+        // Different from Vim:
+        // Do not exit Visual mode after indentation/unindentation.
+        if (p_type == IndentType::AutoIndent) {
+            VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
+        } else {
+            VEditUtils::indentSelectedBlocks(cursor,
+                                             m_editConfig->m_tabSpaces,
+                                             p_type == IndentType::Indent);
+        }
+
+        return;
+    }
+
     processMovement(cursor,
                     QTextCursor::KeepAnchor,
                     to,
                     repeat);
 
     int nrBlock = VEditUtils::selectedBlockCount(cursor);
-    if (p_isIndent) {
-        message(tr("%1 %2 >ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
-                                                                    : tr("line")));
+    if (p_type == IndentType::AutoIndent) {
+        VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
     } else {
-        message(tr("%1 %2 <ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
-                                                                    : tr("line")));
+        VEditUtils::indentSelectedBlocks(cursor,
+                                         m_editConfig->m_tabSpaces,
+                                         p_type == IndentType::Indent);
     }
 
-    VEditUtils::indentSelectedBlocks(doc,
-                                     cursor,
-                                     m_editConfig->m_tabSpaces,
-                                     p_isIndent);
+    message(tr("%1 %2 %3ed 1 time").arg(nrBlock)
+                                   .arg(nrBlock > 1 ? tr("lines")
+                                                    : tr("line"))
+                                   .arg(op));
 }
 
 void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)

+ 6 - 2
src/utils/vvim.h

@@ -361,6 +361,7 @@ private:
         Change,
         Indent,
         UnIndent,
+        AutoIndent,
         ToUpper,
         ToLower,
         ReverseCase,
@@ -582,6 +583,8 @@ private:
         const int c_maximumLocations;
     };
 
+    enum IndentType { Indent = 0, UnIndent, AutoIndent };
+
     // Returns true if the event is consumed and need no more handling.
     bool handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos = NULL);
 
@@ -615,8 +618,9 @@ private:
     // @p_tokens is the arguments of the Action::Change action.
     void processChangeAction(QList<Token> &p_tokens);
 
-    // @p_tokens is the arguments of the Action::Indent and Action::UnIndent action.
-    void processIndentAction(QList<Token> &p_tokens, bool p_isIndent);
+    // @p_tokens is the arguments of the Action::Indent, Action::UnIndent,
+    // and Action::AutoIndent action.
+    void processIndentAction(QList<Token> &p_tokens, IndentType p_type);
 
     // @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action.
     void processToLowerAction(QList<Token> &p_tokens, bool p_toLower);

+ 4 - 5
src/vmdeditoperations.cpp

@@ -410,7 +410,6 @@ bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
 
 bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
 {
-    QTextDocument *doc = m_editor->documentW();
     QString text(m_editConfig->m_tabSpaces);
 
     if (p_event->modifiers() == Qt::NoModifier) {
@@ -419,7 +418,7 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
             m_autoIndentPos = -1;
             cursor.beginEditBlock();
             // Indent each selected line.
-            VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
+            VEditUtils::indentSelectedBlocks(cursor, text, true);
             cursor.endEditBlock();
             m_editor->setTextCursorW(cursor);
         } else {
@@ -457,9 +456,9 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
         m_autoIndentPos = -1;
         return false;
     }
-    QTextDocument *doc = m_editor->documentW();
+
     QTextCursor cursor = m_editor->textCursorW();
-    QTextBlock block = doc->findBlock(cursor.selectionStart());
+    QTextBlock block = m_editor->documentW()->findBlock(cursor.selectionStart());
     bool continueAutoIndent = false;
     int seq = -1;
     if (cursor.position() == m_autoIndentPos
@@ -473,7 +472,7 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
         changeListBlockSeqNumber(block, 1);
     }
 
-    VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
+    VEditUtils::indentSelectedBlocks(cursor, m_editConfig->m_tabSpaces, false);
     cursor.endEditBlock();
 
     if (continueAutoIndent) {