Conrad IrwinWork on tests4143d3a2 years agoHistory
1107 lines · 34.7 KB · Rust
RawDownload
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.