diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 789e05b912..67775c6a67 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -66,7 +66,9 @@ "shift-A": "vim::InsertEndOfLine", "x": "vim::DeleteRight", "shift-X": "vim::DeleteLeft", - "shift-^": "vim::FirstNonWhitespace" + "shift-^": "vim::FirstNonWhitespace", + "o": "vim::InsertLineBelow", + "shift-O": "vim::InsertLineAbove" } }, { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ced21d8ee4..18cae80496 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1334,6 +1334,19 @@ impl Editor { self.update_selections(vec![selection], None, cx); } + pub fn display_selections( + &mut self, + cx: &mut ViewContext, + ) -> (DisplaySnapshot, Vec>) { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let selections = self + .local_selections::(cx) + .into_iter() + .map(|selection| selection.map(|point| point.to_display_point(&display_map))) + .collect(); + (display_map, selections) + } + pub fn move_selections( &mut self, cx: &mut ViewContext, @@ -1382,6 +1395,25 @@ impl Editor { }); } + pub fn edit(&mut self, edits: I, cx: &mut ViewContext) + where + I: IntoIterator, T)>, + S: ToOffset, + T: Into>, + { + self.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); + } + + pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ViewContext) + where + I: IntoIterator, T)>, + S: ToOffset, + T: Into>, + { + self.buffer + .update(cx, |buffer, cx| buffer.edit_with_autoindent(edits, cx)); + } + fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext) { self.hide_context_menu(cx); diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index a1740ea09e..679f50bd26 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -7,6 +7,8 @@ use crate::{ Vim, }; use change::init as change_init; +use collections::HashSet; +use editor::{Bias, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; use language::SelectionGoal; use workspace::Workspace; @@ -120,41 +122,54 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - editor.move_cursors(cx, |map, cursor, goal| { - let (indent, _) = map.line_indent(cursor.row()); - let (cursor, _) = Motion::EndOfLine.move_point(map, cursor, goal); - (cursor, SelectionGoal::Column(indent)) + let (map, old_selections) = editor.display_selections(cx); + let selection_start_rows: HashSet = old_selections + .into_iter() + .map(|selection| selection.start.row()) + .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 mut new_text = " ".repeat(indent as usize); + new_text.push('\n'); + (start_of_line..start_of_line, new_text) }); - editor.insert("\n", cx); - editor.move_cursors(cx, |_, mut cursor, goal| { - if let SelectionGoal::Column(column) = goal { - *cursor.column_mut() = column; - } - (cursor, SelectionGoal::None) + editor.edit(edits, cx); + editor.move_cursors(cx, |map, mut cursor, _| { + *cursor.row_mut() -= 1; + *cursor.column_mut() = map.line_len(cursor.row()); + (map.clip_point(cursor, Bias::Left), SelectionGoal::None) }); }); }); }); } -fn insert_line_below(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext) { +fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { + let (map, old_selections) = editor.display_selections(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 mut new_text = "\n".to_string(); + new_text.push_str(&" ".repeat(indent as usize)); + (end_of_line..end_of_line, new_text) + }); editor.move_cursors(cx, |map, cursor, goal| { - let (indent, _) = map.line_indent(cursor.row()); - let (cursor, _) = Motion::StartOfLine.move_point(map, cursor, goal); - (cursor, SelectionGoal::Column(indent)) - }); - editor.insert("\n", cx); - editor.move_cursors(cx, |_, mut cursor, goal| { - *cursor.row_mut() -= 1; - if let SelectionGoal::Column(column) = goal { - *cursor.column_mut() = column; - } - (cursor, SelectionGoal::None) + Motion::EndOfLine.move_point(map, cursor, goal) }); + editor.edit(edits, cx); }); }); });