mod case;mod change;mod delete;mod increment;mod indent;pub(crate) mod mark;mod paste;pub(crate) mod repeat;mod scroll;pub(crate) mod search;pub mod substitute;pub(crate) mod yank; use std::collections::HashMap;use std::sync::Arc; use crate::{ motion::{self, first_non_whitespace, next_line_end, right, Motion}, object::Object, state::{Mode, Operator}, surrounds::{check_and_move_to_valid_bracket_pair, SurroundsType}, Vim,};use case::{change_case_motion, change_case_object, CaseTarget};use collections::BTreeSet;use editor::scroll::Autoscroll;use editor::Anchor;use editor::Bias;use editor::Editor;use editor::{display_map::ToDisplayPoint, movement};use gpui::{actions, ViewContext, WindowContext};use language::{Point, SelectionGoal};use log::error;use multi_buffer::MultiBufferRow;use workspace::Workspace; use self::{ case::{change_case, convert_to_lower_case, convert_to_upper_case}, change::{change_motion, change_object}, delete::{delete_motion, delete_object}, indent::{indent_motion, indent_object, IndentDirection}, yank::{yank_motion, yank_object},}; actions!( vim, [ InsertAfter, InsertBefore, InsertFirstNonWhitespace, InsertEndOfLine, InsertLineAbove, InsertLineBelow, InsertAtPrevious, DeleteLeft, DeleteRight, ChangeToEndOfLine, DeleteToEndOfLine, Yank, YankLine, ChangeCase, ConvertToUpperCase, ConvertToLowerCase, JoinLines, Indent, Outdent, ToggleComments, Undo, Redo, ]); pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) { workspace.register_action(insert_after); workspace.register_action(insert_before); workspace.register_action(insert_first_non_whitespace); workspace.register_action(insert_end_of_line); workspace.register_action(insert_line_above); workspace.register_action(insert_line_below); workspace.register_action(insert_at_previous); workspace.register_action(change_case); workspace.register_action(convert_to_upper_case); workspace.register_action(convert_to_lower_case); workspace.register_action(yank_line); workspace.register_action(toggle_comments); workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion(vim, Motion::Left, times, cx); }) }); workspace.register_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion(vim, Motion::Right, times, cx); }) }); workspace.register_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); let times = vim.take_count(cx); change_motion( vim, Motion::EndOfLine { display_lines: false, }, times, cx, ); }) }); workspace.register_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion( vim, Motion::EndOfLine { display_lines: false, }, times, cx, ); }) }); workspace.register_action(|_: &mut Workspace, _: &JoinLines, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let mut times = vim.take_count(cx).unwrap_or(1); if vim.state().mode.is_visual() { times = 1; } else if times > 1 { // 2J joins two lines together (same as J or 1J) times -= 1; } vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { for _ in 0..times { editor.join_lines(&Default::default(), cx) } }) }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Indent, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.indent(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Outdent, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.outdent(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Undo, cx| { Vim::update(cx, |vim, cx| { let times = vim.take_count(cx); vim.update_active_editor(cx, |_, editor, cx| { for _ in 0..times.unwrap_or(1) { editor.undo(&editor::actions::Undo, cx); } }); }) }); workspace.register_action(|_: &mut Workspace, _: &Redo, cx| { Vim::update(cx, |vim, cx| { let times = vim.take_count(cx); vim.update_active_editor(cx, |_, editor, cx| { for _ in 0..times.unwrap_or(1) { editor.redo(&editor::actions::Redo, cx); } }); }) }); paste::register(workspace, cx); repeat::register(workspace, cx); scroll::register(workspace, cx); search::register(workspace, cx); substitute::register(workspace, cx); increment::register(workspace, cx);} pub fn normal_motion( motion: Motion, operator: Option<Operator>, times: Option<usize>, cx: &mut WindowContext,) { Vim::update(cx, |vim, cx| { match operator { None => move_cursor(vim, motion, times, cx), Some(Operator::Change) => change_motion(vim, motion, times, cx), Some(Operator::Delete) => delete_motion(vim, motion, times, cx), Some(Operator::Yank) => yank_motion(vim, motion, times, cx), Some(Operator::AddSurrounds { target: None }) => {} Some(Operator::Indent) => indent_motion(vim, motion, times, IndentDirection::In, cx), Some(Operator::Outdent) => indent_motion(vim, motion, times, IndentDirection::Out, cx), Some(Operator::Lowercase) => { change_case_motion(vim, motion, times, CaseTarget::Lowercase, cx) } Some(Operator::Uppercase) => { change_case_motion(vim, motion, times, CaseTarget::Uppercase, cx) } Some(Operator::OppositeCase) => { change_case_motion(vim, motion, times, CaseTarget::OppositeCase, cx) } Some(operator) => { // Can't do anything for text objects, Ignoring error!("Unexpected normal mode motion operator: {:?}", operator) } } });} pub fn normal_object(object: Object, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| { let mut waiting_operator: Option<Operator> = None; match vim.maybe_pop_operator() { Some(Operator::Object { around }) => match vim.maybe_pop_operator() { Some(Operator::Change) => change_object(vim, object, around, cx), Some(Operator::Delete) => delete_object(vim, object, around, cx), Some(Operator::Yank) => yank_object(vim, object, around, cx), Some(Operator::Indent) => { indent_object(vim, object, around, IndentDirection::In, cx) } Some(Operator::Outdent) => { indent_object(vim, object, around, IndentDirection::Out, cx) } Some(Operator::Lowercase) => { change_case_object(vim, object, around, CaseTarget::Lowercase, cx) } Some(Operator::Uppercase) => { change_case_object(vim, object, around, CaseTarget::Uppercase, cx) } Some(Operator::OppositeCase) => { change_case_object(vim, object, around, CaseTarget::OppositeCase, cx) } Some(Operator::AddSurrounds { target: None }) => { waiting_operator = Some(Operator::AddSurrounds { target: Some(SurroundsType::Object(object)), }); } _ => { // Can't do anything for namespace operators. Ignoring } }, Some(Operator::DeleteSurrounds) => { waiting_operator = Some(Operator::DeleteSurrounds); } Some(Operator::ChangeSurrounds { target: None }) => { if check_and_move_to_valid_bracket_pair(vim, object, cx) { waiting_operator = Some(Operator::ChangeSurrounds { target: Some(object), }); } } _ => { // Can't do anything with change/delete/yank/surrounds and text objects. Ignoring } } vim.clear_operator(cx); if let Some(operator) = waiting_operator { vim.push_operator(operator, cx); } });} pub(crate) fn move_cursor( vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext,) { vim.update_active_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(cx); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, goal| { motion .move_point(map, cursor, goal, times, &text_layout_details) .unwrap_or((cursor, goal)) }) }) });} fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None)); }); }); });} fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); });} fn insert_first_non_whitespace( _: &mut Workspace, _: &InsertFirstNonWhitespace, cx: &mut ViewContext<Workspace>,) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { ( first_non_whitespace(map, false, cursor), SelectionGoal::None, ) }); }); }); });} fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { (next_line_end(map, cursor, 1), SelectionGoal::None) }); }); }); });} fn insert_at_previous(_: &mut Workspace, _: &InsertAtPrevious, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |vim, editor, cx| { if let Some(marks) = vim.state().marks.get("^") { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.select_anchor_ranges(marks.iter().map(|mark| *mark..*mark)) }); } }); });} fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let selections = editor.selections.all::<Point>(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let selection_start_rows: BTreeSet<u32> = selections .into_iter() .map(|selection| selection.start.row) .collect(); let edits = selection_start_rows.into_iter().map(|row| { let indent = snapshot .indent_size_for_line(MultiBufferRow(row)) .chars() .collect::<String>(); let start_of_line = Point::new(row, 0); (start_of_line..start_of_line, indent + "\n") }); editor.edit_with_autoindent(edits, cx); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1); let insert_point = motion::end_of_line(map, false, previous_line, 1); (insert_point, SelectionGoal::None) }); }); }); }); });} fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(cx); editor.transact(cx, |editor, cx| { let selections = editor.selections.all::<Point>(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let selection_end_rows: BTreeSet<u32> = selections .into_iter() .map(|selection| selection.end.row) .collect(); let edits = selection_end_rows.into_iter().map(|row| { let indent = snapshot .indent_size_for_line(MultiBufferRow(row)) .chars() .collect::<String>(); let end_of_line = Point::new(row, snapshot.line_len(MultiBufferRow(row))); (end_of_line..end_of_line, "\n".to_string() + &indent) }); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.maybe_move_cursors_with(|map, cursor, goal| { Motion::CurrentLine.move_point( map, cursor, goal, None, &text_layout_details, ) }); }); editor.edit_with_autoindent(edits, cx); }); }); });} fn yank_line(_: &mut Workspace, _: &YankLine, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { let count = vim.take_count(cx); yank_motion(vim, motion::Motion::CurrentLine, count, cx) })} fn toggle_comments(_: &mut Workspace, _: &ToggleComments, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.toggle_comments(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } });} fn save_selection_starts(editor: &Editor, cx: &mut ViewContext<Editor>) -> HashMap<usize, Anchor> { let (map, selections) = editor.selections.all_display(cx); selections .iter() .map(|selection| { ( selection.id, map.display_point_to_anchor(selection.start, Bias::Right), ) }) .collect::<HashMap<_, _>>()} fn restore_selection_cursors( editor: &mut Editor, cx: &mut ViewContext<Editor>, positions: &mut HashMap<usize, Anchor>,) { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_with(|map, selection| {Lines 1–500 of 1461. Load more
mod case; mod change; mod delete; mod increment; mod indent; pub(crate) mod mark; mod paste; pub(crate) mod repeat; mod scroll; pub(crate) mod search; pub mod substitute; pub(crate) mod yank; use std::collections::HashMap; use std::sync::Arc; use crate::{ motion::{self, first_non_whitespace, next_line_end, right, Motion}, object::Object, state::{Mode, Operator}, surrounds::{check_and_move_to_valid_bracket_pair, SurroundsType}, Vim, }; use case::{change_case_motion, change_case_object, CaseTarget}; use collections::BTreeSet; use editor::scroll::Autoscroll; use editor::Anchor; use editor::Bias; use editor::Editor; use editor::{display_map::ToDisplayPoint, movement}; use gpui::{actions, ViewContext, WindowContext}; use language::{Point, SelectionGoal}; use log::error; use multi_buffer::MultiBufferRow; use workspace::Workspace; use self::{ case::{change_case, convert_to_lower_case, convert_to_upper_case}, change::{change_motion, change_object}, delete::{delete_motion, delete_object}, indent::{indent_motion, indent_object, IndentDirection}, yank::{yank_motion, yank_object}, }; actions!( vim, [ InsertAfter, InsertBefore, InsertFirstNonWhitespace, InsertEndOfLine, InsertLineAbove, InsertLineBelow, InsertAtPrevious, DeleteLeft, DeleteRight, ChangeToEndOfLine, DeleteToEndOfLine, Yank, YankLine, ChangeCase, ConvertToUpperCase, ConvertToLowerCase, JoinLines, Indent, Outdent, ToggleComments, Undo, Redo, ] ); pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) { workspace.register_action(insert_after); workspace.register_action(insert_before); workspace.register_action(insert_first_non_whitespace); workspace.register_action(insert_end_of_line); workspace.register_action(insert_line_above); workspace.register_action(insert_line_below); workspace.register_action(insert_at_previous); workspace.register_action(change_case); workspace.register_action(convert_to_upper_case); workspace.register_action(convert_to_lower_case); workspace.register_action(yank_line); workspace.register_action(toggle_comments); workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion(vim, Motion::Left, times, cx); }) }); workspace.register_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion(vim, Motion::Right, times, cx); }) }); workspace.register_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); let times = vim.take_count(cx); change_motion( vim, Motion::EndOfLine { display_lines: false, }, times, cx, ); }) }); workspace.register_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let times = vim.take_count(cx); delete_motion( vim, Motion::EndOfLine { display_lines: false, }, times, cx, ); }) }); workspace.register_action(|_: &mut Workspace, _: &JoinLines, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); let mut times = vim.take_count(cx).unwrap_or(1); if vim.state().mode.is_visual() { times = 1; } else if times > 1 { // 2J joins two lines together (same as J or 1J) times -= 1; } vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { for _ in 0..times { editor.join_lines(&Default::default(), cx) } }) }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Indent, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.indent(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Outdent, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.outdent(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); }); workspace.register_action(|_: &mut Workspace, _: &Undo, cx| { Vim::update(cx, |vim, cx| { let times = vim.take_count(cx); vim.update_active_editor(cx, |_, editor, cx| { for _ in 0..times.unwrap_or(1) { editor.undo(&editor::actions::Undo, cx); } }); }) }); workspace.register_action(|_: &mut Workspace, _: &Redo, cx| { Vim::update(cx, |vim, cx| { let times = vim.take_count(cx); vim.update_active_editor(cx, |_, editor, cx| { for _ in 0..times.unwrap_or(1) { editor.redo(&editor::actions::Redo, cx); } }); }) }); paste::register(workspace, cx); repeat::register(workspace, cx); scroll::register(workspace, cx); search::register(workspace, cx); substitute::register(workspace, cx); increment::register(workspace, cx); } pub fn normal_motion( motion: Motion, operator: Option<Operator>, times: Option<usize>, cx: &mut WindowContext, ) { Vim::update(cx, |vim, cx| { match operator { None => move_cursor(vim, motion, times, cx), Some(Operator::Change) => change_motion(vim, motion, times, cx), Some(Operator::Delete) => delete_motion(vim, motion, times, cx), Some(Operator::Yank) => yank_motion(vim, motion, times, cx), Some(Operator::AddSurrounds { target: None }) => {} Some(Operator::Indent) => indent_motion(vim, motion, times, IndentDirection::In, cx), Some(Operator::Outdent) => indent_motion(vim, motion, times, IndentDirection::Out, cx), Some(Operator::Lowercase) => { change_case_motion(vim, motion, times, CaseTarget::Lowercase, cx) } Some(Operator::Uppercase) => { change_case_motion(vim, motion, times, CaseTarget::Uppercase, cx) } Some(Operator::OppositeCase) => { change_case_motion(vim, motion, times, CaseTarget::OppositeCase, cx) } Some(operator) => { // Can't do anything for text objects, Ignoring error!("Unexpected normal mode motion operator: {:?}", operator) } } }); } pub fn normal_object(object: Object, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| { let mut waiting_operator: Option<Operator> = None; match vim.maybe_pop_operator() { Some(Operator::Object { around }) => match vim.maybe_pop_operator() { Some(Operator::Change) => change_object(vim, object, around, cx), Some(Operator::Delete) => delete_object(vim, object, around, cx), Some(Operator::Yank) => yank_object(vim, object, around, cx), Some(Operator::Indent) => { indent_object(vim, object, around, IndentDirection::In, cx) } Some(Operator::Outdent) => { indent_object(vim, object, around, IndentDirection::Out, cx) } Some(Operator::Lowercase) => { change_case_object(vim, object, around, CaseTarget::Lowercase, cx) } Some(Operator::Uppercase) => { change_case_object(vim, object, around, CaseTarget::Uppercase, cx) } Some(Operator::OppositeCase) => { change_case_object(vim, object, around, CaseTarget::OppositeCase, cx) } Some(Operator::AddSurrounds { target: None }) => { waiting_operator = Some(Operator::AddSurrounds { target: Some(SurroundsType::Object(object)), }); } _ => { // Can't do anything for namespace operators. Ignoring } }, Some(Operator::DeleteSurrounds) => { waiting_operator = Some(Operator::DeleteSurrounds); } Some(Operator::ChangeSurrounds { target: None }) => { if check_and_move_to_valid_bracket_pair(vim, object, cx) { waiting_operator = Some(Operator::ChangeSurrounds { target: Some(object), }); } } _ => { // Can't do anything with change/delete/yank/surrounds and text objects. Ignoring } } vim.clear_operator(cx); if let Some(operator) = waiting_operator { vim.push_operator(operator, cx); } }); } pub(crate) fn move_cursor( vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext, ) { vim.update_active_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(cx); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, goal| { motion .move_point(map, cursor, goal, times, &text_layout_details) .unwrap_or((cursor, goal)) }) }) }); } fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None)); }); }); }); } fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); }); } fn insert_first_non_whitespace( _: &mut Workspace, _: &InsertFirstNonWhitespace, cx: &mut ViewContext<Workspace>, ) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { ( first_non_whitespace(map, false, cursor), SelectionGoal::None, ) }); }); }); }); } fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { (next_line_end(map, cursor, 1), SelectionGoal::None) }); }); }); }); } fn insert_at_previous(_: &mut Workspace, _: &InsertAtPrevious, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |vim, editor, cx| { if let Some(marks) = vim.state().marks.get("^") { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.select_anchor_ranges(marks.iter().map(|mark| *mark..*mark)) }); } }); }); } fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let selections = editor.selections.all::<Point>(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let selection_start_rows: BTreeSet<u32> = selections .into_iter() .map(|selection| selection.start.row) .collect(); let edits = selection_start_rows.into_iter().map(|row| { let indent = snapshot .indent_size_for_line(MultiBufferRow(row)) .chars() .collect::<String>(); let start_of_line = Point::new(row, 0); (start_of_line..start_of_line, indent + "\n") }); editor.edit_with_autoindent(edits, cx); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_cursors_with(|map, cursor, _| { let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1); let insert_point = motion::end_of_line(map, false, previous_line, 1); (insert_point, SelectionGoal::None) }); }); }); }); }); } fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.start_recording(cx); vim.switch_mode(Mode::Insert, false, cx); vim.update_active_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(cx); editor.transact(cx, |editor, cx| { let selections = editor.selections.all::<Point>(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let selection_end_rows: BTreeSet<u32> = selections .into_iter() .map(|selection| selection.end.row) .collect(); let edits = selection_end_rows.into_iter().map(|row| { let indent = snapshot .indent_size_for_line(MultiBufferRow(row)) .chars() .collect::<String>(); let end_of_line = Point::new(row, snapshot.line_len(MultiBufferRow(row))); (end_of_line..end_of_line, "\n".to_string() + &indent) }); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.maybe_move_cursors_with(|map, cursor, goal| { Motion::CurrentLine.move_point( map, cursor, goal, None, &text_layout_details, ) }); }); editor.edit_with_autoindent(edits, cx); }); }); }); } fn yank_line(_: &mut Workspace, _: &YankLine, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { let count = vim.take_count(cx); yank_motion(vim, motion::Motion::CurrentLine, count, cx) }) } fn toggle_comments(_: &mut Workspace, _: &ToggleComments, cx: &mut ViewContext<Workspace>) { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); vim.update_active_editor(cx, |_, editor, cx| { editor.transact(cx, |editor, cx| { let mut original_positions = save_selection_starts(editor, cx); editor.toggle_comments(&Default::default(), cx); restore_selection_cursors(editor, cx, &mut original_positions); }); }); if vim.state().mode.is_visual() { vim.switch_mode(Mode::Normal, false, cx) } }); } fn save_selection_starts(editor: &Editor, cx: &mut ViewContext<Editor>) -> HashMap<usize, Anchor> { let (map, selections) = editor.selections.all_display(cx); selections .iter() .map(|selection| { ( selection.id, map.display_point_to_anchor(selection.start, Bias::Right), ) }) .collect::<HashMap<_, _>>() } fn restore_selection_cursors( editor: &mut Editor, cx: &mut ViewContext<Editor>, positions: &mut HashMap<usize, Anchor>, ) { editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.move_with(|map, selection| {