瀏覽代碼

vim-mode: fix w/W/e/E/b/B/ge/gE movement with block cursor

Le Tan 8 年之前
父節點
當前提交
70eb04ebd2
共有 4 個文件被更改,包括 300 次插入67 次删除
  1. 31 34
      src/resources/themes/v_pure/v_pure.mdhl
  2. 0 1
      src/utils/veditutils.h
  3. 264 31
      src/utils/vvim.cpp
  4. 5 1
      src/utils/vvim.h

+ 31 - 34
src/resources/themes/v_pure/v_pure.mdhl

@@ -43,7 +43,7 @@ background: c5cae9
 # [VNote] Vim insert mode cursor line background
 vim-insert-background: c5cae9
 # [VNote] Vim normal mode cursor line background
-vim-normal-background: bdbdbd
+vim-normal-background: c6c6c6
 # [VNote] Vim visual mode cursor line background
 vim-visual-background: 90caf9
 # [VNote] Vim replace mode cursor line background
@@ -130,42 +130,39 @@ font-family: Consolas, Monaco, Andale Mono, Monospace, Courier New
 # [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, color)
 # The last occurence of the same attribute takes effect
 # Could specify multiple attribute in one line
-hljs-comment: italic, 898989
-hljs-quote: italic, 898989
-hljs-doctag: a626a4
-hljs-keyword: a626a4
-hljs-formula: a626a4
-hljs-section: e45649
-hljs-name: e45649
-hljs-selector-tag: e45649
-hljs-deletion: e45649
-hljs-subst: e45649
-hljs-literal: 0184bb
-hljs-string: 50a1f4
-hljs-regexp: 50a1f4
-hljs-addition: 50a1f4
-hljs-attribute: 50a1f4
-hljs-meta-string: 50a1f4
-hljs-built_in: c18401
-hljs-attr: 986801
-hljs-variable: 986801
-hljs-template-variable: 986801
-hljs-type: 986801
-hljs-selector-class: 986801
-hljs-selector-attr: 986801
-hljs-selector-pseudo: 986801
-hljs-number: 986801
-hljs-symbol: 4078f2
-hljs-bullet: 4078f2
-hljs-link: underlined, 4078f2
-hljs-meta: 4078f2
-hljs-selector-id: 4078f2
-hljs-title: 4078f2
-hljs-emphasis: italic
-hljs-strong: bold
+hljs-comment: 6c6c6c
+hljs-keyword: 0000ee
+hljs-attribute: 0000ee
+hljs-selector-tag: 0000ee
 hljs-meta-keyword: 0000ee
+hljs-doctag: 0000ee
+hljs-name: 0000ee
+hljs-type: 880000
+hljs-string: 880000
+hljs-number: 880000
+hljs-selector-id: 880000
+hljs-selector-class: 880000
+hljs-quote: 880000
 hljs-template-tag: 880000
+hljs-deletion: 880000
+hljs-title: bold, 880000
+hljs-section: bold, 880000
+hljs-regexp: bc6060
+hljs-symbol: bc6060
+hljs-variable: bc6060
+hljs-template-variable: bc6060
+hljs-link: bc6060
+hljs-selector-attr: bc6060
+hljs-selector-pseudo: bc6060
+hljs-literal: af00d7
+hljs-built_in: 008700
+hljs-bullet: 008700
 hljs-code: 008700
+hljs-addition: 008700
+hljs-meta: 1f7199
+hljs-meta-string: 4d99bf
+hljs-emphasis: italic
+hljs-strong: bold
 
 BLOCKQUOTE
 foreground: 00af00

+ 0 - 1
src/utils/veditutils.h

@@ -176,7 +176,6 @@ public:
     // 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.
-    // Attention: www|sss will select www, which is different from findCurrentWord().
     static void findCurrentWORD(const QTextCursor &p_cursor,
                                 int &p_start,
                                 int &p_end);

+ 264 - 31
src/utils/vvim.cpp

