Skip to content

Commit

Permalink
Short-circuit the word and treesitter object movement commands (#5851)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
trink committed Feb 7, 2023
1 parent 23ed8c1 commit c704701
Showing 1 changed file with 27 additions and 4 deletions.
31 changes: 27 additions & 4 deletions helix-core/src/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -251,13 +257,18 @@ 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;
}
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);
Expand Down Expand Up @@ -293,13 +304,18 @@ 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;
}
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 {
Expand Down Expand Up @@ -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)]
Expand Down

0 comments on commit c704701

Please sign in to comment.