vim: Fix % motion edge case (#39620)

Update Vim's `%` motion to first attempt finding the exact matching
bracket/tag under the cursor, then fall back to the previous
nearest-enclosing logic if none is found. This prevents accidentally
jumping to nested pairs in languages like TSX and Svelte where `<>`,
`</>`, and `/>` are also treated as brackets.

Closes #39368 

Release Notes:

- Fixed an edge case with the `%` motion in vim, where the cursor could
end up in a closing HTML tag instead of the matching bracket
This commit is contained in:
Dino
2025-10-08 13:49:55 +01:00
committed by GitHub
parent a9455eb947
commit 057b7b1543
5 changed files with 148 additions and 6 deletions
@@ -262,6 +262,77 @@ impl EditorLspTestContext {
Self::new(language, capabilities, cx).await
}
pub async fn new_tsx(
capabilities: lsp::ServerCapabilities,
cx: &mut gpui::TestAppContext,
) -> EditorLspTestContext {
let mut word_characters: HashSet<char> = Default::default();
word_characters.insert('$');
word_characters.insert('#');
let language = Language::new(
LanguageConfig {
name: "TSX".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["tsx".to_string()],
..Default::default()
},
brackets: language::BracketPairConfig {
pairs: vec![language::BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
}],
disabled_scopes_by_bracket_ix: Default::default(),
},
word_characters,
..Default::default()
},
Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
)
.with_queries(LanguageQueries {
brackets: Some(Cow::from(indoc! {r#"
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("<" @open ">" @close)
("<" @open "/>" @close)
("</" @open ">" @close)
("\"" @open "\"" @close)
("'" @open "'" @close)
("`" @open "`" @close)
((jsx_element (jsx_opening_element) @open (jsx_closing_element) @close) (#set! newline.only))"#})),
indents: Some(Cow::from(indoc! {r#"
[
(call_expression)
(assignment_expression)
(member_expression)
(lexical_declaration)
(variable_declaration)
(assignment_expression)
(if_statement)
(for_statement)
] @indent
(_ "[" "]" @end) @indent
(_ "<" ">" @end) @indent
(_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent
(jsx_opening_element ">" @end) @indent
(jsx_element
(jsx_opening_element) @start
(jsx_closing_element)? @end) @indent
"#})),
..Default::default()
})
.expect("Could not parse queries");
Self::new(language, capabilities, cx).await
}
pub async fn new_html(cx: &mut gpui::TestAppContext) -> Self {
let language = Language::new(
LanguageConfig {