Reset selection goal after helix motion (#33184)

Closes #33060

Motions like `NextWordStart` don't reset the selection goal in vim mode
`helix_normal` unlike in `normal` which can lead to the cursor jumping
back to the previous horizontal position after going up or down.

Release Notes:

- N/A
This commit is contained in:
fantacell 2025-06-27 06:32:35 +02:00 committed by GitHub
parent 8c9116daa5
commit fbb5628ec6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@ use editor::{DisplayPoint, Editor, movement, scroll::Autoscroll};
use gpui::{Action, actions}; use gpui::{Action, actions};
use gpui::{Context, Window}; use gpui::{Context, Window};
use language::{CharClassifier, CharKind}; use language::{CharClassifier, CharKind};
use text::SelectionGoal;
use crate::{Vim, motion::Motion, state::Mode}; use crate::{Vim, motion::Motion, state::Mode};
@ -49,43 +50,43 @@ impl Vim {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
let times = times.unwrap_or(1); let times = times.unwrap_or(1);
let new_goal = SelectionGoal::None;
let mut head = selection.head();
let mut tail = selection.tail();
if selection.head() == map.max_point() { if head == map.max_point() {
return; return;
} }
// collapse to block cursor // collapse to block cursor
if selection.tail() < selection.head() { if tail < head {
selection.set_tail(movement::left(map, selection.head()), selection.goal); tail = movement::left(map, head);
} else { } else {
selection.set_tail(selection.head(), selection.goal); tail = head;
selection.set_head(movement::right(map, selection.head()), selection.goal); head = movement::right(map, head);
} }
// create a classifier // create a classifier
let classifier = map let classifier = map.buffer_snapshot.char_classifier_at(head.to_point(map));
.buffer_snapshot
.char_classifier_at(selection.head().to_point(map));
let mut last_selection = selection.clone();
for _ in 0..times { for _ in 0..times {
let (new_tail, new_head) = let (maybe_next_tail, next_head) =
movement::find_boundary_trail(map, selection.head(), |left, right| { movement::find_boundary_trail(map, head, |left, right| {
is_boundary(left, right, &classifier) is_boundary(left, right, &classifier)
}); });
selection.set_head(new_head, selection.goal); if next_head == head && maybe_next_tail.unwrap_or(next_head) == tail {
if let Some(new_tail) = new_tail {
selection.set_tail(new_tail, selection.goal);
}
if selection.head() == last_selection.head()
&& selection.tail() == last_selection.tail()
{
break; break;
} }
last_selection = selection.clone();
head = next_head;
if let Some(next_tail) = maybe_next_tail {
tail = next_tail;
} }
}
selection.set_tail(tail, new_goal);
selection.set_head(head, new_goal);
}); });
}); });
}); });
@ -102,47 +103,50 @@ impl Vim {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
let times = times.unwrap_or(1); let times = times.unwrap_or(1);
let new_goal = SelectionGoal::None;
let mut head = selection.head();
let mut tail = selection.tail();
if selection.head() == DisplayPoint::zero() { if head == DisplayPoint::zero() {
return; return;
} }
// collapse to block cursor // collapse to block cursor
if selection.tail() < selection.head() { if tail < head {
selection.set_tail(movement::left(map, selection.head()), selection.goal); tail = movement::left(map, head);
} else { } else {
selection.set_tail(selection.head(), selection.goal); tail = head;
selection.set_head(movement::right(map, selection.head()), selection.goal); head = movement::right(map, head);
} }
selection.set_head(head, new_goal);
selection.set_tail(tail, new_goal);
// flip the selection // flip the selection
selection.swap_head_tail(); selection.swap_head_tail();
head = selection.head();
tail = selection.tail();
// create a classifier // create a classifier
let classifier = map let classifier = map.buffer_snapshot.char_classifier_at(head.to_point(map));
.buffer_snapshot
.char_classifier_at(selection.head().to_point(map));
let mut last_selection = selection.clone();
for _ in 0..times { for _ in 0..times {
let (new_tail, new_head) = movement::find_preceding_boundary_trail( let (maybe_next_tail, next_head) =
map, movement::find_preceding_boundary_trail(map, head, |left, right| {
selection.head(), is_boundary(left, right, &classifier)
|left, right| is_boundary(left, right, &classifier), });
);
selection.set_head(new_head, selection.goal); if next_head == head && maybe_next_tail.unwrap_or(next_head) == tail {
if let Some(new_tail) = new_tail {
selection.set_tail(new_tail, selection.goal);
}
if selection.head() == last_selection.head()
&& selection.tail() == last_selection.tail()
{
break; break;
} }
last_selection = selection.clone();
head = next_head;
if let Some(next_tail) = maybe_next_tail {
tail = next_tail;
} }
}
selection.set_tail(tail, new_goal);
selection.set_head(head, new_goal);
}); });
}) })
}); });