editor: Fix inconsistent SelectPrevious behavior (#27695)

When starting a selection from only carets, the action
`editor::SelectPrevious` behaved in a manner inconsistent with
`editor::SelectNext` as well as equivalent keybinds in editors such as
VSCode, by selecting substrings of whole words matching the initially
selected string on subsequent triggers.

This fix brings the `select_previous` function in line with
`select_next_internal`by calling `select_match_ranges` (previously an
internal function of `select_next_internal`) in the same way it was
previously used in the function that exhibited expected behavior.

Furthermore, the relevant test was adapted to bring it in line with the
equivalent test for the `editor::SelectNext` action

Closes #24346

Release Notes:

- Fixed inconsistent SelectPrevious behavior
This commit is contained in:
Francisco Fernandes 2025-05-06 07:37:58 +01:00 committed by GitHub
parent 2eb10ab9fb
commit a07ba3c718
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 52 deletions

View file

@ -12129,6 +12129,28 @@ impl Editor {
}
}
fn select_match_ranges(
&mut self,
range: Range<usize>,
reversed: bool,
replace_newest: bool,
auto_scroll: Option<Autoscroll>,
window: &mut Window,
cx: &mut Context<Editor>,
) {
self.unfold_ranges(&[range.clone()], false, auto_scroll.is_some(), cx);
self.change_selections(auto_scroll, window, cx, |s| {
if replace_newest {
s.delete(s.newest_anchor().id);
}
if reversed {
s.insert_range(range.end..range.start);
} else {
s.insert_range(range);
}
});
}
pub fn select_next_match_internal(
&mut self,
display_map: &DisplaySnapshot,
@ -12137,28 +12159,6 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) -> Result<()> {
fn select_next_match_ranges(
this: &mut Editor,
range: Range<usize>,
reversed: bool,
replace_newest: bool,
auto_scroll: Option<Autoscroll>,
window: &mut Window,
cx: &mut Context<Editor>,
) {
this.unfold_ranges(&[range.clone()], false, auto_scroll.is_some(), cx);
this.change_selections(auto_scroll, window, cx, |s| {
if replace_newest {
s.delete(s.newest_anchor().id);
}
if reversed {
s.insert_range(range.end..range.start);
} else {
s.insert_range(range);
}
});
}
let buffer = &display_map.buffer_snapshot;
let mut selections = self.selections.all::<usize>(cx);
if let Some(mut select_next_state) = self.select_next_state.take() {
@ -12203,8 +12203,7 @@ impl Editor {
}
if let Some(next_selected_range) = next_selected_range {
select_next_match_ranges(
self,
self.select_match_ranges(
next_selected_range,
last_selection.reversed,
replace_newest,
@ -12262,8 +12261,7 @@ impl Editor {
selection.end = word_range.end.to_offset(display_map, Bias::Left);
selection.goal = SelectionGoal::None;
selection.reversed = false;
select_next_match_ranges(
self,
self.select_match_ranges(
selection.start..selection.end,
selection.reversed,
replace_newest,
@ -12428,17 +12426,14 @@ impl Editor {
}
if let Some(next_selected_range) = next_selected_range {
self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
if action.replace_newest {
s.delete(s.newest_anchor().id);
}
if last_selection.reversed {
s.insert_range(next_selected_range.end..next_selected_range.start);
} else {
s.insert_range(next_selected_range);
}
});
self.select_match_ranges(
next_selected_range,
last_selection.reversed,
action.replace_newest,
Some(Autoscroll::newest()),
window,
cx,
);
} else {
select_prev_state.done = true;
}
@ -12489,6 +12484,14 @@ impl Editor {
selection.end = word_range.end.to_offset(&display_map, Bias::Left);
selection.goal = SelectionGoal::None;
selection.reversed = false;
self.select_match_ranges(
selection.start..selection.end,
selection.reversed,
action.replace_newest,
Some(Autoscroll::newest()),
window,
cx,
);
}
if selections.len() == 1 {
let selection = selections
@ -12507,16 +12510,6 @@ impl Editor {
} else {
self.select_prev_state = None;
}
self.unfold_ranges(
&selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
false,
true,
cx,
);
self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
s.select(selections);
});
} else if let Some(selected_text) = selected_text {
self.select_prev_state = Some(SelectNextState {
query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,

View file

@ -6130,11 +6130,7 @@ async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
.unwrap();
cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
.unwrap();
cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
}
#[gpui::test]