@@ -447,6 +447,7 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
vim.clear_operator(cx);
if let Some(operator) = waiting_operator {
vim.push_operator(operator, cx);
+ vim.update_state(|state| state.pre_count = count)
}
});
}
@@ -755,7 +756,7 @@ impl Motion {
},
NextLineStart => (next_line_start(map, point, times), SelectionGoal::None),
StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None),
- EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None),
+ EndOfLineDownward => (last_non_whitespace(map, point, times), SelectionGoal::None),
GoToColumn => (go_to_column(map, point, times), SelectionGoal::None),
WindowTop => window_top(map, point, &text_layout_details, times - 1),
WindowMiddle => window_middle(map, point, &text_layout_details),
@@ -1422,6 +1423,26 @@ pub(crate) fn first_non_whitespace(
start_offset.to_display_point(map)
}
+pub(crate) fn last_non_whitespace(
+ map: &DisplaySnapshot,
+ from: DisplayPoint,
+ count: usize,
+) -> DisplayPoint {
+ let mut end_of_line = end_of_line(map, false, from, count).to_offset(map, Bias::Left);
+ let scope = map.buffer_snapshot.language_scope_at(from.to_point(map));
+ for (ch, offset) in map.reverse_buffer_chars_at(end_of_line) {
+ if ch == '\n' {
+ break;
+ }
+ end_of_line = offset;
+ if char_kind(&scope, ch) != CharKind::Whitespace || ch == '\n' {
+ break;
+ }
+ }
+
+ end_of_line.to_display_point(map)
+}
+
pub(crate) fn start_of_line(
map: &DisplaySnapshot,
display_lines: bool,
@@ -1899,6 +1920,16 @@ mod test {
cx.assert_shared_state("one\n ˇtwo\nthree").await;
}
+ #[gpui::test]
+ async fn test_end_of_line_downward(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.set_shared_state("ˇ one \n two \nthree").await;
+ cx.simulate_shared_keystrokes(["g", "_"]).await;
+ cx.assert_shared_state(" onˇe \n two \nthree").await;
+ cx.simulate_shared_keystrokes(["2", "g", "_"]).await;
+ cx.assert_shared_state(" one \n twˇo \nthree").await;
+ }
+
#[gpui::test]
async fn test_window_top(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
@@ -1,4 +1,9 @@
-use crate::{motion::Motion, object::Object, state::Mode, Vim};
+use crate::{
+ motion::{self, Motion},
+ object::Object,
+ state::Mode,
+ Vim,
+};
use editor::{movement, scroll::Autoscroll, Bias};
use gpui::WindowContext;
use language::BracketPair;
@@ -23,6 +28,7 @@ impl<'de> Deserialize<'de> for SurroundsType {
pub fn add_surrounds(text: Arc<str>, target: SurroundsType, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| {
vim.stop_recording();
+ let count = vim.take_count(cx);
vim.update_active_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
@@ -52,22 +58,26 @@ pub fn add_surrounds(text: Arc<str>, target: SurroundsType, cx: &mut WindowConte
.range(
&display_map,
selection.clone(),
- Some(1),
+ count,
true,
&text_layout_details,
)
.map(|mut range| {
- // The Motion::CurrentLine operation will contain the newline of the current line,
- // so we need to deal with this edge case
+ // The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace
if let Motion::CurrentLine = motion {
- let offset = range.end.to_offset(&display_map, Bias::Left);
- if let Some((last_ch, _)) =
- display_map.reverse_buffer_chars_at(offset).next()
- {
- if last_ch == '\n' {
- range.end = movement::left(&display_map, range.end);
- }
- }
+ range.start = motion::first_non_whitespace(
+ &display_map,
+ false,
+ range.start,
+ );
+ range.end = movement::saturating_right(
+ &display_map,
+ motion::last_non_whitespace(
+ &display_map,
+ movement::left(&display_map, range.end),
+ 1,
+ ),
+ );
}
range
});
@@ -627,6 +637,30 @@ mod test {
the lazy dog."},
Mode::Normal,
);
+
+ cx.set_state(
+ indoc! {"
+ The quˇick brown•
+ fox jumps over
+ the lazy dog."},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes(["y", "s", "s", "{"]);
+ cx.assert_state(
+ indoc! {"
+ ˇ{ The quick brown }•
+ fox jumps over
+ the lazy dog."},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes(["2", "y", "s", "s", ")"]);
+ cx.assert_state(
+ indoc! {"
+ ˇ({ The quick brown }•
+ fox jumps over)
+ the lazy dog."},
+ Mode::Normal,
+ );
}
#[gpui::test]