From 9523168fc3894b454418f784ec01f0a1433412d5 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 2 Sep 2017 20:39:26 +0800 Subject: [PATCH] vim-mode: support S, { and } Thanks to xianzhon@github --- src/resources/docs/shortcuts_en.md | 4 +- src/resources/docs/shortcuts_zh.md | 4 +- src/utils/veditutils.cpp | 31 +++++++++++++ src/utils/veditutils.h | 6 +++ src/utils/vvim.cpp | 74 ++++++++++++++++++++++++++---- src/utils/vvim.h | 2 + 6 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/resources/docs/shortcuts_en.md b/src/resources/docs/shortcuts_en.md index 448a5d94..c2d6c5eb 100644 --- a/src/resources/docs/shortcuts_en.md +++ b/src/resources/docs/shortcuts_en.md @@ -161,9 +161,9 @@ VNote supports a simple but useful Vim mode, including **Normal**, **Insert**, * VNote supports following features of Vim: -- `r`, `s`, `i`, `I`, `a`, `A`, `o`, and `O`; +- `r`, `s`, `S`, `i`, `I`, `a`, `A`, `c`, `C`, `o`, and `O`; - Actions `d`, `c`, `y`, `p`, `<`, `>`, `gu`, `gU`, and `~`; -- Movements `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, and `$`; +- Movements `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, `{`, `}`, and `$`; - Marks `a-z`; - Registers `"`, `_`, `+`, `a-z`(`A-Z`); - Jump locations list (`Ctrl+O` and `Ctrl+I`); diff --git a/src/resources/docs/shortcuts_zh.md b/src/resources/docs/shortcuts_zh.md index 1bba0c03..0fea1434 100644 --- a/src/resources/docs/shortcuts_zh.md +++ b/src/resources/docs/shortcuts_zh.md @@ -162,9 +162,9 @@ VNote支持一个简单但有用的Vim模式,包括 **正常**, **插入** VNote支持以下几个Vim的特性: -- `r`, `s`, `i`, `I`, `a`, `A`, `o`, `O`; +- `r`, `s`, `S`, `i`, `I`, `a`, `A`, `c`, `C`, `o`, `O`; - 操作 `d`, `c`, `y`, `p`, `<`, `>`, `gu`, `gU`, `~`; -- 移动 `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, `$`; +- 移动 `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, `{`, `}`, `$`; - 标记 `a-z`; - 寄存器 `"`, `_`, `+`, `a-z`(`A-Z`); - 跳转位置列表 (`Ctrl+O` and `Ctrl+I`); diff --git a/src/utils/veditutils.cpp b/src/utils/veditutils.cpp index fee0f7d1..325b23f1 100644 --- a/src/utils/veditutils.cpp +++ b/src/utils/veditutils.cpp @@ -639,3 +639,34 @@ round: p_cursor.setPosition(closing, QTextCursor::KeepAnchor); return true; } + +int VEditUtils::findNextEmptyBlock(const QTextCursor &p_cursor, + bool p_forward, + int p_repeat) +{ + Q_ASSERT(p_repeat > 0); + int res = -1; + QTextBlock block = p_cursor.block(); + if (p_forward) { + block = block.next(); + } else { + block = block.previous(); + } + + while (block.isValid()) { + if (block.length() == 1) { + res = block.position(); + if (--p_repeat == 0) { + break; + } + } + + if (p_forward) { + block = block.next(); + } else { + block = block.previous(); + } + } + + return p_repeat > 0 ? -1 : res; +} diff --git a/src/utils/veditutils.h b/src/utils/veditutils.h index 407a5dcb..eaf13a1b 100644 --- a/src/utils/veditutils.h +++ b/src/utils/veditutils.h @@ -124,6 +124,12 @@ public: // Need to call setTextCursor() to make it take effect. static void deleteIndentAndListMark(QTextCursor &p_cursor); + // Find next @p_repeat empty block. + // Returns the position of that block if found. Otherwise, returns -1. + static int findNextEmptyBlock(const QTextCursor &p_cursor, + bool p_forward, + int p_repeat); + private: VEditUtils() {} }; diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index 92e43f5b..fa19d27c 100644 --- a/src/utils/vvim.cpp +++ b/src/utils/vvim.cpp @@ -1030,6 +1030,17 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos) } setMode(VimMode::Insert); + } else if (modifiers == Qt::ShiftModifier) { + // S, change current line. + tryGetRepeatToken(m_keys, m_tokens); + if (hasActionToken() || !m_keys.isEmpty()) { + // Invalid sequence. + break; + } + + addActionToken(Action::Change); + addRangeToken(Range::Line); + processCommand(m_tokens); } break; @@ -2048,12 +2059,17 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos) addRangeToken(range); processCommand(m_tokens); break; - } else if (hasActionToken() || !checkMode(VimMode::Normal)) { - // Invalid sequence. - break; - } else if (checkPendingKey(Key(Qt::Key_BracketLeft))) { + } else if (m_keys.isEmpty()) { + // {, ParagraphUp movement. + tryAddMoveAction(); + addMovementToken(Movement::ParagraphUp); + processCommand(m_tokens); + } else if (!hasActionToken() + && checkPendingKey(Key(Qt::Key_BracketLeft))) { // [{, goto previous title at one higher level. - processTitleJump(m_tokens, false, -1); + if (checkMode(VimMode::Normal)) { + processTitleJump(m_tokens, false, -1); + } } break; @@ -2077,12 +2093,17 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos) addRangeToken(range); processCommand(m_tokens); break; - } else if (hasActionToken() || !checkMode(VimMode::Normal)) { - // Invalid sequence. - break; - } else if (checkPendingKey(Key(Qt::Key_BracketRight))) { + } else if (m_keys.isEmpty()) { + // }, ParagraphDown movement. + tryAddMoveAction(); + addMovementToken(Movement::ParagraphDown); + processCommand(m_tokens); + } else if (!hasActionToken() + && checkPendingKey(Key(Qt::Key_BracketRight))) { // ]}, goto next title at one higher level. - processTitleJump(m_tokens, true, -1); + if (checkMode(VimMode::Normal)) { + processTitleJump(m_tokens, true, -1); + } } break; @@ -3101,6 +3122,36 @@ handle_target: break; } + case Movement::ParagraphUp: + forward = false; + // Fall through. + case Movement::ParagraphDown: + { + if (p_repeat == -1) { + p_repeat = 1; + } + + // Record current location. + m_locations.addLocation(p_cursor); + + int oriPos = p_cursor.position(); + + int position = VEditUtils::findNextEmptyBlock(p_cursor, + forward, + p_repeat); + if (position == -1) { + // No empty block. Move to the first/last character. + p_cursor.movePosition(forward ? QTextCursor::End : QTextCursor::Start, + p_moveMode); + hasMoved = p_cursor.position() != oriPos; + } else { + p_cursor.setPosition(position, p_moveMode); + hasMoved = true; + } + + break; + } + default: break; } @@ -3575,6 +3626,9 @@ void VVim::processDeleteAction(QList &p_tokens) break; } + // ParagraphUp and ParagraphDown are a little different from Vim in + // block deletion. + default: break; } diff --git a/src/utils/vvim.h b/src/utils/vvim.h index 9092f1d7..69973e8f 100644 --- a/src/utils/vvim.h +++ b/src/utils/vvim.h @@ -415,6 +415,8 @@ private: FindPrevious, FindNextWordUnderCursor, FindPreviousWordUnderCursor, + ParagraphUp, + ParagraphDown, Invalid };