use std::sync::Arc; use collections::HashMap;use editor::{ display_map::{DisplayRow, DisplaySnapshot, ToDisplayPoint}, movement, scroll::Autoscroll, Bias, DisplayPoint, Editor, ToOffset,};use gpui::{actions, Context, Window};use language::{Point, Selection, SelectionGoal};use multi_buffer::MultiBufferRow;use search::BufferSearchBar;use util::ResultExt;use workspace::searchable::Direction; use crate::{ motion::{first_non_whitespace, next_line_end, start_of_line, Motion}, object::Object, state::{Mode, Operator}, Vim,}; actions!( vim, [ ToggleVisual, ToggleVisualLine, ToggleVisualBlock, VisualDelete, VisualDeleteLine, VisualYank, VisualYankLine, OtherEnd, SelectNext, SelectPrevious, SelectNextMatch, SelectPreviousMatch, SelectSmallerSyntaxNode, SelectLargerSyntaxNode, RestoreVisualSelection, VisualInsertEndOfLine, VisualInsertFirstNonWhiteSpace, ]); pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) { Vim::action(editor, cx, |vim, _: &ToggleVisual, window, cx| { vim.toggle_mode(Mode::Visual, window, cx) }); Vim::action(editor, cx, |vim, _: &ToggleVisualLine, window, cx| { vim.toggle_mode(Mode::VisualLine, window, cx) }); Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, window, cx| { vim.toggle_mode(Mode::VisualBlock, window, cx) }); Vim::action(editor, cx, Vim::other_end); Vim::action(editor, cx, Vim::visual_insert_end_of_line); Vim::action(editor, cx, Vim::visual_insert_first_non_white_space); Vim::action(editor, cx, |vim, _: &VisualDelete, window, cx| { vim.record_current_action(cx); vim.visual_delete(false, window, cx); }); Vim::action(editor, cx, |vim, _: &VisualDeleteLine, window, cx| { vim.record_current_action(cx); vim.visual_delete(true, window, cx); }); Vim::action(editor, cx, |vim, _: &VisualYank, window, cx| { vim.visual_yank(false, window, cx) }); Vim::action(editor, cx, |vim, _: &VisualYankLine, window, cx| { vim.visual_yank(true, window, cx) }); Vim::action(editor, cx, Vim::select_next); Vim::action(editor, cx, Vim::select_previous); Vim::action(editor, cx, |vim, _: &SelectNextMatch, window, cx| { vim.select_match(Direction::Next, window, cx); }); Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, window, cx| { vim.select_match(Direction::Prev, window, cx); }); Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, window, cx| { let count = Vim::take_count(cx).unwrap_or(1); for _ in 0..count { vim.update_editor(window, cx, |_, editor, window, cx| { editor.select_larger_syntax_node(&Default::default(), window, cx); }); } }); Vim::action( editor, cx, |vim, _: &SelectSmallerSyntaxNode, window, cx| { let count = Vim::take_count(cx).unwrap_or(1); for _ in 0..count { vim.update_editor(window, cx, |_, editor, window, cx| { editor.select_smaller_syntax_node(&Default::default(), window, cx); }); } }, ); Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, window, cx| { let Some((stored_mode, reversed)) = vim.stored_visual_mode.take() else { return; }; let Some((start, end)) = vim.marks.get("<").zip(vim.marks.get(">")) else { return; }; let ranges = start .iter() .zip(end) .zip(reversed) .map(|((start, end), reversed)| (*start, *end, reversed)) .collect::<Vec<_>>(); if vim.mode.is_visual() { vim.create_visual_marks(vim.mode, window, cx); } vim.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let map = s.display_map(); let ranges = ranges .into_iter() .map(|(start, end, reversed)| { let new_end = movement::saturating_right(&map, end.to_display_point(&map)); Selection { id: s.new_selection_id(), start: start.to_offset(&map.buffer_snapshot), end: new_end.to_offset(&map, Bias::Left), reversed, goal: SelectionGoal::None, } }) .collect(); s.select(ranges); }) }); vim.switch_mode(stored_mode, true, window, cx) });} impl Vim { pub fn visual_motion( &mut self, motion: Motion, times: Option<usize>, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |vim, editor, window, cx| { let text_layout_details = editor.text_layout_details(window); if vim.mode == Mode::VisualBlock && !matches!( motion, Motion::EndOfLine { display_lines: false } ) { let is_up_or_down = matches!(motion, Motion::Up { .. } | Motion::Down { .. }); vim.visual_block_motion(is_up_or_down, editor, window, cx, |map, point, goal| { motion.move_point(map, point, goal, times, &text_layout_details) }) } else { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { let was_reversed = selection.reversed; let mut current_head = selection.head(); // our motions assume the current character is after the cursor, // but in (forward) visual mode the current character is just // before the end of the selection. // If the file ends with a newline (which is common) we don't do this. // so that if you go to the end of such a file you can use "up" to go // to the previous line and have it work somewhat as expected. #[allow(clippy::nonminimal_bool)] if !selection.reversed && !selection.is_empty() && !(selection.end.column() == 0 && selection.end == map.max_point()) { current_head = movement::left(map, selection.end) } let Some((new_head, goal)) = motion.move_point( map, current_head, selection.goal, times, &text_layout_details, ) else { return; }; selection.set_head(new_head, goal); // ensure the current character is included in the selection. if !selection.reversed { let next_point = if vim.mode == Mode::VisualBlock { movement::saturating_right(map, selection.end) } else { movement::right(map, selection.end) }; if !(next_point.column() == 0 && next_point == map.max_point()) { selection.end = next_point; } } // vim always ensures the anchor character stays selected. // if our selection has reversed, we need to move the opposite end // to ensure the anchor is still selected. if was_reversed && !selection.reversed { selection.start = movement::left(map, selection.start); } else if !was_reversed && selection.reversed { selection.end = movement::right(map, selection.end); } }) }); } }); } pub fn visual_block_motion( &mut self, preserve_goal: bool, editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>, mut move_selection: impl FnMut( &DisplaySnapshot, DisplayPoint, SelectionGoal, ) -> Option<(DisplayPoint, SelectionGoal)>, ) { let text_layout_details = editor.text_layout_details(window); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let map = &s.display_map(); let mut head = s.newest_anchor().head().to_display_point(map); let mut tail = s.oldest_anchor().tail().to_display_point(map); let mut head_x = map.x_for_display_point(head, &text_layout_details); let mut tail_x = map.x_for_display_point(tail, &text_layout_details); let (start, end) = match s.newest_anchor().goal { SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end), SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start), _ => (tail_x.0, head_x.0), }; let mut goal = SelectionGoal::HorizontalRange { start, end }; let was_reversed = tail_x > head_x; if !was_reversed && !preserve_goal { head = movement::saturating_left(map, head); } let Some((new_head, _)) = move_selection(map, head, goal) else { return; }; head = new_head; head_x = map.x_for_display_point(head, &text_layout_details); let is_reversed = tail_x > head_x; if was_reversed && !is_reversed { tail = movement::saturating_left(map, tail); tail_x = map.x_for_display_point(tail, &text_layout_details); } else if !was_reversed && is_reversed { tail = movement::saturating_right(map, tail); tail_x = map.x_for_display_point(tail, &text_layout_details); } if !is_reversed && !preserve_goal { head = movement::saturating_right(map, head); head_x = map.x_for_display_point(head, &text_layout_details); } let positions = if is_reversed { head_x..tail_x } else { tail_x..head_x }; if !preserve_goal { goal = SelectionGoal::HorizontalRange { start: positions.start.0, end: positions.end.0, }; } let mut selections = Vec::new(); let mut row = tail.row(); loop { let laid_out_line = map.layout_row(row, &text_layout_details); let start = DisplayPoint::new( row, laid_out_line.closest_index_for_x(positions.start) as u32, ); let mut end = DisplayPoint::new(row, laid_out_line.closest_index_for_x(positions.end) as u32); if end <= start { if start.column() == map.line_len(start.row()) { end = start; } else { end = movement::saturating_right(map, start); } } if positions.start <= laid_out_line.width { let selection = Selection { id: s.new_selection_id(), start: start.to_point(map), end: end.to_point(map), reversed: is_reversed, goal, }; selections.push(selection); } if row == head.row() { break; } if tail.row() > head.row() { row.0 -= 1 } else { row.0 += 1 } } s.select(selections); }) } pub fn visual_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Vim>) { if let Some(Operator::Object { around }) = self.active_operator() { self.pop_operator(window, cx); let current_mode = self.mode; let target_mode = object.target_visual_mode(current_mode, around); if target_mode != current_mode { self.switch_mode(target_mode, true, window, cx); } self.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { let mut mut_selection = selection.clone(); // all our motions assume that the current character is // after the cursor; however in the case of a visual selection // the current character is before the cursor. // But this will affect the judgment of the html tag // so the html tag needs to skip this logic. if !selection.reversed && object != Object::Tag { mut_selection.set_head( movement::left(map, mut_selection.head()), mut_selection.goal, ); } if let Some(range) = object.range(map, mut_selection, around) { if !range.is_empty() { let expand_both_ways = object.always_expands_both_ways() || selection.is_empty() || movement::right(map, selection.start) == selection.end; if expand_both_ways { selection.start = range.start; selection.end = range.end; } else if selection.reversed { selection.start = range.start; } else { selection.end = range.end; } } // In the visual selection result of a paragraph object, the cursor is // placed at the start of the last line. And in the visual mode, the // selection end is located after the end character. So, adjustment of // selection end is needed. // // We don't do this adjustment for a one-line blank paragraph since the // trailing newline is included in its selection from the beginning. if object == Object::Paragraph && range.start != range.end { let row_of_selection_end_line = selection.end.to_point(map).row; let new_selection_end = if map .buffer_snapshot .line_len(MultiBufferRow(row_of_selection_end_line)) == 0 { Point::new(row_of_selection_end_line + 1, 0) } else { Point::new(row_of_selection_end_line, 1) }; selection.end = new_selection_end.to_display_point(map); } } }); }); }); } } fn visual_insert_end_of_line( &mut self, _: &VisualInsertEndOfLine, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |_, editor, window, cx| { editor.split_selection_into_lines(&Default::default(), window, cx); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_cursors_with(|map, cursor, _| { (next_line_end(map, cursor, 1), SelectionGoal::None) }); }); }); self.switch_mode(Mode::Insert, false, window, cx); } fn visual_insert_first_non_white_space( &mut self, _: &VisualInsertFirstNonWhiteSpace, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |_, editor, window, cx| { editor.split_selection_into_lines(&Default::default(), window, cx); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_cursors_with(|map, cursor, _| { ( first_non_whitespace(map, false, cursor), SelectionGoal::None, ) }); }); }); self.switch_mode(Mode::Insert, false, window, cx); } fn toggle_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) { if self.mode == mode { self.switch_mode(Mode::Normal, false, window, cx); } else { self.switch_mode(mode, false, window, cx); } } pub fn other_end(&mut self, _: &OtherEnd, window: &mut Window, cx: &mut Context<Self>) { self.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|_, selection| { selection.reversed = !selection.reversed; }) }) }); } pub fn visual_delete(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) { self.store_visual_marks(window, cx); self.update_editor(window, cx, |vim, editor, window, cx| { let mut original_columns: HashMap<_, _> = Default::default(); let line_mode = line_mode || editor.selections.line_mode; editor.transact(window, cx, |editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { if line_mode { let mut position = selection.head(); if !selection.reversed { position = movement::left(map, position); } original_columns.insert(selection.id, position.to_point(map).column); if vim.mode == Mode::VisualBlock { *selection.end.column_mut() = map.line_len(selection.end.row()) } else if vim.mode != Mode::VisualLine { selection.start = DisplayPoint::new(selection.start.row(), 0); selection.end = map.next_line_boundary(selection.end.to_point(map)).1; if selection.end.row() == map.max_point().row() { selection.end = map.max_point(); if selection.start == selection.end { let prev_row = DisplayRow(selection.start.row().0.saturating_sub(1)); selection.start = DisplayPoint::new(prev_row, map.line_len(prev_row)); } } else { *selection.end.row_mut() += 1; *selection.end.column_mut() = 0; } } } selection.goal = SelectionGoal::None; });Lines 1–500 of 1497. Load more
use std::sync::Arc; use collections::HashMap; use editor::{ display_map::{DisplayRow, DisplaySnapshot, ToDisplayPoint}, movement, scroll::Autoscroll, Bias, DisplayPoint, Editor, ToOffset, }; use gpui::{actions, Context, Window}; use language::{Point, Selection, SelectionGoal}; use multi_buffer::MultiBufferRow; use search::BufferSearchBar; use util::ResultExt; use workspace::searchable::Direction; use crate::{ motion::{first_non_whitespace, next_line_end, start_of_line, Motion}, object::Object, state::{Mode, Operator}, Vim, }; actions!( vim, [ ToggleVisual, ToggleVisualLine, ToggleVisualBlock, VisualDelete, VisualDeleteLine, VisualYank, VisualYankLine, OtherEnd, SelectNext, SelectPrevious, SelectNextMatch, SelectPreviousMatch, SelectSmallerSyntaxNode, SelectLargerSyntaxNode, RestoreVisualSelection, VisualInsertEndOfLine, VisualInsertFirstNonWhiteSpace, ] ); pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) { Vim::action(editor, cx, |vim, _: &ToggleVisual, window, cx| { vim.toggle_mode(Mode::Visual, window, cx) }); Vim::action(editor, cx, |vim, _: &ToggleVisualLine, window, cx| { vim.toggle_mode(Mode::VisualLine, window, cx) }); Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, window, cx| { vim.toggle_mode(Mode::VisualBlock, window, cx) }); Vim::action(editor, cx, Vim::other_end); Vim::action(editor, cx, Vim::visual_insert_end_of_line); Vim::action(editor, cx, Vim::visual_insert_first_non_white_space); Vim::action(editor, cx, |vim, _: &VisualDelete, window, cx| { vim.record_current_action(cx); vim.visual_delete(false, window, cx); }); Vim::action(editor, cx, |vim, _: &VisualDeleteLine, window, cx| { vim.record_current_action(cx); vim.visual_delete(true, window, cx); }); Vim::action(editor, cx, |vim, _: &VisualYank, window, cx| { vim.visual_yank(false, window, cx) }); Vim::action(editor, cx, |vim, _: &VisualYankLine, window, cx| { vim.visual_yank(true, window, cx) }); Vim::action(editor, cx, Vim::select_next); Vim::action(editor, cx, Vim::select_previous); Vim::action(editor, cx, |vim, _: &SelectNextMatch, window, cx| { vim.select_match(Direction::Next, window, cx); }); Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, window, cx| { vim.select_match(Direction::Prev, window, cx); }); Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, window, cx| { let count = Vim::take_count(cx).unwrap_or(1); for _ in 0..count { vim.update_editor(window, cx, |_, editor, window, cx| { editor.select_larger_syntax_node(&Default::default(), window, cx); }); } }); Vim::action( editor, cx, |vim, _: &SelectSmallerSyntaxNode, window, cx| { let count = Vim::take_count(cx).unwrap_or(1); for _ in 0..count { vim.update_editor(window, cx, |_, editor, window, cx| { editor.select_smaller_syntax_node(&Default::default(), window, cx); }); } }, ); Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, window, cx| { let Some((stored_mode, reversed)) = vim.stored_visual_mode.take() else { return; }; let Some((start, end)) = vim.marks.get("<").zip(vim.marks.get(">")) else { return; }; let ranges = start .iter() .zip(end) .zip(reversed) .map(|((start, end), reversed)| (*start, *end, reversed)) .collect::<Vec<_>>(); if vim.mode.is_visual() { vim.create_visual_marks(vim.mode, window, cx); } vim.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let map = s.display_map(); let ranges = ranges .into_iter() .map(|(start, end, reversed)| { let new_end = movement::saturating_right(&map, end.to_display_point(&map)); Selection { id: s.new_selection_id(), start: start.to_offset(&map.buffer_snapshot), end: new_end.to_offset(&map, Bias::Left), reversed, goal: SelectionGoal::None, } }) .collect(); s.select(ranges); }) }); vim.switch_mode(stored_mode, true, window, cx) }); } impl Vim { pub fn visual_motion( &mut self, motion: Motion, times: Option<usize>, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |vim, editor, window, cx| { let text_layout_details = editor.text_layout_details(window); if vim.mode == Mode::VisualBlock && !matches!( motion, Motion::EndOfLine { display_lines: false } ) { let is_up_or_down = matches!(motion, Motion::Up { .. } | Motion::Down { .. }); vim.visual_block_motion(is_up_or_down, editor, window, cx, |map, point, goal| { motion.move_point(map, point, goal, times, &text_layout_details) }) } else { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { let was_reversed = selection.reversed; let mut current_head = selection.head(); // our motions assume the current character is after the cursor, // but in (forward) visual mode the current character is just // before the end of the selection. // If the file ends with a newline (which is common) we don't do this. // so that if you go to the end of such a file you can use "up" to go // to the previous line and have it work somewhat as expected. #[allow(clippy::nonminimal_bool)] if !selection.reversed && !selection.is_empty() && !(selection.end.column() == 0 && selection.end == map.max_point()) { current_head = movement::left(map, selection.end) } let Some((new_head, goal)) = motion.move_point( map, current_head, selection.goal, times, &text_layout_details, ) else { return; }; selection.set_head(new_head, goal); // ensure the current character is included in the selection. if !selection.reversed { let next_point = if vim.mode == Mode::VisualBlock { movement::saturating_right(map, selection.end) } else { movement::right(map, selection.end) }; if !(next_point.column() == 0 && next_point == map.max_point()) { selection.end = next_point; } } // vim always ensures the anchor character stays selected. // if our selection has reversed, we need to move the opposite end // to ensure the anchor is still selected. if was_reversed && !selection.reversed { selection.start = movement::left(map, selection.start); } else if !was_reversed && selection.reversed { selection.end = movement::right(map, selection.end); } }) }); } }); } pub fn visual_block_motion( &mut self, preserve_goal: bool, editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>, mut move_selection: impl FnMut( &DisplaySnapshot, DisplayPoint, SelectionGoal, ) -> Option<(DisplayPoint, SelectionGoal)>, ) { let text_layout_details = editor.text_layout_details(window); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let map = &s.display_map(); let mut head = s.newest_anchor().head().to_display_point(map); let mut tail = s.oldest_anchor().tail().to_display_point(map); let mut head_x = map.x_for_display_point(head, &text_layout_details); let mut tail_x = map.x_for_display_point(tail, &text_layout_details); let (start, end) = match s.newest_anchor().goal { SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end), SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start), _ => (tail_x.0, head_x.0), }; let mut goal = SelectionGoal::HorizontalRange { start, end }; let was_reversed = tail_x > head_x; if !was_reversed && !preserve_goal { head = movement::saturating_left(map, head); } let Some((new_head, _)) = move_selection(map, head, goal) else { return; }; head = new_head; head_x = map.x_for_display_point(head, &text_layout_details); let is_reversed = tail_x > head_x; if was_reversed && !is_reversed { tail = movement::saturating_left(map, tail); tail_x = map.x_for_display_point(tail, &text_layout_details); } else if !was_reversed && is_reversed { tail = movement::saturating_right(map, tail); tail_x = map.x_for_display_point(tail, &text_layout_details); } if !is_reversed && !preserve_goal { head = movement::saturating_right(map, head); head_x = map.x_for_display_point(head, &text_layout_details); } let positions = if is_reversed { head_x..tail_x } else { tail_x..head_x }; if !preserve_goal { goal = SelectionGoal::HorizontalRange { start: positions.start.0, end: positions.end.0, }; } let mut selections = Vec::new(); let mut row = tail.row(); loop { let laid_out_line = map.layout_row(row, &text_layout_details); let start = DisplayPoint::new( row, laid_out_line.closest_index_for_x(positions.start) as u32, ); let mut end = DisplayPoint::new(row, laid_out_line.closest_index_for_x(positions.end) as u32); if end <= start { if start.column() == map.line_len(start.row()) { end = start; } else { end = movement::saturating_right(map, start); } } if positions.start <= laid_out_line.width { let selection = Selection { id: s.new_selection_id(), start: start.to_point(map), end: end.to_point(map), reversed: is_reversed, goal, }; selections.push(selection); } if row == head.row() { break; } if tail.row() > head.row() { row.0 -= 1 } else { row.0 += 1 } } s.select(selections); }) } pub fn visual_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Vim>) { if let Some(Operator::Object { around }) = self.active_operator() { self.pop_operator(window, cx); let current_mode = self.mode; let target_mode = object.target_visual_mode(current_mode, around); if target_mode != current_mode { self.switch_mode(target_mode, true, window, cx); } self.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { let mut mut_selection = selection.clone(); // all our motions assume that the current character is // after the cursor; however in the case of a visual selection // the current character is before the cursor. // But this will affect the judgment of the html tag // so the html tag needs to skip this logic. if !selection.reversed && object != Object::Tag { mut_selection.set_head( movement::left(map, mut_selection.head()), mut_selection.goal, ); } if let Some(range) = object.range(map, mut_selection, around) { if !range.is_empty() { let expand_both_ways = object.always_expands_both_ways() || selection.is_empty() || movement::right(map, selection.start) == selection.end; if expand_both_ways { selection.start = range.start; selection.end = range.end; } else if selection.reversed { selection.start = range.start; } else { selection.end = range.end; } } // In the visual selection result of a paragraph object, the cursor is // placed at the start of the last line. And in the visual mode, the // selection end is located after the end character. So, adjustment of // selection end is needed. // // We don't do this adjustment for a one-line blank paragraph since the // trailing newline is included in its selection from the beginning. if object == Object::Paragraph && range.start != range.end { let row_of_selection_end_line = selection.end.to_point(map).row; let new_selection_end = if map .buffer_snapshot .line_len(MultiBufferRow(row_of_selection_end_line)) == 0 { Point::new(row_of_selection_end_line + 1, 0) } else { Point::new(row_of_selection_end_line, 1) }; selection.end = new_selection_end.to_display_point(map); } } }); }); }); } } fn visual_insert_end_of_line( &mut self, _: &VisualInsertEndOfLine, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |_, editor, window, cx| { editor.split_selection_into_lines(&Default::default(), window, cx); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_cursors_with(|map, cursor, _| { (next_line_end(map, cursor, 1), SelectionGoal::None) }); }); }); self.switch_mode(Mode::Insert, false, window, cx); } fn visual_insert_first_non_white_space( &mut self, _: &VisualInsertFirstNonWhiteSpace, window: &mut Window, cx: &mut Context<Self>, ) { self.update_editor(window, cx, |_, editor, window, cx| { editor.split_selection_into_lines(&Default::default(), window, cx); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_cursors_with(|map, cursor, _| { ( first_non_whitespace(map, false, cursor), SelectionGoal::None, ) }); }); }); self.switch_mode(Mode::Insert, false, window, cx); } fn toggle_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) { if self.mode == mode { self.switch_mode(Mode::Normal, false, window, cx); } else { self.switch_mode(mode, false, window, cx); } } pub fn other_end(&mut self, _: &OtherEnd, window: &mut Window, cx: &mut Context<Self>) { self.update_editor(window, cx, |_, editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|_, selection| { selection.reversed = !selection.reversed; }) }) }); } pub fn visual_delete(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) { self.store_visual_marks(window, cx); self.update_editor(window, cx, |vim, editor, window, cx| { let mut original_columns: HashMap<_, _> = Default::default(); let line_mode = line_mode || editor.selections.line_mode; editor.transact(window, cx, |editor, window, cx| { editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.move_with(|map, selection| { if line_mode { let mut position = selection.head(); if !selection.reversed { position = movement::left(map, position); } original_columns.insert(selection.id, position.to_point(map).column); if vim.mode == Mode::VisualBlock { *selection.end.column_mut() = map.line_len(selection.end.row()) } else if vim.mode != Mode::VisualLine { selection.start = DisplayPoint::new(selection.start.row(), 0); selection.end = map.next_line_boundary(selection.end.to_point(map)).1; if selection.end.row() == map.max_point().row() { selection.end = map.max_point(); if selection.start == selection.end { let prev_row = DisplayRow(selection.start.row().0.saturating_sub(1)); selection.start = DisplayPoint::new(prev_row, map.line_len(prev_row)); } } else { *selection.end.row_mut() += 1; *selection.end.column_mut() = 0; } } } selection.goal = SelectionGoal::None; });