Revert "Keep selection in SwitchToHelixNormalMode (#41583)" (#42892)

Closes #ISSUE

Release Notes:

- Fixes vim "go to definition" making a selection
This commit is contained in:
Conrad Irwin
2025-11-17 11:01:34 -07:00
committed by GitHub
parent 46ad6c0bbb
commit 6bf5e92a25
18 changed files with 94 additions and 183 deletions
+4 -60
View File
@@ -450,7 +450,7 @@ impl Vim {
prior_selections,
prior_operator: self.operator_stack.last().cloned(),
prior_mode: self.mode,
is_helix_regex_search: true,
helix_select: true,
}
});
}
@@ -1278,24 +1278,6 @@ mod test {
cx.assert_state("«one ˇ»two", Mode::HelixSelect);
}
#[gpui::test]
async fn test_exit_visual_mode(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state("ˇone two", Mode::Normal);
cx.simulate_keystrokes("v w");
cx.assert_state("«one tˇ»wo", Mode::Visual);
cx.simulate_keystrokes("escape");
cx.assert_state("one ˇtwo", Mode::Normal);
cx.enable_helix();
cx.set_state("ˇone two", Mode::HelixNormal);
cx.simulate_keystrokes("v w");
cx.assert_state("«one ˇ»two", Mode::HelixSelect);
cx.simulate_keystrokes("escape");
cx.assert_state("«one ˇ»two", Mode::HelixNormal);
}
#[gpui::test]
async fn test_helix_select_regex(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
@@ -1315,47 +1297,9 @@ mod test {
cx.simulate_keystrokes("enter");
cx.assert_state("«oneˇ» two «oneˇ»", Mode::HelixNormal);
// TODO: change "search_in_selection" to not perform any search when in helix select mode with no selection
// cx.set_state("ˇstuff one two one", Mode::HelixNormal);
// cx.simulate_keystrokes("s o n e enter");
// cx.assert_state("ˇstuff one two one", Mode::HelixNormal);
}
#[gpui::test]
async fn test_helix_select_next_match(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state("ˇhello two one two one two one", Mode::Visual);
cx.simulate_keystrokes("/ o n e");
cx.simulate_keystrokes("enter");
cx.simulate_keystrokes("n n");
cx.assert_state("«hello two one two one two oˇ»ne", Mode::Visual);
cx.set_state("ˇhello two one two one two one", Mode::Normal);
cx.simulate_keystrokes("/ o n e");
cx.simulate_keystrokes("enter");
cx.simulate_keystrokes("n n");
cx.assert_state("hello two one two one two ˇone", Mode::Normal);
cx.set_state("ˇhello two one two one two one", Mode::Normal);
cx.simulate_keystrokes("/ o n e");
cx.simulate_keystrokes("enter");
cx.simulate_keystrokes("n g n g n");
cx.assert_state("hello two one two «one two oneˇ»", Mode::Visual);
cx.enable_helix();
cx.set_state("ˇhello two one two one two one", Mode::HelixNormal);
cx.simulate_keystrokes("/ o n e");
cx.simulate_keystrokes("enter");
cx.simulate_keystrokes("n n");
cx.assert_state("hello two one two one two «oneˇ»", Mode::HelixNormal);
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");
cx.assert_state("ˇhello two «oneˇ» two «oneˇ» two «oneˇ»", Mode::HelixSelect);
cx.set_state("ˇone two one", Mode::HelixNormal);
cx.simulate_keystrokes("s o n e enter");
cx.assert_state("ˇone two one", Mode::HelixNormal);
}
#[gpui::test]
+18 -27
View File
@@ -672,40 +672,31 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
impl Vim {
pub(crate) fn search_motion(&mut self, m: Motion, window: &mut Window, cx: &mut Context<Self>) {
let Motion::ZedSearchResult {
prior_selections,
new_selections,
if let Motion::ZedSearchResult {
prior_selections, ..
} = &m
else {
return;
};
match self.mode {
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
if !prior_selections.is_empty() {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| {
s.select_ranges(prior_selections.iter().cloned());
{
match self.mode {
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
if !prior_selections.is_empty() {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| {
s.select_ranges(prior_selections.iter().cloned())
})
});
});
}
}
self.motion(m, window, cx);
}
Mode::Normal | Mode::Replace | Mode::Insert => {
if self.active_operator().is_some() {
self.motion(m, window, cx);
Mode::Normal | Mode::Replace | Mode::Insert => {
if self.active_operator().is_none() {
return;
}
}
}
Mode::HelixNormal => {}
Mode::HelixSelect => {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| {
s.select_ranges(prior_selections.iter().chain(new_selections).cloned());
});
});
Mode::HelixNormal | Mode::HelixSelect => {}
}
}
self.motion(m, window, cx)
}
pub(crate) fn motion(&mut self, motion: Motion, window: &mut Window, cx: &mut Context<Self>) {
+7 -12
View File
@@ -1,6 +1,5 @@
use editor::{Editor, EditorSettings, VimFlavor};
use editor::{Editor, EditorSettings};
use gpui::{Action, Context, Window, actions};
use language::Point;
use schemars::JsonSchema;
use search::{BufferSearchBar, SearchOptions, buffer_search};
@@ -196,7 +195,7 @@ impl Vim {
prior_selections,
prior_operator: self.operator_stack.last().cloned(),
prior_mode,
is_helix_regex_search: false,
helix_select: false,
}
});
}
@@ -220,7 +219,7 @@ impl Vim {
let new_selections = self.editor_selections(window, cx);
let result = pane.update(cx, |pane, cx| {
let search_bar = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()?;
if self.search.is_helix_regex_search {
if self.search.helix_select {
search_bar.update(cx, |search_bar, cx| {
search_bar.select_all_matches(&Default::default(), window, cx)
});
@@ -241,8 +240,7 @@ impl Vim {
count = count.saturating_sub(1)
}
self.search.count = 1;
let collapse = !self.mode.is_helix();
search_bar.select_match(direction, count, collapse, window, cx);
search_bar.select_match(direction, count, window, cx);
search_bar.focus_editor(&Default::default(), window, cx);
let prior_selections: Vec<_> = self.search.prior_selections.drain(..).collect();
@@ -309,8 +307,7 @@ impl Vim {
if !search_bar.has_active_match() || !search_bar.show(window, cx) {
return false;
}
let collapse = !self.mode.is_helix();
search_bar.select_match(direction, count, collapse, window, cx);
search_bar.select_match(direction, count, window, cx);
true
})
});
@@ -319,7 +316,6 @@ impl Vim {
}
let new_selections = self.editor_selections(window, cx);
self.search_motion(
Motion::ZedSearchResult {
prior_selections,
@@ -385,8 +381,7 @@ impl Vim {
cx.spawn_in(window, async move |_, cx| {
search.await?;
search_bar.update_in(cx, |search_bar, window, cx| {
let collapse = editor::vim_flavor(cx) == Some(VimFlavor::Vim);
search_bar.select_match(direction, count, collapse, window, cx);
search_bar.select_match(direction, count, window, cx);
vim.update(cx, |vim, cx| {
let new_selections = vim.editor_selections(window, cx);
@@ -449,7 +444,7 @@ impl Vim {
cx.spawn_in(window, async move |_, cx| {
search.await?;
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_match(direction, 1, true, window, cx)
search_bar.select_match(direction, 1, window, cx)
})?;
anyhow::Ok(())
})
+2 -6
View File
@@ -67,16 +67,12 @@ impl Display for Mode {
}
impl Mode {
pub fn is_visual(self) -> bool {
pub fn is_visual(&self) -> bool {
match self {
Self::Visual | Self::VisualLine | Self::VisualBlock | Self::HelixSelect => true,
Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false,
}
}
pub fn is_helix(self) -> bool {
matches!(self, Mode::HelixNormal | Mode::HelixSelect)
}
}
#[derive(Clone, Debug, PartialEq)]
@@ -991,7 +987,7 @@ pub struct SearchState {
pub prior_selections: Vec<Range<Anchor>>,
pub prior_operator: Option<Operator>,
pub prior_mode: Mode,
pub is_helix_regex_search: bool,
pub helix_select: bool,
}
impl Operator {
+20
View File
@@ -1139,6 +1139,26 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
}
#[gpui::test]
async fn test_go_to_definition(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new_typescript(cx).await;
cx.set_state("const before = 2; console.log(beforˇe)", Mode::Normal);
let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
let mut go_to_request =
cx.set_request_handler::<lsp::request::GotoDefinition, _, _>(move |url, _, _| async move {
Ok(Some(lsp::GotoDefinitionResponse::Scalar(
lsp::Location::new(url.clone(), def_range),
)))
});
cx.simulate_keystrokes("g d");
go_to_request.next().await.unwrap();
cx.run_until_parked();
cx.assert_state("const ˇbefore = 2; console.log(before)", Mode::Normal);
}
#[perf]
#[gpui::test]
async fn test_remap(cx: &mut gpui::TestAppContext) {
+1
View File
@@ -59,6 +59,7 @@ impl VimTestContext {
prepare_provider: Some(true),
work_done_progress_options: Default::default(),
})),
definition_provider: Some(lsp::OneOf::Left(true)),
..Default::default()
},
cx,
+3 -1
View File
@@ -668,7 +668,7 @@ impl Vim {
editor,
cx,
|vim, _: &SwitchToHelixNormalMode, window, cx| {
vim.switch_mode(Mode::HelixNormal, true, window, cx)
vim.switch_mode(Mode::HelixNormal, false, window, cx)
},
);
Vim::action(editor, cx, |_, _: &PushForcedMotion, _, cx| {
@@ -954,6 +954,7 @@ impl Vim {
fn deactivate(editor: &mut Editor, cx: &mut Context<Editor>) {
editor.set_cursor_shape(CursorShape::Bar, cx);
editor.set_clip_at_line_ends(false, cx);
editor.set_collapse_matches(false);
editor.set_input_enabled(true);
editor.set_autoindent(true);
editor.selections.set_line_mode(false);
@@ -1929,6 +1930,7 @@ impl Vim {
self.update_editor(cx, |vim, editor, cx| {
editor.set_cursor_shape(vim.cursor_shape(cx), cx);
editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
editor.set_collapse_matches(true);
editor.set_input_enabled(vim.editor_input_enabled());
editor.set_autoindent(vim.should_autoindent());
editor
+6 -2
View File
@@ -847,6 +847,9 @@ impl Vim {
let mut start_selection = 0usize;
let mut end_selection = 0usize;
self.update_editor(cx, |_, editor, _| {
editor.set_collapse_matches(false);
});
if vim_is_normal {
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()
@@ -857,7 +860,7 @@ impl Vim {
}
// without update_match_index there is a bug when the cursor is before the first match
search_bar.update_match_index(window, cx);
search_bar.select_match(direction.opposite(), 1, false, window, cx);
search_bar.select_match(direction.opposite(), 1, window, cx);
});
}
});
@@ -875,7 +878,7 @@ impl Vim {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |search_bar, cx| {
search_bar.update_match_index(window, cx);
search_bar.select_match(direction, count, false, window, cx);
search_bar.select_match(direction, count, window, cx);
match_exists = search_bar.match_exists(window, cx);
});
}
@@ -902,6 +905,7 @@ impl Vim {
editor.change_selections(Default::default(), window, cx, |s| {
s.select_ranges([start_selection..end_selection]);
});
editor.set_collapse_matches(true);
});
match self.maybe_pop_operator() {