vim-mode: fix movement with block cursor

This commit is contained in:
Le Tan 2017-12-18 21:54:02 +08:00
parent f6cf98c827
commit d46917d6a9
5 changed files with 123 additions and 25 deletions

View File

@ -283,6 +283,7 @@ bool VEditUtils::findTargetWithinBlock(QTextCursor &p_cursor,
QChar p_target, QChar p_target,
bool p_forward, bool p_forward,
bool p_inclusive, bool p_inclusive,
bool p_leftSideOfCursor,
int p_repeat) int p_repeat)
{ {
if (p_repeat < 1) { if (p_repeat < 1) {
@ -296,6 +297,9 @@ bool VEditUtils::findTargetWithinBlock(QTextCursor &p_cursor,
// The index to start searching. // The index to start searching.
int idx = pib + (p_inclusive ? delta : 2 * delta); int idx = pib + (p_inclusive ? delta : 2 * delta);
if (p_leftSideOfCursor) {
--idx;
}
for (; idx < text.size() && idx >= 0; idx += delta) { for (; idx < text.size() && idx >= 0; idx += delta) {
if (text[idx] == p_target) { if (text[idx] == p_target) {
@ -322,9 +326,9 @@ bool VEditUtils::findTargetWithinBlock(QTextCursor &p_cursor,
} }
int VEditUtils::findTargetsWithinBlock(QTextCursor &p_cursor, int VEditUtils::findTargetsWithinBlock(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode,
const QList<QChar> &p_targets, const QList<QChar> &p_targets,
bool p_forward, bool p_forward,
bool p_leftSideOfCursor,
bool p_inclusive) bool p_inclusive)
{ {
if (p_targets.isEmpty()) { if (p_targets.isEmpty()) {
@ -339,6 +343,9 @@ int VEditUtils::findTargetsWithinBlock(QTextCursor &p_cursor,
// The index to start searching. // The index to start searching.
int idx = pib + (p_inclusive ? delta : 2 * delta); int idx = pib + (p_inclusive ? delta : 2 * delta);
if (p_leftSideOfCursor) {
--idx;
}
for (; idx < text.size() && idx >= 0; idx += delta) { for (; idx < text.size() && idx >= 0; idx += delta) {
int index = p_targets.indexOf(text[idx]); int index = p_targets.indexOf(text[idx]);
@ -353,14 +360,11 @@ int VEditUtils::findTargetsWithinBlock(QTextCursor &p_cursor,
} }
// text[idx] is the target character. // text[idx] is the target character.
if ((p_forward && p_inclusive && p_mode == QTextCursor::KeepAnchor) if (p_forward && !p_inclusive) {
|| (!p_forward && !p_inclusive)) {
++idx;
} else if (p_forward && !p_inclusive && p_mode == QTextCursor::MoveAnchor) {
--idx; --idx;
} }
p_cursor.setPosition(block.position() + idx, p_mode); p_cursor.setPosition(block.position() + idx);
return targetIdx; return targetIdx;
} }

View File

@ -89,17 +89,18 @@ public:
QChar p_target, QChar p_target,
bool p_forward, bool p_forward,
bool p_inclusive, bool p_inclusive,
bool p_leftSideOfCursor,
int p_repeat); int p_repeat);
// Find th first occurence of a char in @p_targets within a block. // Find th first occurence of a char in @p_targets within a block.
// Returns the index of the found char in @p_targets if found. // Returns the index of the found char in @p_targets if found.
// Returns -1 if none of the @p_targets is found. // Returns -1 if none of the @p_targets is found.
// Please pay attention to the one-step-forward/backward in KeepAnchor mode // Different from findTargetWithinBlock(), will not modify the ucrsor position
// and exclusive case. // even in KeepAnchor mode.
static int findTargetsWithinBlock(QTextCursor &p_cursor, static int findTargetsWithinBlock(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode,
const QList<QChar> &p_targets, const QList<QChar> &p_targets,
bool p_forward, bool p_forward,
bool p_leftSideOfCursor,
bool p_inclusive); bool p_inclusive);
// Find a pair target (@p_opening, @p_closing) containing current cursor and // Find a pair target (@p_opening, @p_closing) containing current cursor and

View File

@ -178,7 +178,8 @@ static void findCurrentSpace(const QTextCursor &p_cursor, int &p_start, int &p_e
// Backward: wwww|ssssswwww // Backward: wwww|ssssswwww
static void moveCursorAcrossSpaces(QTextCursor &p_cursor, static void moveCursorAcrossSpaces(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode, QTextCursor::MoveMode p_mode,
bool p_forward) bool p_forward,
bool p_stopAtBoundary = false)
{ {
while (true) { while (true) {
QTextBlock block = p_cursor.block(); QTextBlock block = p_cursor.block();
@ -192,7 +193,7 @@ static void moveCursorAcrossSpaces(QTextCursor &p_cursor,
} }
} }
if (pib == text.size()) { if (pib == text.size() && !p_stopAtBoundary) {
// Move to next block. // Move to next block.
p_cursor.movePosition(QTextCursor::Down, p_mode, 1); p_cursor.movePosition(QTextCursor::Down, p_mode, 1);
if (block.blockNumber() == p_cursor.block().blockNumber()) { if (block.blockNumber() == p_cursor.block().blockNumber()) {
@ -218,7 +219,7 @@ static void moveCursorAcrossSpaces(QTextCursor &p_cursor,
} }
} }
if (idx == -1) { if (idx == -1 && !p_stopAtBoundary) {
// Move to previous block. // Move to previous block.
p_cursor.movePosition(QTextCursor::Up, p_mode, 1); p_cursor.movePosition(QTextCursor::Up, p_mode, 1);
if (block.blockNumber() == p_cursor.block().blockNumber()) { if (block.blockNumber() == p_cursor.block().blockNumber()) {
@ -2551,6 +2552,13 @@ bool VVim::processMovement(QTextCursor &p_cursor,
p_repeat = 1; p_repeat = 1;
} }
if (checkMode(VimMode::Visual)) {
int pos = p_cursor.position();
if (pos == p_cursor.anchor() - 1 && pos == m_positionBeforeVisualMode) {
++p_repeat;
}
}
int pib = p_cursor.positionInBlock(); int pib = p_cursor.positionInBlock();
int length = p_cursor.block().length(); int length = p_cursor.block().length();
if (length - pib <= p_repeat) { if (length - pib <= p_repeat) {
@ -2730,6 +2738,12 @@ bool VVim::processMovement(QTextCursor &p_cursor,
// If all the block is space, just move to the end of block; otherwise, // If all the block is space, just move to the end of block; otherwise,
// move to the first non-space character. // move to the first non-space character.
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
break; break;
} }
@ -2752,6 +2766,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
} }
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
break; break;
} }
@ -2766,6 +2785,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1); p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
break; break;
} }
@ -2780,6 +2804,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1); p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
break; break;
} }
@ -2797,7 +2826,17 @@ bool VVim::processMovement(QTextCursor &p_cursor,
} }
p_cursor.movePosition(QTextCursor::NextWord, p_moveMode); p_cursor.movePosition(QTextCursor::NextWord, p_moveMode);
if (p_cursor.atBlockEnd() || VEditUtils::isSpaceBlock(p_cursor.block())) { if (p_cursor.atBlockEnd()) {
// dw/yw/cw will stop at the end of the line.
if (p_repeat == 1
&& checkMode(VimMode::Normal)
&& p_moveMode == QTextCursor::KeepAnchor) {
--p_repeat;
}
continue;
} else if (doc->characterAt(p_cursor.position()).isSpace()
|| VEditUtils::isSpaceBlock(p_cursor.block())) {
continue; continue;
} }
@ -2830,6 +2869,18 @@ bool VVim::processMovement(QTextCursor &p_cursor,
VEditUtils::findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to end of current WORD. // Move cursor to end of current WORD.
p_cursor.setPosition(end, p_moveMode); p_cursor.setPosition(end, p_moveMode);
if (p_repeat == 1
&& checkMode(VimMode::Normal)
&& p_moveMode == QTextCursor::KeepAnchor) {
// dW/yW/cW will stop at the end of the line.
moveCursorAcrossSpaces(p_cursor, p_moveMode, true, true);
if (p_cursor.atBlockEnd()) {
--p_repeat;
continue;
}
}
// Skip spaces. // Skip spaces.
moveCursorAcrossSpaces(p_cursor, p_moveMode, true); moveCursorAcrossSpaces(p_cursor, p_moveMode, true);
if (p_cursor.atBlockEnd()) { if (p_cursor.atBlockEnd()) {
@ -3193,6 +3244,7 @@ handle_target:
target, target,
forward, forward,
inclusive, inclusive,
useLeftSideOfCursor(p_cursor),
p_repeat); p_repeat);
} }
@ -3227,6 +3279,11 @@ handle_target:
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
} }
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
} }
@ -3251,15 +3308,19 @@ handle_target:
} }
// First check if current char hits the targets. // First check if current char hits the targets.
QChar ch = doc->characterAt(position); bool useLeftSideBefore = useLeftSideOfCursor(p_cursor);
QChar ch = doc->characterAt(useLeftSideBefore ? position - 1
: position);
int idx = targets.indexOf(ch); int idx = targets.indexOf(ch);
if (idx == -1) { if (idx == -1) {
// Use MoveAnchor to avoid the one-step-forward.
idx = VEditUtils::findTargetsWithinBlock(p_cursor, idx = VEditUtils::findTargetsWithinBlock(p_cursor,
QTextCursor::MoveAnchor,
targets, targets,
true, true,
useLeftSideOfCursor(p_cursor),
true); true);
} else if (useLeftSideBefore) {
// Move one character back to let p_cursor position at the pair.
p_cursor.movePosition(QTextCursor::PreviousCharacter, p_moveMode);
} }
if (idx == -1) { if (idx == -1) {
@ -3294,6 +3355,12 @@ handle_target:
p_cursor.setPosition(anchor); p_cursor.setPosition(anchor);
p_cursor.setPosition(target, p_moveMode); p_cursor.setPosition(target, p_moveMode);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
// Move one character forward.
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
hasMoved = true; hasMoved = true;
break; break;
} else { } else {
@ -3323,11 +3390,27 @@ handle_target:
// Record current location. // Record current location.
m_locations.addLocation(p_cursor); m_locations.addLocation(p_cursor);
bool useLeftSideBefore = useLeftSideOfCursor(p_cursor);
const SearchItem &item = m_searchHistory.lastItem(); const SearchItem &item = m_searchHistory.lastItem();
while (--p_repeat >= 0) { while (--p_repeat >= 0) {
hasMoved = m_editor->findText(item.m_text, item.m_options, bool found = m_editor->findText(item.m_text,
item.m_options,
forward ? item.m_forward : !item.m_forward, forward ? item.m_forward : !item.m_forward,
&p_cursor, p_moveMode); &p_cursor,
p_moveMode,
useLeftSideBefore);
if (found) {
hasMoved = true;
useLeftSideBefore = false;
} else {
break;
}
}
if (hasMoved) {
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
} }
break; break;
@ -3396,6 +3479,9 @@ handle_target:
} }
Q_ASSERT(hasMoved); Q_ASSERT(hasMoved);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
break; break;
} }

