diff --git a/crates/vim/src/helix.rs b/crates/vim/src/helix.rs index 314327b048..ad5cd37e3f 100644 --- a/crates/vim/src/helix.rs +++ b/crates/vim/src/helix.rs @@ -1004,8 +1004,9 @@ impl Vim { window: &mut Window, cx: &mut Context, ) { - let is_visual = self.mode.is_visual(); - let Some(data) = self.collect_helix_jump_data(is_visual, window, cx) else { + let allow_targets_in_selection = self.mode.has_selection(); + let Some(data) = self.collect_helix_jump_data(allow_targets_in_selection, window, cx) + else { return; }; @@ -1031,7 +1032,7 @@ impl Vim { fn collect_helix_jump_data( &mut self, - is_visual: bool, + allow_targets_in_selection: bool, window: &mut Window, cx: &mut Context, ) -> Option { @@ -1044,7 +1045,11 @@ impl Vim { let end_offset = buffer_snapshot.point_to_offset(visible_range.end); let selections = editor.selections.all::(&display_snapshot); - let skip_data = Self::selection_skip_offsets(buffer_snapshot, &selections, is_visual); + let skip_data = Self::selection_skip_offsets( + buffer_snapshot, + &selections, + allow_targets_in_selection, + ); // Get the primary cursor position for alternating forward/backward labeling let cursor_offset = selections @@ -1254,7 +1259,7 @@ impl Vim { fn selection_skip_offsets( buffer: &MultiBufferSnapshot, selections: &[Selection], - is_visual: bool, + allow_targets_in_selection: bool, ) -> HelixJumpSkipData { let mut skip_points = Vec::with_capacity(selections.len()); let mut skip_ranges = Vec::new(); @@ -1263,8 +1268,7 @@ impl Vim { let head_offset = buffer.point_to_offset(selection.head()); skip_points.push(head_offset); - // In visual mode, don't skip ranges so we can shrink the selection - if !is_visual && selection.start != selection.end { + if !allow_targets_in_selection && selection.start != selection.end { let mut start = buffer.point_to_offset(selection.start); let mut end = buffer.point_to_offset(selection.end); if start > end { @@ -3489,6 +3493,19 @@ mod test { assert_eq!(cx.active_operator(), None); } + #[gpui::test] + async fn test_helix_jump_includes_line_selection_targets(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.enable_helix(); + cx.set_state("alpha beta\nˇfoo bar baz\nqux quux", Mode::HelixNormal); + + cx.simulate_keystrokes("x"); + jump_to_word(&mut cx, "bar"); + + cx.assert_state("alpha beta\nfoo «barˇ» baz\nqux quux", Mode::HelixNormal); + assert_eq!(cx.active_operator(), None); + } + #[gpui::test] async fn test_vim_jump_moves_to_target_word_start(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 1c15c7d486..b92adf4cb5 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -80,6 +80,11 @@ impl Mode { matches!(self, Self::HelixNormal | Self::HelixSelect) } + /// `HelixNormal` qualifies because its cursor is itself a one-character selection. + pub fn has_selection(&self) -> bool { + self.is_visual() || matches!(self, Self::HelixNormal) + } + pub fn is_normal(&self) -> bool { matches!(self, Self::Normal | Self::HelixNormal) } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index b8e387e972..678b30d8ad 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -2233,7 +2233,7 @@ impl Vim { input_enabled: self.editor_input_enabled(), expects_character_input: self.expects_character_input(), autoindent: self.should_autoindent(), - cursor_offset_on_selection: self.mode.is_visual() || self.mode.is_helix(), + cursor_offset_on_selection: self.mode.has_selection(), line_mode: matches!(self.mode, Mode::VisualLine), hide_edit_predictions: !matches!(self.mode, Mode::Insert | Mode::Replace) && !(self.mode.is_normal()