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

View File

@ -89,17 +89,18 @@ public:
QChar p_target,
bool p_forward,
bool p_inclusive,
bool p_leftSideOfCursor,
int p_repeat);
// 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 -1 if none of the @p_targets is found.
// Please pay attention to the one-step-forward/backward in KeepAnchor mode
// and exclusive case.
// Different from findTargetWithinBlock(), will not modify the ucrsor position
// even in KeepAnchor mode.
static int findTargetsWithinBlock(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode,
const QList<QChar> &p_targets,
bool p_forward,
bool p_leftSideOfCursor,
bool p_inclusive);
// 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
static void moveCursorAcrossSpaces(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode,
bool p_forward)
bool p_forward,
bool p_stopAtBoundary = false)
{
while (true) {
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.
p_cursor.movePosition(QTextCursor::Down, p_mode, 1);
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.
p_cursor.movePosition(QTextCursor::Up, p_mode, 1);
if (block.blockNumber() == p_cursor.block().blockNumber()) {
@ -2551,6 +2552,13 @@ bool VVim::processMovement(QTextCursor &p_cursor,
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 length = p_cursor.block().length();
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,
// move to the first non-space character.
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;
break;
}
@ -2752,6 +2766,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
}
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;
break;
}
@ -2766,6 +2785,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
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;
break;
}
@ -2780,6 +2804,11 @@ bool VVim::processMovement(QTextCursor &p_cursor,
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
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;
break;
}
@ -2797,7 +2826,17 @@ bool VVim::processMovement(QTextCursor &p_cursor,
}
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;
}
@ -2830,6 +2869,18 @@ bool VVim::processMovement(QTextCursor &p_cursor,
VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to end of current WORD.
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.
moveCursorAcrossSpaces(p_cursor, p_moveMode, true);
if (p_cursor.atBlockEnd()) {
@ -3193,6 +3244,7 @@ handle_target:
target,
forward,
inclusive,
useLeftSideOfCursor(p_cursor),
p_repeat);
}
@ -3227,6 +3279,11 @@ handle_target:
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;
}
@ -3251,15 +3308,19 @@ handle_target:
}
// 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);
if (idx == -1) {
// Use MoveAnchor to avoid the one-step-forward.
idx = VEditUtils::findTargetsWithinBlock(p_cursor,
QTextCursor::MoveAnchor,
targets,
true,
useLeftSideOfCursor(p_cursor),
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) {
@ -3294,6 +3355,12 @@ handle_target:
p_cursor.setPosition(anchor);
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;
break;
} else {
@ -3323,11 +3390,27 @@ handle_target:
// Record current location.
m_locations.addLocation(p_cursor);
bool useLeftSideBefore = useLeftSideOfCursor(p_cursor);
const SearchItem &item = m_searchHistory.lastItem();
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,
&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;
@ -3396,6 +3479,9 @@ handle_target:
}
Q_ASSERT(hasMoved);
if (!p_cursor.atEnd() && useLeftSideOfCursor(p_cursor)) {
p_cursor.movePosition(QTextCursor::NextCharacter, p_moveMode);
}
break;
}

View File

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

View File

@ -75,7 +75,8 @@ public:
uint p_options,
bool p_forward,
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,
uint p_options,