diff options
author | Mike Trinkala | 2023-02-07 23:05:27 +0000 |
---|---|---|
committer | GitHub | 2023-02-07 23:05:27 +0000 |
commit | c704701714236f9de9fdb03823b6adb9227be744 (patch) | |
tree | 454fb4e39a46671681e81abdf5c24212a41a84b8 /helix-core/src | |
parent | 23ed8c12f17c28ee888b5560d0ab2a9f9cd74dc9 (diff) |
Short-circuit the word and treesitter object movement commands (#5851)
The loop always iterates the number of times the user specified even
if the beginning/end of the document is reached.
For an extreme demonstration try the following commands, Helix will
hang for several seconds.
100000000w
100000000]c
Diffstat (limited to 'helix-core/src')
-rw-r--r-- | helix-core/src/movement.rs | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs index 11c12a6f..8e6b6306 100644 --- a/helix-core/src/movement.rs +++ b/helix-core/src/movement.rs @@ -227,9 +227,15 @@ fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTar }; // Do the main work. - (0..count).fold(start_range, |r, _| { - slice.chars_at(r.head).range_to_target(target, r) - }) + let mut range = start_range; + for _ in 0..count { + let next_range = slice.chars_at(range.head).range_to_target(target, range); + if range == next_range { + break; + } + range = next_range; + } + range } pub fn move_prev_paragraph( @@ -251,6 +257,7 @@ pub fn move_prev_paragraph( let mut lines = slice.lines_at(line); lines.reverse(); let mut lines = lines.map(rope_is_line_ending).peekable(); + let mut last_line = line; for _ in 0..count { while lines.next_if(|&e| e).is_some() { line -= 1; @@ -258,6 +265,10 @@ pub fn move_prev_paragraph( while lines.next_if(|&e| !e).is_some() { line -= 1; } + if line == last_line { + break; + } + last_line = line; } let head = slice.line_to_char(line); @@ -293,6 +304,7 @@ pub fn move_next_paragraph( line += 1; } let mut lines = slice.lines_at(line).map(rope_is_line_ending).peekable(); + let mut last_line = line; for _ in 0..count { while lines.next_if(|&e| !e).is_some() { line += 1; @@ -300,6 +312,10 @@ pub fn move_next_paragraph( while lines.next_if(|&e| e).is_some() { line += 1; } + if line == last_line { + break; + } + last_line = line; } let head = slice.line_to_char(line); let anchor = if behavior == Movement::Move { @@ -523,7 +539,14 @@ pub fn goto_treesitter_object( // head of range should be at beginning Some(Range::new(start_char, end_char)) }; - (0..count).fold(range, |range, _| get_range(range).unwrap_or(range)) + let mut last_range = range; + for _ in 0..count { + match get_range(last_range) { + Some(r) if r != last_range => last_range = r, + _ => break, + } + } + last_range } #[cfg(test)] |