Compare commits

...

1 Commits

Author SHA1 Message Date
Conrad Irwin
f8ae9d6e1f Fix vim mouse selections
Closes #27720
2025-12-17 17:29:09 -07:00
2 changed files with 67 additions and 12 deletions

View File

@@ -1303,6 +1303,39 @@ async fn test_mouse_selection(cx: &mut TestAppContext) {
cx.assert_state("one «ˇtwo» three", Mode::Visual)
}
#[gpui::test]
async fn test_mouse_drag_across_anchor_does_not_drift(cx: &mut TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state("ˇone two three four", Mode::Normal);
let click_pos = cx.pixel_position("one ˇtwo three four");
let drag_left = cx.pixel_position("ˇone two three four");
let anchor_pos = cx.pixel_position("one tˇwo three four");
cx.simulate_mouse_down(click_pos, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.simulate_mouse_move(drag_left, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.assert_state("«ˇone t»wo three four", Mode::Visual);
cx.simulate_mouse_move(anchor_pos, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.simulate_mouse_move(drag_left, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.assert_state("«ˇone t»wo three four", Mode::Visual);
cx.simulate_mouse_move(anchor_pos, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.simulate_mouse_move(drag_left, MouseButton::Left, Modifiers::none());
cx.run_until_parked();
cx.assert_state("«ˇone t»wo three four", Mode::Visual);
cx.simulate_mouse_up(drag_left, MouseButton::Left, Modifiers::none());
}
#[perf]
#[gpui::test]
async fn test_lowercase_marks(cx: &mut TestAppContext) {

View File

@@ -23,8 +23,9 @@ use crate::normal::paste::Paste as VimPaste;
use collections::HashMap;
use editor::{
Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, MultiBufferOffset,
SelectionEffects, ToPoint,
SelectionEffects,
actions::Paste,
display_map::ToDisplayPoint,
movement::{self, FindRange},
};
use gpui::{
@@ -37,6 +38,8 @@ use language::{
};
pub use mode_indicator::ModeIndicator;
use motion::Motion;
use multi_buffer::ToPoint as _;
use multi_buffer::ToPoint as _;
use normal::search::SearchSubmit;
use object::Object;
use schemars::JsonSchema;
@@ -503,6 +506,7 @@ pub(crate) struct Vim {
pub(crate) current_anchor: Option<Selection<Anchor>>,
pub(crate) undo_modes: HashMap<TransactionId, Mode>,
pub(crate) undo_last_line_tx: Option<TransactionId>,
extended_pending_selection_id: Option<usize>,
selected_register: Option<char>,
pub search: SearchState,
@@ -561,6 +565,7 @@ impl Vim {
current_tx: None,
undo_last_line_tx: None,
current_anchor: None,
extended_pending_selection_id: None,
undo_modes: HashMap::default(),
status_label: None,
@@ -1218,17 +1223,32 @@ impl Vim {
s.select_anchor_ranges(vec![pos..pos])
}
let snapshot = s.display_snapshot();
if let Some(pending) = s.pending_anchor_mut()
&& pending.reversed
let mut should_extend_pending = false;
if !last_mode.is_visual()
&& mode.is_visual()
&& !last_mode.is_visual()
&& let Some(pending) = s.pending_anchor()
{
let mut end = pending.end.to_point(&snapshot.buffer_snapshot());
end = snapshot
.buffer_snapshot()
.clip_point(end + Point::new(0, 1), Bias::Right);
pending.end = snapshot.buffer_snapshot().anchor_before(end);
let snapshot = s.display_snapshot();
let is_empty = pending
.start
.cmp(&pending.end, &snapshot.buffer_snapshot())
.is_eq();
should_extend_pending = pending.reversed
&& !is_empty
&& vim.extended_pending_selection_id != Some(pending.id);
};
if should_extend_pending {
let snapshot = s.display_snapshot();
if let Some(pending) = s.pending_anchor_mut() {
let end = pending.end.to_point(&snapshot.buffer_snapshot());
let end = end.to_display_point(&snapshot);
let new_end = movement::right(&snapshot, end);
pending.end = snapshot
.buffer_snapshot()
.anchor_before(new_end.to_point(&snapshot));
}
vim.extended_pending_selection_id = s.pending_anchor().map(|p| p.id)
}
s.move_with(|map, selection| {
@@ -1240,8 +1260,10 @@ impl Vim {
point = map.clip_point(point, Bias::Left);
}
selection.collapse_to(point, selection.goal)
} else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
selection.end = movement::right(map, selection.start);
} else if !last_mode.is_visual() && mode.is_visual() {
if selection.is_empty() {
selection.end = movement::right(map, selection.start);
}
}
});
})