diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 99472dd1c8..0c1e96942d 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) {