From 3cf98c4fae820b5c609d8498fa0726d27b8a8350 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sun, 22 Oct 2023 22:04:55 -0600 Subject: [PATCH] Add | as a bracket and a motion Although vim/nvim doesn't have | as brackets, it's common in langauges like Rust and Ruby, and I expect it to work. --- assets/keymaps/vim.json | 224 ++++-------------- crates/vim/src/motion.rs | 12 + crates/vim/src/object.rs | 62 ++++- ...ltiline_surrounding_character_objects.json | 2 +- 4 files changed, 119 insertions(+), 181 deletions(-) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index ea025747d8..81235bb72a 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -39,6 +39,7 @@ "w": "vim::NextWordStart", "{": "vim::StartOfParagraph", "}": "vim::EndOfParagraph", + "|": "vim::GoToColumn", "shift-w": [ "vim::NextWordStart", { @@ -97,14 +98,8 @@ "ctrl-o": "pane::GoBack", "ctrl-i": "pane::GoForward", "ctrl-]": "editor::GoToDefinition", - "escape": [ - "vim::SwitchMode", - "Normal" - ], - "ctrl+[": [ - "vim::SwitchMode", - "Normal" - ], + "escape": ["vim::SwitchMode", "Normal"], + "ctrl+[": ["vim::SwitchMode", "Normal"], "v": "vim::ToggleVisual", "shift-v": "vim::ToggleVisualLine", "ctrl-v": "vim::ToggleVisualBlock", @@ -233,123 +228,36 @@ } ], // Count support - "1": [ - "vim::Number", - 1 - ], - "2": [ - "vim::Number", - 2 - ], - "3": [ - "vim::Number", - 3 - ], - "4": [ - "vim::Number", - 4 - ], - "5": [ - "vim::Number", - 5 - ], - "6": [ - "vim::Number", - 6 - ], - "7": [ - "vim::Number", - 7 - ], - "8": [ - "vim::Number", - 8 - ], - "9": [ - "vim::Number", - 9 - ], + "1": ["vim::Number", 1], + "2": ["vim::Number", 2], + "3": ["vim::Number", 3], + "4": ["vim::Number", 4], + "5": ["vim::Number", 5], + "6": ["vim::Number", 6], + "7": ["vim::Number", 7], + "8": ["vim::Number", 8], + "9": ["vim::Number", 9], // window related commands (ctrl-w X) - "ctrl-w left": [ - "workspace::ActivatePaneInDirection", - "Left" - ], - "ctrl-w right": [ - "workspace::ActivatePaneInDirection", - "Right" - ], - "ctrl-w up": [ - "workspace::ActivatePaneInDirection", - "Up" - ], - "ctrl-w down": [ - "workspace::ActivatePaneInDirection", - "Down" - ], - "ctrl-w h": [ - "workspace::ActivatePaneInDirection", - "Left" - ], - "ctrl-w l": [ - "workspace::ActivatePaneInDirection", - "Right" - ], - "ctrl-w k": [ - "workspace::ActivatePaneInDirection", - "Up" - ], - "ctrl-w j": [ - "workspace::ActivatePaneInDirection", - "Down" - ], - "ctrl-w ctrl-h": [ - "workspace::ActivatePaneInDirection", - "Left" - ], - "ctrl-w ctrl-l": [ - "workspace::ActivatePaneInDirection", - "Right" - ], - "ctrl-w ctrl-k": [ - "workspace::ActivatePaneInDirection", - "Up" - ], - "ctrl-w ctrl-j": [ - "workspace::ActivatePaneInDirection", - "Down" - ], - "ctrl-w shift-left": [ - "workspace::SwapPaneInDirection", - "Left" - ], - "ctrl-w shift-right": [ - "workspace::SwapPaneInDirection", - "Right" - ], - "ctrl-w shift-up": [ - "workspace::SwapPaneInDirection", - "Up" - ], - "ctrl-w shift-down": [ - "workspace::SwapPaneInDirection", - "Down" - ], - "ctrl-w shift-h": [ - "workspace::SwapPaneInDirection", - "Left" - ], - "ctrl-w shift-l": [ - "workspace::SwapPaneInDirection", - "Right" - ], - "ctrl-w shift-k": [ - "workspace::SwapPaneInDirection", - "Up" - ], - "ctrl-w shift-j": [ - "workspace::SwapPaneInDirection", - "Down" - ], + "ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"], + "ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"], + "ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"], + "ctrl-w down": ["workspace::ActivatePaneInDirection", "Down"], + "ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"], + "ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"], + "ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"], + "ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"], + "ctrl-w ctrl-h": ["workspace::ActivatePaneInDirection", "Left"], + "ctrl-w ctrl-l": ["workspace::ActivatePaneInDirection", "Right"], + "ctrl-w ctrl-k": ["workspace::ActivatePaneInDirection", "Up"], + "ctrl-w ctrl-j": ["workspace::ActivatePaneInDirection", "Down"], + "ctrl-w shift-left": ["workspace::SwapPaneInDirection", "Left"], + "ctrl-w shift-right": ["workspace::SwapPaneInDirection", "Right"], + "ctrl-w shift-up": ["workspace::SwapPaneInDirection", "Up"], + "ctrl-w shift-down": ["workspace::SwapPaneInDirection", "Down"], + "ctrl-w shift-h": ["workspace::SwapPaneInDirection", "Left"], + "ctrl-w shift-l": ["workspace::SwapPaneInDirection", "Right"], + "ctrl-w shift-k": ["workspace::SwapPaneInDirection", "Up"], + "ctrl-w shift-j": ["workspace::SwapPaneInDirection", "Down"], "ctrl-w g t": "pane::ActivateNextItem", "ctrl-w ctrl-g t": "pane::ActivateNextItem", "ctrl-w g shift-t": "pane::ActivatePrevItem", @@ -371,14 +279,8 @@ "ctrl-w ctrl-q": "pane::CloseAllItems", "ctrl-w o": "workspace::CloseInactiveTabsAndPanes", "ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes", - "ctrl-w n": [ - "workspace::NewFileInDirection", - "Up" - ], - "ctrl-w ctrl-n": [ - "workspace::NewFileInDirection", - "Up" - ] + "ctrl-w n": ["workspace::NewFileInDirection", "Up"], + "ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"] } }, { @@ -393,21 +295,12 @@ "context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting", "bindings": { ".": "vim::Repeat", - "c": [ - "vim::PushOperator", - "Change" - ], + "c": ["vim::PushOperator", "Change"], "shift-c": "vim::ChangeToEndOfLine", - "d": [ - "vim::PushOperator", - "Delete" - ], + "d": ["vim::PushOperator", "Delete"], "shift-d": "vim::DeleteToEndOfLine", "shift-j": "vim::JoinLines", - "y": [ - "vim::PushOperator", - "Yank" - ], + "y": ["vim::PushOperator", "Yank"], "shift-y": "vim::YankLine", "i": "vim::InsertBefore", "shift-i": "vim::InsertFirstNonWhitespace", @@ -443,10 +336,7 @@ "backwards": true } ], - "r": [ - "vim::PushOperator", - "Replace" - ], + "r": ["vim::PushOperator", "Replace"], "s": "vim::Substitute", "shift-s": "vim::SubstituteLine", "> >": "editor::Indent", @@ -458,10 +348,7 @@ { "context": "Editor && VimCount", "bindings": { - "0": [ - "vim::Number", - 0 - ] + "0": ["vim::Number", 0] } }, { @@ -497,12 +384,15 @@ "'": "vim::Quotes", "`": "vim::BackQuotes", "\"": "vim::DoubleQuotes", + "|": "vim::VerticalBars", "(": "vim::Parentheses", ")": "vim::Parentheses", + "b": "vim::Parentheses", "[": "vim::SquareBrackets", "]": "vim::SquareBrackets", "{": "vim::CurlyBrackets", "}": "vim::CurlyBrackets", + "shift-b": "vim::CurlyBrackets", "<": "vim::AngleBrackets", ">": "vim::AngleBrackets" } @@ -548,22 +438,10 @@ "shift-i": "vim::InsertBefore", "shift-a": "vim::InsertAfter", "shift-j": "vim::JoinLines", - "r": [ - "vim::PushOperator", - "Replace" - ], - "ctrl-c": [ - "vim::SwitchMode", - "Normal" - ], - "escape": [ - "vim::SwitchMode", - "Normal" - ], - "ctrl+[": [ - "vim::SwitchMode", - "Normal" - ], + "r": ["vim::PushOperator", "Replace"], + "ctrl-c": ["vim::SwitchMode", "Normal"], + "escape": ["vim::SwitchMode", "Normal"], + "ctrl+[": ["vim::SwitchMode", "Normal"], ">": "editor::Indent", "<": "editor::Outdent", "i": [ @@ -602,14 +480,8 @@ "bindings": { "tab": "vim::Tab", "enter": "vim::Enter", - "escape": [ - "vim::SwitchMode", - "Normal" - ], - "ctrl+[": [ - "vim::SwitchMode", - "Normal" - ] + "escape": ["vim::SwitchMode", "Normal"], + "ctrl+[": ["vim::SwitchMode", "Normal"] } }, { diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index e8d954bc13..92010b63c0 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -40,6 +40,7 @@ pub enum Motion { NextLineStart, StartOfLineDownward, EndOfLineDownward, + GoToColumn, } #[derive(Clone, Deserialize, PartialEq)] @@ -119,6 +120,7 @@ actions!( NextLineStart, StartOfLineDownward, EndOfLineDownward, + GoToColumn, ] ); impl_actions!( @@ -215,6 +217,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| { motion(Motion::EndOfLineDownward, cx) }); + cx.add_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx)); cx.add_action(|_: &mut Workspace, action: &RepeatFind, cx: _| { repeat_motion(action.backwards, cx) }) @@ -292,6 +295,7 @@ impl Motion { | Right | StartOfLine { .. } | EndOfLineDownward + | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } @@ -317,6 +321,7 @@ impl Motion { | EndOfParagraph | StartOfLineDownward | EndOfLineDownward + | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } @@ -346,6 +351,7 @@ impl Motion { | StartOfLineDownward | StartOfParagraph | EndOfParagraph + | GoToColumn | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } @@ -429,6 +435,7 @@ impl Motion { 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)) @@ -919,6 +926,11 @@ fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> first_non_whitespace(map, false, correct_line) } +fn go_to_column(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint { + let correct_line = start_of_relative_buffer_row(map, point, 0); + right(map, correct_line, times.saturating_sub(1)) +} + pub(crate) fn next_line_end( map: &DisplaySnapshot, mut point: DisplayPoint, diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index e4a0659f3f..6521390580 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -20,6 +20,7 @@ pub enum Object { Quotes, BackQuotes, DoubleQuotes, + VerticalBars, Parentheses, SquareBrackets, CurlyBrackets, @@ -40,6 +41,7 @@ actions!( Quotes, BackQuotes, DoubleQuotes, + VerticalBars, Parentheses, SquareBrackets, CurlyBrackets, @@ -64,6 +66,7 @@ pub fn init(cx: &mut AppContext) { }); cx.add_action(|_: &mut Workspace, _: &CurlyBrackets, cx: _| object(Object::CurlyBrackets, cx)); cx.add_action(|_: &mut Workspace, _: &AngleBrackets, cx: _| object(Object::AngleBrackets, cx)); + cx.add_action(|_: &mut Workspace, _: &VerticalBars, cx: _| object(Object::VerticalBars, cx)); } fn object(object: Object, cx: &mut WindowContext) { @@ -79,9 +82,11 @@ fn object(object: Object, cx: &mut WindowContext) { impl Object { pub fn is_multiline(self) -> bool { match self { - Object::Word { .. } | Object::Quotes | Object::BackQuotes | Object::DoubleQuotes => { - false - } + Object::Word { .. } + | Object::Quotes + | Object::BackQuotes + | Object::VerticalBars + | Object::DoubleQuotes => false, Object::Sentence | Object::Parentheses | Object::AngleBrackets @@ -96,6 +101,7 @@ impl Object { Object::Quotes | Object::BackQuotes | Object::DoubleQuotes + | Object::VerticalBars | Object::Parentheses | Object::SquareBrackets | Object::CurlyBrackets @@ -111,6 +117,7 @@ impl Object { | Object::Quotes | Object::BackQuotes | Object::DoubleQuotes + | Object::VerticalBars | Object::Parentheses | Object::SquareBrackets | Object::CurlyBrackets @@ -142,6 +149,9 @@ impl Object { Object::DoubleQuotes => { surrounding_markers(map, relative_to, around, self.is_multiline(), '"', '"') } + Object::VerticalBars => { + surrounding_markers(map, relative_to, around, self.is_multiline(), '|', '|') + } Object::Parentheses => { surrounding_markers(map, relative_to, around, self.is_multiline(), '(', ')') } @@ -568,7 +578,10 @@ fn surrounding_markers( mod test { use indoc::indoc; - use crate::test::{ExemptionFeatures, NeovimBackedTestContext}; + use crate::{ + state::Mode, + test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext}, + }; const WORD_LOCATIONS: &'static str = indoc! {" The quick ˇbrowˇnˇ••• @@ -940,6 +953,47 @@ mod test { .await; } + #[gpui::test] + async fn test_vertical_bars(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.set_state( + indoc! {" + fn boop() { + baz(ˇ|a, b| { bar(|j, k| { })}) + }" + }, + Mode::Normal, + ); + cx.simulate_keystrokes(["c", "i", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|ˇ| { bar(|j, k| { })}) + }" + }, + Mode::Insert, + ); + cx.simulate_keystrokes(["escape", "1", "8", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|| { bar(ˇ|j, k| { })}) + }" + }, + Mode::Normal, + ); + + cx.simulate_keystrokes(["v", "a", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|| { bar(«|j, k| ˇ»{ })}) + }" + }, + Mode::Visual, + ); + } + #[gpui::test] async fn test_delete_surrounding_character_objects(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; diff --git a/crates/vim/test_data/test_multiline_surrounding_character_objects.json b/crates/vim/test_data/test_multiline_surrounding_character_objects.json index 9758367c39..973df647a2 100644 --- a/crates/vim/test_data/test_multiline_surrounding_character_objects.json +++ b/crates/vim/test_data/test_multiline_surrounding_character_objects.json @@ -8,7 +8,7 @@ {"Key":"i"} {"Key":"{"} {"Get":{"state":"func empty(a string) bool {\n if a == \"\" {\n« return true\nˇ» }\n return false\n}","mode":"Visual"}} -{"Put":{"state":"func empty(a string) bool {\n if a == \"\" {\n ˇreturn true\n }\n return false\n}"}} +{"Put":{"state":"func empty(a string) bool {\n if a == \"\" ˇ{\n return true\n }\n return false\n}"}} {"Key":"v"} {"Key":"i"} {"Key":"{"}