A few more fixes for wrapped line motions

Conrad Irwin created

Change summary

crates/vim/src/motion.rs                          | 22 +++--
crates/vim/src/normal.rs                          | 23 ++--
crates/vim/src/test.rs                            | 73 +++++++++++++++++
crates/vim/src/test/neovim_backed_test_context.rs |  3 
crates/vim/test_data/test_wrapped_lines.json      | 24 +++++
5 files changed, 123 insertions(+), 22 deletions(-)

Detailed changes

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)]

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<u32> = 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)

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]

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};

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"}}