use editor::{ display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint}, movement::{self, find_boundary, find_preceding_boundary, FindRange, TextLayoutDetails}, Bias, DisplayPoint, ToOffset,};use gpui::{actions, impl_actions, px, ViewContext, WindowContext};use language::{char_kind, CharKind, Point, Selection, SelectionGoal};use serde::Deserialize;use workspace::Workspace; use crate::{ normal::normal_motion, state::{Mode, Operator}, utils::coerce_punctuation, visual::visual_motion, Vim,}; #[derive(Clone, Debug, PartialEq, Eq)]pub enum Motion { Left, Backspace, Down { display_lines: bool }, Up { display_lines: bool }, Right, NextWordStart { ignore_punctuation: bool }, NextWordEnd { ignore_punctuation: bool }, PreviousWordStart { ignore_punctuation: bool }, FirstNonWhitespace { display_lines: bool }, CurrentLine, StartOfLine { display_lines: bool }, EndOfLine { display_lines: bool }, StartOfParagraph, EndOfParagraph, StartOfDocument, EndOfDocument, Matching, FindForward { before: bool, char: char }, FindBackward { after: bool, char: char }, NextLineStart, StartOfLineDownward, EndOfLineDownward, GoToColumn,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]struct NextWordStart { #[serde(default)] ignore_punctuation: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]struct NextWordEnd { #[serde(default)] ignore_punctuation: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]struct PreviousWordStart { #[serde(default)] ignore_punctuation: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]pub(crate) struct Up { #[serde(default)] pub(crate) display_lines: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]pub(crate) struct Down { #[serde(default)] pub(crate) display_lines: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]struct FirstNonWhitespace { #[serde(default)] display_lines: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]struct EndOfLine { #[serde(default)] display_lines: bool,} #[derive(Clone, Deserialize, PartialEq)]#[serde(rename_all = "camelCase")]pub struct StartOfLine { #[serde(default)] pub(crate) display_lines: bool,} #[derive(Clone, Deserialize, PartialEq)]struct RepeatFind { #[serde(default)] backwards: bool,} impl_actions!( vim, [ RepeatFind, StartOfLine, EndOfLine, FirstNonWhitespace, Down, Up, PreviousWordStart, NextWordEnd, NextWordStart ]); actions!( vim, [ Left, Backspace, Right, CurrentLine, StartOfParagraph, EndOfParagraph, StartOfDocument, EndOfDocument, Matching, NextLineStart, StartOfLineDownward, EndOfLineDownward, GoToColumn, ]); pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) { workspace.register_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx)); workspace .register_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx)); workspace.register_action(|_: &mut Workspace, action: &Down, cx: _| { motion( Motion::Down { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &Up, cx: _| { motion( Motion::Up { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx)); workspace.register_action(|_: &mut Workspace, action: &FirstNonWhitespace, cx: _| { motion( Motion::FirstNonWhitespace { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &StartOfLine, cx: _| { motion( Motion::StartOfLine { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &EndOfLine, cx: _| { motion( Motion::EndOfLine { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, _: &CurrentLine, cx: _| { motion(Motion::CurrentLine, cx) }); workspace.register_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| { motion(Motion::StartOfParagraph, cx) }); workspace.register_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| { motion(Motion::EndOfParagraph, cx) }); workspace.register_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| { motion(Motion::StartOfDocument, cx) }); workspace.register_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| { motion(Motion::EndOfDocument, cx) }); workspace .register_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx)); workspace.register_action( |_: &mut Workspace, &NextWordStart { ignore_punctuation }: &NextWordStart, cx: _| { motion(Motion::NextWordStart { ignore_punctuation }, cx) }, ); workspace.register_action( |_: &mut Workspace, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx: _| { motion(Motion::NextWordEnd { ignore_punctuation }, cx) }, ); workspace.register_action( |_: &mut Workspace, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) }, ); workspace.register_action(|_: &mut Workspace, &NextLineStart, cx: _| { motion(Motion::NextLineStart, cx) }); workspace.register_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| { motion(Motion::StartOfLineDownward, cx) }); workspace.register_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| { motion(Motion::EndOfLineDownward, cx) }); workspace .register_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx)); workspace.register_action(|_: &mut Workspace, action: &RepeatFind, cx: _| { repeat_motion(action.backwards, cx) });} pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { if let Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. }) = Vim::read(cx).active_operator() { Vim::update(cx, |vim, cx| vim.pop_operator(cx)); } let count = Vim::update(cx, |vim, cx| vim.take_count(cx)); let operator = Vim::read(cx).active_operator(); match Vim::read(cx).state().mode { Mode::Normal => normal_motion(motion, operator, count, cx), Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_motion(motion, count, cx), Mode::Insert => { // Shouldn't execute a motion in insert mode. Ignoring } } Vim::update(cx, |vim, cx| vim.clear_operator(cx));} fn repeat_motion(backwards: bool, cx: &mut WindowContext) { let find = match Vim::read(cx).workspace_state.last_find.clone() { Some(Motion::FindForward { before, char }) => { if backwards { Motion::FindBackward { after: before, char, } } else { Motion::FindForward { before, char } } } Some(Motion::FindBackward { after, char }) => { if backwards { Motion::FindForward { before: after, char, } } else { Motion::FindBackward { after, char } } } _ => return, }; motion(find, cx)} // Motion handling is specified here:// https://github.com/vim/vim/blob/master/runtime/doc/motion.txtimpl Motion { pub fn linewise(&self) -> bool { use Motion::*; match self { Down { .. } | Up { .. } | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart | StartOfLineDownward | StartOfParagraph | EndOfParagraph => true, EndOfLine { .. } | NextWordEnd { .. } | Matching | FindForward { .. } | Left | Backspace | Right | StartOfLine { .. } | EndOfLineDownward | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } => false, } } pub fn infallible(&self) -> bool { use Motion::*; match self { StartOfDocument | EndOfDocument | CurrentLine => true, Down { .. } | Up { .. } | EndOfLine { .. } | NextWordEnd { .. } | Matching | FindForward { .. } | Left | Backspace | Right | StartOfLine { .. } | StartOfParagraph | EndOfParagraph | StartOfLineDownward | EndOfLineDownward | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } | NextLineStart => false, } } pub fn inclusive(&self) -> bool { use Motion::*; match self { Down { .. } | Up { .. } | StartOfDocument | EndOfDocument | CurrentLine | EndOfLine { .. } | EndOfLineDownward | NextWordEnd { .. } | Matching | FindForward { .. } | NextLineStart => true, Left | Backspace | Right | StartOfLine { .. } | StartOfLineDownward | StartOfParagraph | EndOfParagraph | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } => false, } } pub fn move_point( &self, map: &DisplaySnapshot, point: DisplayPoint, goal: SelectionGoal, maybe_times: Option<usize>, text_layout_details: &TextLayoutDetails, ) -> Option<(DisplayPoint, SelectionGoal)> { let times = maybe_times.unwrap_or(1); use Motion::*; let infallible = self.infallible(); let (new_point, goal) = match self { Left => (left(map, point, times), SelectionGoal::None), Backspace => (backspace(map, point, times), SelectionGoal::None), Down { display_lines: false, } => up_down_buffer_rows(map, point, goal, times as isize, &text_layout_details), Down { display_lines: true, } => down_display(map, point, goal, times, &text_layout_details), Up { display_lines: false, } => up_down_buffer_rows(map, point, goal, 0 - times as isize, &text_layout_details), Up { display_lines: true, } => up_display(map, point, goal, times, &text_layout_details), Right => (right(map, point, times), SelectionGoal::None), NextWordStart { ignore_punctuation } => ( next_word_start(map, point, *ignore_punctuation, times), SelectionGoal::None, ), NextWordEnd { ignore_punctuation } => ( next_word_end(map, point, *ignore_punctuation, times), SelectionGoal::None, ), PreviousWordStart { ignore_punctuation } => ( previous_word_start(map, point, *ignore_punctuation, times), SelectionGoal::None, ), FirstNonWhitespace { display_lines } => ( first_non_whitespace(map, *display_lines, point), SelectionGoal::None, ), StartOfLine { display_lines } => ( start_of_line(map, *display_lines, point), SelectionGoal::None, ), EndOfLine { display_lines } => { (end_of_line(map, *display_lines, point), SelectionGoal::None) } StartOfParagraph => ( movement::start_of_paragraph(map, point, times), SelectionGoal::None, ), EndOfParagraph => ( map.clip_at_line_end(movement::end_of_paragraph(map, point, times)), SelectionGoal::None, ), CurrentLine => (next_line_end(map, point, times), SelectionGoal::None), StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => ( end_of_document(map, point, maybe_times), SelectionGoal::None, ), Matching => (matching(map, point), SelectionGoal::None), FindForward { before, char } => ( find_forward(map, point, *before, *char, times), SelectionGoal::None, ), FindBackward { after, char } => ( find_backward(map, point, *after, *char, times), SelectionGoal::None, ), NextLineStart => (next_line_start(map, point, times), SelectionGoal::None), StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None), EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None), GoToColumn => (go_to_column(map, point, times), SelectionGoal::None), }; (new_point != point || infallible).then_some((new_point, goal)) } // Expands a selection using self motion for an operator pub fn expand_selection( &self, map: &DisplaySnapshot, selection: &mut Selection<DisplayPoint>, times: Option<usize>, expand_to_surrounding_newline: bool, text_layout_details: &TextLayoutDetails, ) -> bool { if let Some((new_head, goal)) = self.move_point( map, selection.head(), selection.goal, times, &text_layout_details, ) { selection.set_head(new_head, goal); if self.linewise() { selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; if expand_to_surrounding_newline { if selection.end.row() < map.max_point().row() { *selection.end.row_mut() += 1; *selection.end.column_mut() = 0; selection.end = map.clip_point(selection.end, Bias::Right); // Don't reset the end here return true; } else if selection.start.row() > 0 { *selection.start.row_mut() -= 1; *selection.start.column_mut() = map.line_len(selection.start.row()); selection.start = map.clip_point(selection.start, Bias::Left); } } (_, selection.end) = map.next_line_boundary(selection.end.to_point(map)); } else { // Another special case: When using the "w" motion in combination with an // operator and the last word moved over is at the end of a line, the end of // that word becomes the end of the operated text, not the first word in the // next line. if let Motion::NextWordStart { ignore_punctuation: _, } = self { let start_row = selection.start.to_point(&map).row; if selection.end.to_point(&map).row > start_row {Lines 1–500 of 1107. Load more
use editor::{ display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint}, movement::{self, find_boundary, find_preceding_boundary, FindRange, TextLayoutDetails}, Bias, DisplayPoint, ToOffset, }; use gpui::{actions, impl_actions, px, ViewContext, WindowContext}; use language::{char_kind, CharKind, Point, Selection, SelectionGoal}; use serde::Deserialize; use workspace::Workspace; use crate::{ normal::normal_motion, state::{Mode, Operator}, utils::coerce_punctuation, visual::visual_motion, Vim, }; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Motion { Left, Backspace, Down { display_lines: bool }, Up { display_lines: bool }, Right, NextWordStart { ignore_punctuation: bool }, NextWordEnd { ignore_punctuation: bool }, PreviousWordStart { ignore_punctuation: bool }, FirstNonWhitespace { display_lines: bool }, CurrentLine, StartOfLine { display_lines: bool }, EndOfLine { display_lines: bool }, StartOfParagraph, EndOfParagraph, StartOfDocument, EndOfDocument, Matching, FindForward { before: bool, char: char }, FindBackward { after: bool, char: char }, NextLineStart, StartOfLineDownward, EndOfLineDownward, GoToColumn, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct NextWordStart { #[serde(default)] ignore_punctuation: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct NextWordEnd { #[serde(default)] ignore_punctuation: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct PreviousWordStart { #[serde(default)] ignore_punctuation: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub(crate) struct Up { #[serde(default)] pub(crate) display_lines: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub(crate) struct Down { #[serde(default)] pub(crate) display_lines: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct FirstNonWhitespace { #[serde(default)] display_lines: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct EndOfLine { #[serde(default)] display_lines: bool, } #[derive(Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StartOfLine { #[serde(default)] pub(crate) display_lines: bool, } #[derive(Clone, Deserialize, PartialEq)] struct RepeatFind { #[serde(default)] backwards: bool, } impl_actions!( vim, [ RepeatFind, StartOfLine, EndOfLine, FirstNonWhitespace, Down, Up, PreviousWordStart, NextWordEnd, NextWordStart ] ); actions!( vim, [ Left, Backspace, Right, CurrentLine, StartOfParagraph, EndOfParagraph, StartOfDocument, EndOfDocument, Matching, NextLineStart, StartOfLineDownward, EndOfLineDownward, GoToColumn, ] ); pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) { workspace.register_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx)); workspace .register_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx)); workspace.register_action(|_: &mut Workspace, action: &Down, cx: _| { motion( Motion::Down { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &Up, cx: _| { motion( Motion::Up { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx)); workspace.register_action(|_: &mut Workspace, action: &FirstNonWhitespace, cx: _| { motion( Motion::FirstNonWhitespace { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &StartOfLine, cx: _| { motion( Motion::StartOfLine { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, action: &EndOfLine, cx: _| { motion( Motion::EndOfLine { display_lines: action.display_lines, }, cx, ) }); workspace.register_action(|_: &mut Workspace, _: &CurrentLine, cx: _| { motion(Motion::CurrentLine, cx) }); workspace.register_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| { motion(Motion::StartOfParagraph, cx) }); workspace.register_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| { motion(Motion::EndOfParagraph, cx) }); workspace.register_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| { motion(Motion::StartOfDocument, cx) }); workspace.register_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| { motion(Motion::EndOfDocument, cx) }); workspace .register_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx)); workspace.register_action( |_: &mut Workspace, &NextWordStart { ignore_punctuation }: &NextWordStart, cx: _| { motion(Motion::NextWordStart { ignore_punctuation }, cx) }, ); workspace.register_action( |_: &mut Workspace, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx: _| { motion(Motion::NextWordEnd { ignore_punctuation }, cx) }, ); workspace.register_action( |_: &mut Workspace, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) }, ); workspace.register_action(|_: &mut Workspace, &NextLineStart, cx: _| { motion(Motion::NextLineStart, cx) }); workspace.register_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| { motion(Motion::StartOfLineDownward, cx) }); workspace.register_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| { motion(Motion::EndOfLineDownward, cx) }); workspace .register_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx)); workspace.register_action(|_: &mut Workspace, action: &RepeatFind, cx: _| { repeat_motion(action.backwards, cx) }); } pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { if let Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. }) = Vim::read(cx).active_operator() { Vim::update(cx, |vim, cx| vim.pop_operator(cx)); } let count = Vim::update(cx, |vim, cx| vim.take_count(cx)); let operator = Vim::read(cx).active_operator(); match Vim::read(cx).state().mode { Mode::Normal => normal_motion(motion, operator, count, cx), Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_motion(motion, count, cx), Mode::Insert => { // Shouldn't execute a motion in insert mode. Ignoring } } Vim::update(cx, |vim, cx| vim.clear_operator(cx)); } fn repeat_motion(backwards: bool, cx: &mut WindowContext) { let find = match Vim::read(cx).workspace_state.last_find.clone() { Some(Motion::FindForward { before, char }) => { if backwards { Motion::FindBackward { after: before, char, } } else { Motion::FindForward { before, char } } } Some(Motion::FindBackward { after, char }) => { if backwards { Motion::FindForward { before: after, char, } } else { Motion::FindBackward { after, char } } } _ => return, }; motion(find, cx) } // Motion handling is specified here: // https://github.com/vim/vim/blob/master/runtime/doc/motion.txt impl Motion { pub fn linewise(&self) -> bool { use Motion::*; match self { Down { .. } | Up { .. } | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart | StartOfLineDownward | StartOfParagraph | EndOfParagraph => true, EndOfLine { .. } | NextWordEnd { .. } | Matching | FindForward { .. } | Left | Backspace | Right | StartOfLine { .. } | EndOfLineDownward | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } => false, } } pub fn infallible(&self) -> bool { use Motion::*; match self { StartOfDocument | EndOfDocument | CurrentLine => true, Down { .. } | Up { .. } | EndOfLine { .. } | NextWordEnd { .. } | Matching | FindForward { .. } | Left | Backspace | Right | StartOfLine { .. } | StartOfParagraph | EndOfParagraph | StartOfLineDownward | EndOfLineDownward | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } | NextLineStart => false, } } pub fn inclusive(&self) -> bool { use Motion::*; match self { Down { .. } | Up { .. } | StartOfDocument | EndOfDocument | CurrentLine | EndOfLine { .. } | EndOfLineDownward | NextWordEnd { .. } | Matching | FindForward { .. } | NextLineStart => true, Left | Backspace | Right | StartOfLine { .. } | StartOfLineDownward | StartOfParagraph | EndOfParagraph | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } => false, } } pub fn move_point( &self, map: &DisplaySnapshot, point: DisplayPoint, goal: SelectionGoal, maybe_times: Option<usize>, text_layout_details: &TextLayoutDetails, ) -> Option<(DisplayPoint, SelectionGoal)> { let times = maybe_times.unwrap_or(1); use Motion::*; let infallible = self.infallible(); let (new_point, goal) = match self { Left => (left(map, point, times), SelectionGoal::None), Backspace => (backspace(map, point, times), SelectionGoal::None), Down { display_lines: false, } => up_down_buffer_rows(map, point, goal, times as isize, &text_layout_details), Down { display_lines: true, } => down_display(map, point, goal, times, &text_layout_details), Up { display_lines: false, } => up_down_buffer_rows(map, point, goal, 0 - times as isize, &text_layout_details), Up { display_lines: true, } => up_display(map, point, goal, times, &text_layout_details), Right => (right(map, point, times), SelectionGoal::None), NextWordStart { ignore_punctuation } => ( next_word_start(map, point, *ignore_punctuation, times), SelectionGoal::None, ), NextWordEnd { ignore_punctuation } => ( next_word_end(map, point, *ignore_punctuation, times), SelectionGoal::None, ), PreviousWordStart { ignore_punctuation } => ( previous_word_start(map, point, *ignore_punctuation, times), SelectionGoal::None, ), FirstNonWhitespace { display_lines } => ( first_non_whitespace(map, *display_lines, point), SelectionGoal::None, ), StartOfLine { display_lines } => ( start_of_line(map, *display_lines, point), SelectionGoal::None, ), EndOfLine { display_lines } => { (end_of_line(map, *display_lines, point), SelectionGoal::None) } StartOfParagraph => ( movement::start_of_paragraph(map, point, times), SelectionGoal::None, ), EndOfParagraph => ( map.clip_at_line_end(movement::end_of_paragraph(map, point, times)), SelectionGoal::None, ), CurrentLine => (next_line_end(map, point, times), SelectionGoal::None), StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => ( end_of_document(map, point, maybe_times), SelectionGoal::None, ), Matching => (matching(map, point), SelectionGoal::None), FindForward { before, char } => ( find_forward(map, point, *before, *char, times), SelectionGoal::None, ), FindBackward { after, char } => ( find_backward(map, point, *after, *char, times), SelectionGoal::None, ), NextLineStart => (next_line_start(map, point, times), SelectionGoal::None), StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None), EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None), GoToColumn => (go_to_column(map, point, times), SelectionGoal::None), }; (new_point != point || infallible).then_some((new_point, goal)) } // Expands a selection using self motion for an operator pub fn expand_selection( &self, map: &DisplaySnapshot, selection: &mut Selection<DisplayPoint>, times: Option<usize>, expand_to_surrounding_newline: bool, text_layout_details: &TextLayoutDetails, ) -> bool { if let Some((new_head, goal)) = self.move_point( map, selection.head(), selection.goal, times, &text_layout_details, ) { selection.set_head(new_head, goal); if self.linewise() { selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; if expand_to_surrounding_newline { if selection.end.row() < map.max_point().row() { *selection.end.row_mut() += 1; *selection.end.column_mut() = 0; selection.end = map.clip_point(selection.end, Bias::Right); // Don't reset the end here return true; } else if selection.start.row() > 0 { *selection.start.row_mut() -= 1; *selection.start.column_mut() = map.line_len(selection.start.row()); selection.start = map.clip_point(selection.start, Bias::Left); } } (_, selection.end) = map.next_line_boundary(selection.end.to_point(map)); } else { // Another special case: When using the "w" motion in combination with an // operator and the last word moved over is at the end of a line, the end of // that word becomes the end of the operated text, not the first word in the // next line. if let Motion::NextWordStart { ignore_punctuation: _, } = self { let start_row = selection.start.to_point(&map).row; if selection.end.to_point(&map).row > start_row {