@@ -2273,7 +2273,7 @@ void VVim::setMode(VimMode p_mode, bool p_clearSelection, int p_position)
         VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
         switch (m_mode) {
         case VimMode::Insert:
-            m_editor->setCursorBlockModeW(CursorBlock::None);
+            setCursorBlockMode(m_editor, CursorBlock::None);
             if (mdEditor) {
                 mdEditor->setHighlightCursorLineBlockEnabled(false);
             }
@@ -2285,7 +2285,7 @@ void VVim::setMode(VimMode p_mode, bool p_clearSelection, int p_position)
             V_FALLTHROUGH;
 
         default:
-            m_editor->setCursorBlockModeW(CursorBlock::RightSide);
+            setCursorBlockMode(m_editor, CursorBlock::RightSide);
             if (mdEditor) {
                 QString color;
                 if (m_mode == VimMode::Normal) {
@@ -2777,7 +2777,24 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        p_cursor.movePosition(QTextCursor::NextWord, p_moveMode, p_repeat);
+        while (p_repeat) {
+            if (p_cursor.atEnd()) {
+                break;
+            }
+
+            p_cursor.movePosition(QTextCursor::NextWord, p_moveMode);
+            if (p_cursor.atBlockEnd() || VEditUtils::isSpaceBlock(p_cursor.block())) {
+                continue;
+            }
+
+            --p_repeat;
+        }
+
+        if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
+            // Move one character forward.
+            p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
+        }
+
         hasMoved = true;
         break;
     }
@@ -2789,16 +2806,28 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        for (int i = 0; i < p_repeat; ++i) {
+        while (p_repeat) {
+            if (p_cursor.atEnd()) {
+                break;
+            }
+
             int start, end;
             // [start, end] is current WORD.
             VEditUtils::findCurrentWORD(p_cursor, start, end);
-
             // Move cursor to end of current WORD.
             p_cursor.setPosition(end, p_moveMode);
-
             // Skip spaces.
             moveCursorAcrossSpaces(p_cursor, p_moveMode, true);
+            if (p_cursor.atBlockEnd()) {
+                continue;
+            }
+
+            --p_repeat;
+        }
+
+        if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
+            // Move one character forward.
+            p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
         }
 
         hasMoved = true;
@@ -2813,16 +2842,45 @@ bool VVim::processMovement(QTextCursor &p_cursor,
         }
 
         int pos = p_cursor.position();
+        bool leftSideBefore = useLeftSideOfCursor(p_cursor);
         // First move to the end of current word.
         p_cursor.movePosition(QTextCursor::EndOfWord, p_moveMode, 1);
-        if (pos != p_cursor.position()) {
-            // We did move.
-            p_repeat -= 1;
+        if (p_cursor.position() > pos) {
+            if (p_cursor.position() > pos + 1 || (leftSideBefore && useLeftSideOfCursor(p_cursor))) {
+                // We did move.
+                p_repeat -= 1;
+            }
         }
 
-        if (p_repeat) {
-            p_cursor.movePosition(QTextCursor::NextWord, p_moveMode, p_repeat);
+        while (p_repeat) {
+            if (p_cursor.atEnd()) {
+                break;
+            }
+
+            pos = p_cursor.position();
             p_cursor.movePosition(QTextCursor::EndOfWord, p_moveMode);
+            if (p_cursor.position() == pos) {
+                // Need to move to the start of next word.
+                p_cursor.movePosition(QTextCursor::NextWord, p_moveMode);
+                if (p_cursor.atBlockEnd()) {
+                    continue;
+                }
+
+                p_cursor.movePosition(QTextCursor::EndOfWord, p_moveMode);
+                if (p_cursor.atBlockStart()) {
+                    continue;
+                }
+            }
+
+            --p_repeat;
+        }
+
+        // Move one character back.
+        if (!p_cursor.atBlockStart()) {
+            if (p_moveMode == QTextCursor::MoveAnchor
+                || (checkMode(VimMode::Visual) && !useLeftSideOfCursor(p_cursor))) {
+                p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+            }
         }
 
         hasMoved = true;
@@ -2836,9 +2894,18 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        for (int i = 0; i < p_repeat; ++i) {
+        int pos = p_cursor.position();
+        bool leftSideBefore = useLeftSideOfCursor(p_cursor);
+        while (p_repeat) {
+            if (p_cursor.atEnd()) {
+                break;
+            }
+
             // Skip spaces.
             moveCursorAcrossSpaces(p_cursor, p_moveMode, true);
+            if (p_cursor.atBlockEnd()) {
+                continue;
+            }
 
             int start, end;
             // [start, end] is current WORD.
@@ -2846,6 +2913,19 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
             // Move cursor to the end of current WORD.
             p_cursor.setPosition(end, p_moveMode);
+
+            if (p_cursor.position() > pos + 1
+                || (leftSideBefore && useLeftSideOfCursor(p_cursor))) {
+                --p_repeat;
+            }
+        }
+
+        // Move one character back.
+        if (!p_cursor.atBlockStart()) {
+            if (p_moveMode == QTextCursor::MoveAnchor
+                || (checkMode(VimMode::Visual) && !useLeftSideOfCursor(p_cursor))) {
+                p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+            }
         }
 
         hasMoved = true;
@@ -2861,14 +2941,46 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
         int pos = p_cursor.position();
         // first move to the start of current word.
-        p_cursor.movePosition(QTextCursor::StartOfWord, p_moveMode, 1);
-        if (pos != p_cursor.position()) {
-            // We did move.
-            p_repeat -= 1;
+        p_cursor.movePosition(QTextCursor::StartOfWord, p_moveMode);
+        if (p_cursor.position() == pos && useLeftSideOfCursor(p_cursor)) {
+            // Cursor did not move and now is at the start of a word.
+            // Actually we are using the left side character so we need to move
+            // to previous word.
+            p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode);
         }
 
-        if (p_repeat) {
-            p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode, p_repeat);
+        if (p_cursor.position() < pos) {
+            if (p_cursor.position() < pos - 1 || !useLeftSideOfCursor(p_cursor)) {
+                // We did move.
+                p_repeat -= 1;
+            }
+        }
+
+        while (p_repeat) {
+            if (p_cursor.atStart()) {
+                break;
+            }
+
+            pos = p_cursor.position();
+            p_cursor.movePosition(QTextCursor::StartOfWord, p_moveMode);
+            if (p_cursor.position() == pos) {
+                // Need to move to the start of previous word.
+                p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode);
+                if (p_cursor.atBlockEnd()) {
+                    continue;
+                }
+
+                if (p_cursor.atBlockStart() && doc->characterAt(p_cursor.position()).isSpace()) {
+                    continue;
+                }
+            }
+
+            --p_repeat;
+        }
+
+        if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
+            // Move one character forward.
+            p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
         }
 
         hasMoved = true;
@@ -2882,9 +2994,19 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        for (int i = 0; i < p_repeat; ++i) {
+        int pos = p_cursor.position();
+        while (p_repeat) {
+            if (p_cursor.atStart()) {
+                break;
+            }
+
             // Skip Spaces.
             moveCursorAcrossSpaces(p_cursor, p_moveMode, false);
+            if (p_cursor.atBlockStart()) {
+                continue;
+            }
+
+            p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
 
             int start, end;
             // [start, end] is current WORD.
@@ -2892,6 +3014,15 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
             // Move cursor to the start of current WORD.
             p_cursor.setPosition(start, p_moveMode);
+
+            if (p_cursor.position() < pos - 1 || !useLeftSideOfCursor(p_cursor)) {
+                --p_repeat;
+            }
+        }
+
+        if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
+            // Move one character forward.
+            p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
         }
 
         hasMoved = true;
@@ -2905,13 +3036,55 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        int pib = p_cursor.positionInBlock();
-        if (!(pib > 0 && p_cursor.block().text()[pib -1].isSpace())) {
-            ++p_repeat;
+        if (useLeftSideOfCursor(p_cursor)) {
+            // Move one previous char.
+            p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+        }
+
+        int pos = p_cursor.position();
+        // Move across spaces backward.
+        moveCursorAcrossSpaces(p_cursor, p_moveMode, false);
+        int start, end;
+        VEditUtils::findCurrentWord(p_cursor, start, end);
+        if (pos != p_cursor.position() || start == end || start == pos) {
+            // We are alreay at the end of previous word.
+            --p_repeat;
+
+            // Move it to the start of current word.
+            p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode);
+        } else {
+            p_cursor.movePosition(QTextCursor::StartOfWord, p_moveMode);
+        }
+
+        while (p_repeat) {
+            if (p_cursor.atStart()) {
+                break;
+            }
+
+            p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode);
+            if (p_cursor.atBlockEnd()) {
+                continue;
+            }
+
+            if (p_cursor.atBlockStart() && doc->characterAt(p_cursor.position()).isSpace()) {
+                continue;
+            }
+
+            --p_repeat;
+        }
+
+        // Move it to the end.
+        p_cursor.movePosition(QTextCursor::EndOfWord, p_moveMode);
+
+        // Move one character back.
+        if (!p_cursor.atBlockStart()) {
+            if (p_moveMode == QTextCursor::MoveAnchor
+                || (checkMode(VimMode::Visual) && !useLeftSideOfCursor(p_cursor))
+                || p_cursor.position() <= p_cursor.anchor()) {
+                p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+            }
         }
 
-        p_cursor.movePosition(QTextCursor::PreviousWord, p_moveMode, p_repeat);
-        p_cursor.movePosition(QTextCursor::EndOfWord, p_moveMode, 1);
         hasMoved = true;
         break;
     }
