Stop unnecessary repeat cursor movements in Vim mode (#7641)

Fixes: #7605

When repeating some cursor movements in Vim mode (e.g. `99999999 w`),
Zed tries to repeat the movement that many times, even if further
actions don't have any effect. This causes Zed to hang.

This commit makes those movements like other actions (like moving the
cursor left/right), stopping the repeat movement if a boundary of the
text is reached/the cursor can't move anymore.

Release Notes:

- Fixed [#7605](https://github.com/zed-industries/zed/issues/7605).
This commit is contained in:
Colin Cai 2024-02-10 00:50:38 -05:00 committed by GitHub
parent 0304edd8ab
commit e2a3e89318
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -617,6 +617,9 @@ fn left(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> Display
fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint {
for _ in 0..times {
point = movement::left(map, point);
if point.is_zero() {
break;
}
}
point
}
@ -624,6 +627,9 @@ fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> Di
fn space(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint {
for _ in 0..times {
point = wrapping_right(map, point);
if point == map.max_point() {
break;
}
}
point
}
@ -768,7 +774,7 @@ pub(crate) fn next_word_start(
let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
for _ in 0..times {
let mut crossed_newline = false;
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
let new_point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
let at_newline = right == '\n';
@ -779,7 +785,11 @@ pub(crate) fn next_word_start(
crossed_newline |= at_newline;
found
})
});
if point == new_point {
break;
}
point = new_point;
}
point
}
@ -792,21 +802,30 @@ fn next_word_end(
) -> DisplayPoint {
let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
for _ in 0..times {
if point.column() < map.line_len(point.row()) {
*point.column_mut() += 1;
} else if point.row() < map.max_buffer_row() {
*point.row_mut() += 1;
*point.column_mut() = 0;
let mut new_point = point;
if new_point.column() < map.line_len(new_point.row()) {
*new_point.column_mut() += 1;
} else if new_point.row() < map.max_buffer_row() {
*new_point.row_mut() += 1;
*new_point.column_mut() = 0;
}
point =
movement::find_boundary_exclusive(map, point, FindRange::MultiLine, |left, right| {
let new_point = movement::find_boundary_exclusive(
map,
new_point,
FindRange::MultiLine,
|left, right| {
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});
point = map.clip_point(point, Bias::Left);
},
);
let new_point = map.clip_point(new_point, Bias::Left);
if point == new_point {
break;
}
point = new_point;
}
point
}
@ -821,13 +840,17 @@ fn previous_word_start(
for _ in 0..times {
// This works even though find_preceding_boundary is called for every character in the line containing
// cursor because the newline is checked only once.
point =
let new_point =
movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
(left_kind != right_kind && !right.is_whitespace()) || left == '\n'
});
if point == new_point {
break;
}
point = new_point;
}
point
}
@ -967,10 +990,14 @@ fn find_forward(
for _ in 0..times {
found = false;
to = find_boundary(map, to, FindRange::SingleLine, |_, right| {
let new_to = find_boundary(map, to, FindRange::SingleLine, |_, right| {
found = right == target;
found
});
if to == new_to {
break;
}
to = new_to;
}
if found {
@ -995,7 +1022,12 @@ fn find_backward(
let mut to = from;
for _ in 0..times {
to = find_preceding_boundary(map, to, FindRange::SingleLine, |_, right| right == target);
let new_to =
find_preceding_boundary(map, to, FindRange::SingleLine, |_, right| right == target);
if to == new_to {
break;
}
to = new_to;
}
if map.buffer_snapshot.chars_at(to.to_point(map)).next() == Some(target) {