View File

@ -424,7 +424,8 @@ bool VEditor::findText(const QString &p_text,
uint p_options, uint p_options,
bool p_forward, bool p_forward,
QTextCursor *p_cursor, QTextCursor *p_cursor,
QTextCursor::MoveMode p_moveMode) QTextCursor::MoveMode p_moveMode,
bool p_useLeftSideOfCursor)
{ {
clearIncrementalSearchedWordHighlight(); clearIncrementalSearchedWordHighlight();
@ -442,6 +443,10 @@ bool VEditor::findText(const QString &p_text,
start = p_forward ? p_cursor->position() + 1 : p_cursor->position(); start = p_forward ? p_cursor->position() + 1 : p_cursor->position();
} }
if (p_useLeftSideOfCursor) {
--start;
}
bool found = findTextHelper(p_text, p_options, p_forward, start, bool found = findTextHelper(p_text, p_options, p_forward, start,
wrapped, retCursor); wrapped, retCursor);
if (found) { if (found) {
@ -541,11 +546,12 @@ bool VEditor::findTextHelper(const QString &p_text,
} else if (p_start > m_document->characterCount()) { } else if (p_start > m_document->characterCount()) {
p_start = m_document->characterCount(); p_start = m_document->characterCount();
} }
}
QTextCursor startCursor = cursor; QTextCursor startCursor = cursor;
// Will clear the selection.
startCursor.setPosition(p_start); startCursor.setPosition(p_start);
setTextCursorW(startCursor); setTextCursorW(startCursor);
}
while (!found) { while (!found) {
if (useRegExp) { if (useRegExp) {

View File

@ -75,7 +75,8 @@ public:
uint p_options, uint p_options,
bool p_forward, bool p_forward,
QTextCursor *p_cursor = nullptr, QTextCursor *p_cursor = nullptr,
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor); QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor,
bool p_useLeftSideOfCursor = false);
void replaceText(const QString &p_text, void replaceText(const QString &p_text,
uint p_options, uint p_options,