@@ -2923,13 +3096,58 @@ bool VVim::processMovement(QTextCursor &p_cursor,
             p_repeat = 1;
         }
 
-        for (int i = 0; i < p_repeat; ++i) {
-            int start, end;
-            VEditUtils::findCurrentWORD(p_cursor, start, end);
+        if (useLeftSideOfCursor(p_cursor)) {
+            // Move one previous char.
+            p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+        }
 
+        int pos = p_cursor.position();
+        // Move across spaces backward.
+        moveCursorAcrossSpaces(p_cursor, p_moveMode, false);
+        int start, end;
+        VEditUtils::findCurrentWORD(p_cursor, start, end);
+        if (pos != p_cursor.position() || start == end) {
+            // We are alreay at the end of previous WORD.
+            --p_repeat;
+
+            // Move it to the start of current WORD.
+            p_cursor.setPosition(start, p_moveMode);
+        } else {
+            // Move it to the start of current WORD.
             p_cursor.setPosition(start, p_moveMode);
+        }
+
+        while (p_repeat) {
+            if (p_cursor.atStart()) {
+                break;
+            }
 
             moveCursorAcrossSpaces(p_cursor, p_moveMode, false);
+            if (p_cursor.atBlockStart()) {
+                continue;
+            }
+
+            if (p_cursor.atBlockStart() && doc->characterAt(p_cursor.position()).isSpace()) {
+                continue;
+            }
+
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
+            p_cursor.setPosition(start, p_moveMode);
+
+            --p_repeat;
+        }
+
+        // Move it to the end.
+        VEditUtils::findCurrentWORD(p_cursor, start, end);
+        p_cursor.setPosition(end, p_moveMode);
+
+        // Move one character back.
+        if (!p_cursor.atBlockStart()) {
+            if (p_moveMode == QTextCursor::MoveAnchor
+                || (checkMode(VimMode::Visual) && !useLeftSideOfCursor(p_cursor))
+                || p_cursor.position() <= p_cursor.anchor()) {
+                p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
+            }
         }
 
         hasMoved = true;
@@ -4940,7 +5158,7 @@ void VVim::maintainSelectionInVisualMode(QTextCursor *p_cursor)
             hasChanged = true;
         }
 
