@@ -12,6 +12,7 @@ use editor::{
};
use gpui::actions;
use gpui::{Context, Window};
+use itertools::Itertools as _;
use language::{CharClassifier, CharKind, Point};
use search::{BufferSearchBar, SearchOptions};
use settings::Settings;
@@ -876,11 +877,22 @@ impl Vim {
self.update_editor(cx, |_vim, editor, cx| {
let snapshot = editor.snapshot(window, cx);
editor.change_selections(SelectionEffects::default(), window, cx, |s| {
+ let buffer = snapshot.buffer_snapshot();
+
s.select_anchor_ranges(
prior_selections
.iter()
.cloned()
- .chain(s.all_anchors(&snapshot).iter().map(|s| s.range())),
+ .chain(s.all_anchors(&snapshot).iter().map(|s| s.range()))
+ .sorted_by(|a, b| {
+ a.start
+ .cmp(&b.start, buffer)
+ .then_with(|| a.end.cmp(&b.end, buffer))
+ })
+ .dedup_by(|a, b| {
+ a.start.cmp(&b.start, buffer).is_eq()
+ && a.end.cmp(&b.end, buffer).is_eq()
+ }),
);
})
});
@@ -1670,6 +1682,25 @@ mod test {
cx.assert_state("hello two «oneˇ» two «oneˇ» two «oneˇ»", Mode::HelixSelect);
}
+ #[gpui::test]
+ async fn test_helix_select_next_match_wrapping(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+ cx.enable_helix();
+
+ // Three occurrences of "one". After selecting all three with `n n`,
+ // pressing `n` again wraps the search to the first occurrence.
+ // The prior selections (at higher offsets) are chained before the
+ // wrapped selection (at a lower offset), producing unsorted anchors
+ // that cause `rope::Cursor::summary` to panic with
+ // "cannot summarize backward".
+ cx.set_state("ˇhello two one two one two one", Mode::HelixSelect);
+ cx.simulate_keystrokes("/ o n e");
+ cx.simulate_keystrokes("enter");
+ cx.simulate_keystrokes("n n n");
+ // Should not panic; all three occurrences should remain selected.
+ cx.assert_state("hello two «oneˇ» two «oneˇ» two «oneˇ»", Mode::HelixSelect);
+ }
+
#[gpui::test]
async fn test_helix_substitute(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;