From e2a3e893187bcf8477e8046b0073c2798e7b1c64 Mon Sep 17 00:00:00 2001 From: Colin Cai <104604363+Creative0708@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:50:38 -0500 Subject: [PATCH] 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). --- crates/vim/src/motion.rs | 60 ++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 99472dd1c85a3bfef17c1422deff3f8416911b2f..0c1e96942d726f7d76ac4f7a4150ea7e78ab68d9 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -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) {