Add surround aliases (#20104)

Closes #19417

Release Notes:

- vim : Added `r` and `a` as aliases for `[` and `<` text objects
(copying vim-surround).
- vim: (breaking change) rebound the function argument text object to
`g`.
- vim: Fixed surrounds to allow `b`/`B`/`r`/`a` anywhere you could use
`(`, `{`, `[`, `<`.

---


- vim: Added `b`, `B`, `r`, `s`, `a` as aliases for `()`, `{}`, `[]`,
`<>` in vim surround mode.
- Adds a new `surround_alias` function where aliases are defined.
- This function is used in `find_surround_pairs` to substitute the
chosen text with the alias
- The keymap is also modified to add support for Square and Angle
brackets when changing surrounds. These two were added to follow the
example of Tim Pope's ubiquitous `vim-surround` plugin.
- I had to overwrite the `vim::Argument` keybind in order to do this. I
moved it to use the `g` modifier. I realize this is a breaking change
and will happily move the `vim::AngleBracket` keymap to a different
letter if you'd like to avoid this. I'm just trying to keep with
convention. Ideally, Users would be able to define surround aliases
themselves in the config file but that's a much bigger task than I'm
able to do right now.
- I also added tests for the new aliases.

Thanks for making such a clean and organized codebase. I was able to
find the relevant section of code rather quickly thanks to this.
This commit is contained in:
Mike Lloyd 2024-11-04 08:03:27 -08:00 committed by GitHub
parent 4bbddcad31
commit f0aeab7d00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 259 additions and 13 deletions

View file

@ -364,12 +364,14 @@
"b": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"r": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"shift-b": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets",
"a": "vim::Argument"
"a": "vim::AngleBrackets",
"g": "vim::Argument"
}
},
{

View file

@ -1402,7 +1402,7 @@ mod test {
// Generic arguments
cx.set_state("fn boop<A: ˇDebug, B>() {}", Mode::Normal);
cx.simulate_keystrokes("v i a");
cx.simulate_keystrokes("v i g");
cx.assert_state("fn boop<«A: Debugˇ», B>() {}", Mode::Visual);
// Function arguments
@ -1410,11 +1410,11 @@ mod test {
"fn boop(ˇarg_a: (Tuple, Of, Types), arg_b: String) {}",
Mode::Normal,
);
cx.simulate_keystrokes("d a a");
cx.simulate_keystrokes("d a g");
cx.assert_state("fn boop(ˇarg_b: String) {}", Mode::Normal);
cx.set_state("std::namespace::test(\"strinˇg\", a.b.c())", Mode::Normal);
cx.simulate_keystrokes("v a a");
cx.simulate_keystrokes("v a g");
cx.assert_state("std::namespace::test(«\"string\", ˇ»a.b.c())", Mode::Visual);
// Tuple, vec, and array arguments
@ -1422,34 +1422,34 @@ mod test {
"fn boop(arg_a: (Tuple, Ofˇ, Types), arg_b: String) {}",
Mode::Normal,
);
cx.simulate_keystrokes("c i a");
cx.simulate_keystrokes("c i g");
cx.assert_state(
"fn boop(arg_a: (Tuple, ˇ, Types), arg_b: String) {}",
Mode::Insert,
);
cx.set_state("let a = (test::call(), 'p', my_macro!{ˇ});", Mode::Normal);
cx.simulate_keystrokes("c a a");
cx.simulate_keystrokes("c a g");
cx.assert_state("let a = (test::call(), 'p'ˇ);", Mode::Insert);
cx.set_state("let a = [test::call(ˇ), 300];", Mode::Normal);
cx.simulate_keystrokes("c i a");
cx.simulate_keystrokes("c i g");
cx.assert_state("let a = [ˇ, 300];", Mode::Insert);
cx.set_state(
"let a = vec![Vec::new(), vecˇ![test::call(), 300]];",
Mode::Normal,
);
cx.simulate_keystrokes("c a a");
cx.simulate_keystrokes("c a g");
cx.assert_state("let a = vec![Vec::new()ˇ];", Mode::Insert);
// Cursor immediately before / after brackets
cx.set_state("let a = [test::call(first_arg)ˇ]", Mode::Normal);
cx.simulate_keystrokes("v i a");
cx.simulate_keystrokes("v i g");
cx.assert_state("let a = [«test::call(first_arg)ˇ»]", Mode::Visual);
cx.set_state("let a = [test::callˇ(first_arg)]", Mode::Normal);
cx.simulate_keystrokes("v i a");
cx.simulate_keystrokes("v i g");
cx.assert_state("let a = [«test::call(first_arg)ˇ»]", Mode::Visual);
}

View file

@ -52,7 +52,7 @@ impl Vim {
newline: false,
},
};
let surround = pair.end != *text;
let surround = pair.end != surround_alias((*text).as_ref());
let (display_map, display_selections) = editor.selections.all_adjusted_display(cx);
let mut edits = Vec::new();
let mut anchors = Vec::new();
@ -245,7 +245,7 @@ impl Vim {
newline: false,
},
};
let surround = pair.end != *text;
let surround = pair.end != surround_alias((*text).as_ref());
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let mut edits = Vec::new();
let mut anchors = Vec::new();
@ -393,7 +393,19 @@ impl Vim {
}
fn find_surround_pair<'a>(pairs: &'a [BracketPair], ch: &str) -> Option<&'a BracketPair> {
pairs.iter().find(|pair| pair.start == ch || pair.end == ch)
pairs
.iter()
.find(|pair| pair.start == surround_alias(ch) || pair.end == surround_alias(ch))
}
fn surround_alias(ch: &str) -> &str {
match ch {
"b" => ")",
"B" => "}",
"a" => ">",
"r" => "]",
_ => ch,
}
}
fn all_support_surround_pair() -> Vec<BracketPair> {
@ -1171,4 +1183,236 @@ mod test {
Mode::Normal,
);
}
#[gpui::test]
async fn test_surround_aliases(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
// add aliases
cx.set_state(
indoc! {"
The quˇick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("y s i w b");
cx.assert_state(
indoc! {"
The ˇ(quick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The quˇick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("y s i w B");
cx.assert_state(
indoc! {"
The ˇ{quick} brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The quˇick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("y s i w a");
cx.assert_state(
indoc! {"
The ˇ<quick> brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The quˇick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("y s i w r");
cx.assert_state(
indoc! {"
The ˇ[quick] brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
// change aliases
cx.set_state(
indoc! {"
The {quˇick} brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s { b");
cx.assert_state(
indoc! {"
The ˇ(quick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The (quˇick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s ( B");
cx.assert_state(
indoc! {"
The ˇ{quick} brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The (quˇick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s ( a");
cx.assert_state(
indoc! {"
The ˇ<quick> brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The <quˇick> brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s < b");
cx.assert_state(
indoc! {"
The ˇ(quick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The (quˇick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s ( r");
cx.assert_state(
indoc! {"
The ˇ[quick] brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The [quˇick] brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("c s [ b");
cx.assert_state(
indoc! {"
The ˇ(quick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
// delete alias
cx.set_state(
indoc! {"
The {quˇick} brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("d s B");
cx.assert_state(
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The (quˇick) brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("d s b");
cx.assert_state(
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The [quˇick] brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("d s r");
cx.assert_state(
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.set_state(
indoc! {"
The <quˇick> brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes("d s a");
cx.assert_state(
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog."},
Mode::Normal,
);
}
}