on master help sign in
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

commitc704701714236f9de9fdb03823b6adb9227be744 authorMike Trinkala <trink@acm.org> dateTue 7 Feb 2023, 15:05 (4 years ago) parent23ed8c1
1 file changed+27−4
helix-core/src/movement.rs+27−4
helix-core/src/movement.rs+27−4
@@ -227,9 +227,15 @@ fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTar
227227 };
228228
229229 // Do the main work.
230− (0..count).fold(start_range, |r, _| {
231− slice.chars_at(r.head).range_to_target(target, r)
232− })
230+ let mut range = start_range;
231+ for _ in 0..count {
232+ let next_range = slice.chars_at(range.head).range_to_target(target, range);
233+ if range == next_range {
234+ break;
235+ }
236+ range = next_range;
237+ }
238+ range
233239}
234240
235241pub fn move_prev_paragraph(
@@ -251,6 +257,7 @@ pub fn move_prev_paragraph(
251257 let mut lines = slice.lines_at(line);
252258 lines.reverse();
253259 let mut lines = lines.map(rope_is_line_ending).peekable();
260+ let mut last_line = line;
254261 for _ in 0..count {
255262 while lines.next_if(|&e| e).is_some() {
256263 line -= 1;
@@ -258,6 +265,10 @@ pub fn move_prev_paragraph(
258265 while lines.next_if(|&e| !e).is_some() {
259266 line -= 1;
260267 }
268+ if line == last_line {
269+ break;
270+ }
271+ last_line = line;
261272 }
262273
263274 let head = slice.line_to_char(line);
@@ -293,6 +304,7 @@ pub fn move_next_paragraph(
293304 line += 1;
294305 }
295306 let mut lines = slice.lines_at(line).map(rope_is_line_ending).peekable();
307+ let mut last_line = line;
296308 for _ in 0..count {
297309 while lines.next_if(|&e| !e).is_some() {
298310 line += 1;
@@ -300,6 +312,10 @@ pub fn move_next_paragraph(
300312 while lines.next_if(|&e| e).is_some() {
301313 line += 1;
302314 }
315+ if line == last_line {
316+ break;
317+ }
318+ last_line = line;
303319 }
304320 let head = slice.line_to_char(line);
305321 let anchor = if behavior == Movement::Move {
@@ -523,7 +539,14 @@ pub fn goto_treesitter_object(
523539 // head of range should be at beginning
524540 Some(Range::new(start_char, end_char))
525541 };
526− (0..count).fold(range, |range, _| get_range(range).unwrap_or(range))
542+ let mut last_range = range;
543+ for _ in 0..count {
544+ match get_range(last_range) {
545+ Some(r) if r != last_range => last_range = r,
546+ _ => break,
547+ }
548+ }
549+ last_range
527550}
528551
529552#[cfg(test)]