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
@@ -207,6 +207,26 @@ impl NeovimBackedTestContext {
}
}
pub async fn new_tsx(cx: &mut gpui::TestAppContext) -> NeovimBackedTestContext {
#[cfg(feature = "neovim")]
cx.executor().allow_parking();
let thread = thread::current();
let test_name = thread
.name()
.expect("thread is not named")
.split(':')
.next_back()
.unwrap()
.to_string();
Self {
cx: VimTestContext::new_tsx(cx).await,
neovim: NeovimConnection::new(test_name).await,
last_set_state: None,
recent_keystrokes: Default::default(),
}
}
pub async fn set_shared_state(&mut self, marked_text: &str) {
let mode = if marked_text.contains('»') {
Mode::Visual
+22
View File
@@ -66,6 +66,28 @@ impl VimTestContext {
)
}
pub async fn new_tsx(cx: &mut gpui::TestAppContext) -> VimTestContext {
Self::init(cx);
Self::new_with_lsp(
EditorLspTestContext::new_tsx(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
..Default::default()
}),
rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
prepare_provider: Some(true),
work_done_progress_options: Default::default(),
})),
..Default::default()
},
cx,
)
.await,
true,
)
}
pub fn init_keybindings(enabled: bool, cx: &mut App) {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings(cx, |s| s.vim_mode = Some(enabled));