-        m_editor->setCursorBlockModeW(CursorBlock::LeftSide);
+        setCursorBlockMode(m_editor, CursorBlock::LeftSide);
     } else if (pos == anchor) {
         Q_ASSERT(anchor >= m_positionBeforeVisualMode);
         // Re-select.
@@ -4949,13 +5167,13 @@ void VVim::maintainSelectionInVisualMode(QTextCursor *p_cursor)
             cursor->setPosition(pos, QTextCursor::KeepAnchor);
             hasChanged = true;
 
-            m_editor->setCursorBlockModeW(CursorBlock::RightSide);
+            setCursorBlockMode(m_editor, CursorBlock::RightSide);
         } else {
             cursor->setPosition(m_positionBeforeVisualMode);
             cursor->setPosition(pos, QTextCursor::KeepAnchor);
             hasChanged = true;
 
-            m_editor->setCursorBlockModeW(CursorBlock::LeftSide);
+            setCursorBlockMode(m_editor, CursorBlock::LeftSide);
         }
     } else {
         // Re-select.
@@ -4965,7 +5183,7 @@ void VVim::maintainSelectionInVisualMode(QTextCursor *p_cursor)
             hasChanged = true;
         }
 
-        m_editor->setCursorBlockModeW(CursorBlock::RightSide);
+        setCursorBlockMode(m_editor, CursorBlock::RightSide);
     }
 
     if (hasChanged && !p_cursor) {
@@ -6000,3 +6218,18 @@ void VVim::handleMouseMoved(QMouseEvent *p_event)
         maintainSelectionInVisualMode();
     }
 }
+
+void VVim::setCursorBlockMode(VEditor *p_cursor, CursorBlock p_mode)
+{
+    p_cursor->setCursorBlockModeW(p_mode);
+}
+
+bool VVim::useLeftSideOfCursor(const QTextCursor &p_cursor)
+{
+    if (!checkMode(VimMode::Visual)) {
+        return false;
+    }
+
+    Q_ASSERT(m_positionBeforeVisualMode >= 0);
+    return p_cursor.position() > m_positionBeforeVisualMode;
+}

+ 5 - 1
src/utils/vvim.h

@@ -816,6 +816,11 @@ private:
     // is always selected.
     void maintainSelectionInVisualMode(QTextCursor *p_cursor = NULL);
 
+    void setCursorBlockMode(VEditor *p_cursor, CursorBlock p_mode);
+
+    // Whether we should consider the left or right side of the cursor.
+    bool useLeftSideOfCursor(const QTextCursor &p_cursor);
+
     VEditor *m_editor;
     const VEditConfig *m_editConfig;
     VimMode m_mode;
@@ -881,5 +886,4 @@ inline void VVim::setRegister(QChar p_regName, const QString &p_val)
 {
     s_registers[p_regName].update(p_val);
 }
-
 #endif // VVIM_H