diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 2edbb8ff1c3264b50db9d295cb717671a9892281..0d3fb700efff6e651ff23769070a37e9cc4d9b20 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -535,7 +535,7 @@ fn down_display( (point, goal) } -fn up( +pub(crate) fn up( map: &DisplaySnapshot, point: DisplayPoint, mut goal: SelectionGoal, @@ -681,7 +681,11 @@ fn first_non_whitespace( map.clip_point(last_point, Bias::Left) } -fn start_of_line(map: &DisplaySnapshot, display_lines: bool, point: DisplayPoint) -> DisplayPoint { +pub(crate) fn start_of_line( + map: &DisplaySnapshot, + display_lines: bool, + point: DisplayPoint, +) -> DisplayPoint { if display_lines { map.clip_point(DisplayPoint::new(point.row(), 0), Bias::Right) } else { @@ -689,7 +693,11 @@ fn start_of_line(map: &DisplaySnapshot, display_lines: bool, point: DisplayPoint } } -fn end_of_line(map: &DisplaySnapshot, display_lines: bool, point: DisplayPoint) -> DisplayPoint { +pub(crate) fn end_of_line( + map: &DisplaySnapshot, + display_lines: bool, + point: DisplayPoint, +) -> DisplayPoint { if display_lines { map.clip_point( DisplayPoint::new(point.row(), map.line_len(point.row())), @@ -819,12 +827,8 @@ fn find_backward( } fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint { - let new_row = (point.row() + times as u32).min(map.max_buffer_row()); - first_non_whitespace( - map, - false, - map.clip_point(DisplayPoint::new(new_row, 0), Bias::Left), - ) + let correct_line = down(map, point, SelectionGoal::None, times).0; + first_non_whitespace(map, false, correct_line) } #[cfg(test)] diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 2b03632c42a2f1a3247c60409d1df2030913d282..a73c51880964c09e41698bbad242c5877aa16796 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -10,7 +10,7 @@ mod yank; use std::sync::Arc; use crate::{ - motion::Motion, + motion::{self, Motion}, object::Object, state::{Mode, Operator}, Vim, @@ -214,19 +214,19 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex .collect(); let edits = selection_start_rows.into_iter().map(|row| { let (indent, _) = map.line_indent(row); - let start_of_line = map - .clip_point(DisplayPoint::new(row, 0), Bias::Left) - .to_point(&map); + let start_of_line = + motion::start_of_line(&map, false, DisplayPoint::new(row, 0)) + .to_point(&map); let mut new_text = " ".repeat(indent as usize); new_text.push('\n'); (start_of_line..start_of_line, new_text) }); editor.edit_with_autoindent(edits, cx); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { - s.move_cursors_with(|map, mut cursor, _| { - *cursor.row_mut() -= 1; - *cursor.column_mut() = map.line_len(cursor.row()); - (map.clip_point(cursor, Bias::Left), SelectionGoal::None) + s.move_cursors_with(|map, cursor, _| { + let previous_line = motion::up(map, cursor, SelectionGoal::None, 1).0; + let insert_point = motion::end_of_line(map, false, previous_line); + (insert_point, SelectionGoal::None) }); }); }); @@ -240,15 +240,16 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { let (map, old_selections) = editor.selections.all_display(cx); + let selection_end_rows: HashSet = old_selections .into_iter() .map(|selection| selection.end.row()) .collect(); let edits = selection_end_rows.into_iter().map(|row| { let (indent, _) = map.line_indent(row); - let end_of_line = map - .clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left) - .to_point(&map); + let end_of_line = + motion::end_of_line(&map, false, DisplayPoint::new(row, 0)).to_point(&map); + let mut new_text = "\n".to_string(); new_text.push_str(&" ".repeat(indent as usize)); (end_of_line..end_of_line, new_text) diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index cfde221dc56f71aeb20e7830996dbd580dbf3d28..88fa37585150d3e0749468094feab4bd98545f15 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -358,6 +358,79 @@ async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) { twelve char "}) .await; + + cx.set_shared_state(indoc! { " + tˇwelve char twelve char + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["enter"]).await; + cx.assert_shared_state(indoc! { " + twelve char twelve char + ˇtwelve char + "}) + .await; + + cx.set_shared_state(indoc! { " + twelve char + tˇwelve char twelve char + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["o", "o", "escape"]).await; + cx.assert_shared_state(indoc! { " + twelve char + twelve char twelve char + ˇo + twelve char + "}) + .await; + + cx.set_shared_state(indoc! { " + twelve char + tˇwelve char twelve char + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["shift-a", "a", "escape"]) + .await; + cx.assert_shared_state(indoc! { " + twelve char + twelve char twelve charˇa + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["shift-i", "i", "escape"]) + .await; + cx.assert_shared_state(indoc! { " + twelve char + ˇitwelve char twelve chara + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["shift-d"]).await; + cx.assert_shared_state(indoc! { " + twelve char + ˇ + twelve char + "}) + .await; + + cx.set_shared_state(indoc! { " + twelve char + twelve char tˇwelve char + twelve char + "}) + .await; + cx.simulate_shared_keystrokes(["shift-o", "o", "escape"]) + .await; + cx.assert_shared_state(indoc! { " + twelve char + ˇo + twelve char twelve char + twelve char + "}) + .await; } #[gpui::test] diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index bc37f2fdd631d0dbf3405890d3f72f455c9aaafd..d04b1b776836b04addddf5e767023393dd5782f7 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -1,4 +1,3 @@ -use editor::EditorSettings; use indoc::indoc; use settings::SettingsStore; use std::ops::{Deref, DerefMut, Range}; @@ -6,7 +5,7 @@ use std::ops::{Deref, DerefMut, Range}; use collections::{HashMap, HashSet}; use gpui::ContextHandle; use language::{ - language_settings::{AllLanguageSettings, LanguageSettings, SoftWrap}, + language_settings::{AllLanguageSettings, SoftWrap}, OffsetRangeExt, }; use util::test::{generate_marked_text, marked_text_offsets}; diff --git a/crates/vim/test_data/test_wrapped_lines.json b/crates/vim/test_data/test_wrapped_lines.json index f9f54c5c43bf64397a958271cc4d44cbf84e4a9a..1ebbd4f20507a86747a50a39d9ee70cd2266f74b 100644 --- a/crates/vim/test_data/test_wrapped_lines.json +++ b/crates/vim/test_data/test_wrapped_lines.json @@ -24,3 +24,27 @@ {"Get":{"state":"twelve charˇ twelve char\ntwelve char\n","mode":"Normal"}} {"Key":"$"} {"Get":{"state":"twelve char twelve chaˇr\ntwelve char\n","mode":"Normal"}} +{"Put":{"state":"tˇwelve char twelve char\ntwelve char\n"}} +{"Key":"enter"} +{"Get":{"state":"twelve char twelve char\nˇtwelve char\n","mode":"Normal"}} +{"Put":{"state":"twelve char\ntˇwelve char twelve char\ntwelve char\n"}} +{"Key":"o"} +{"Key":"o"} +{"Key":"escape"} +{"Get":{"state":"twelve char\ntwelve char twelve char\nˇo\ntwelve char\n","mode":"Normal"}} +{"Put":{"state":"twelve char\ntˇwelve char twelve char\ntwelve char\n"}} +{"Key":"shift-a"} +{"Key":"a"} +{"Key":"escape"} +{"Get":{"state":"twelve char\ntwelve char twelve charˇa\ntwelve char\n","mode":"Normal"}} +{"Key":"shift-i"} +{"Key":"i"} +{"Key":"escape"} +{"Get":{"state":"twelve char\nˇitwelve char twelve chara\ntwelve char\n","mode":"Normal"}} +{"Key":"shift-d"} +{"Get":{"state":"twelve char\nˇ\ntwelve char\n","mode":"Normal"}} +{"Put":{"state":"twelve char\ntwelve char tˇwelve char\ntwelve char\n"}} +{"Key":"shift-o"} +{"Key":"o"} +{"Key":"escape"} +{"Get":{"state":"twelve char\nˇo\ntwelve char twelve char\ntwelve char\n","mode":"Normal"}}