Improve logic for obtaining surrounds range in Vim mode (#10938)

now correctly retrieves range in cases where escape characters are
present. Fixed #10827


Release Notes:

- vim: Fix logic for finding surrounding quotes to ignore escaped
characters (#10827)
This commit is contained in:
Hans 2024-04-25 11:19:15 +08:00 committed by GitHub
parent d1425603f6
commit 1a27016123
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 18 deletions

View File

@ -23,6 +23,7 @@ collections.workspace = true
command_palette_hooks.workspace = true
editor.workspace = true
gpui.workspace = true
itertools.workspace = true
language.workspace = true
log.workspace = true
nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = [

View File

@ -9,6 +9,9 @@ use editor::{
movement::{self, FindRange},
Bias, DisplayPoint,
};
use itertools::Itertools;
use gpui::{actions, impl_actions, ViewContext, WindowContext};
use language::{char_kind, BufferSnapshot, CharKind, Point, Selection};
use serde::Deserialize;
@ -801,15 +804,20 @@ fn surrounding_markers(
let mut matched_closes = 0;
let mut opening = None;
let mut before_ch = match movement::chars_before(map, point).next() {
Some((ch, _)) => ch,
_ => '\0',
};
if let Some((ch, range)) = movement::chars_after(map, point).next() {
if ch == open_marker {
if ch == open_marker && before_ch != '\\' {
if open_marker == close_marker {
let mut total = 0;
for (ch, _) in movement::chars_before(map, point) {
for ((ch, _), (before_ch, _)) in movement::chars_before(map, point).tuple_windows()
{
if ch == '\n' {
break;
}
if ch == open_marker {
if ch == open_marker && before_ch != '\\' {
total += 1;
}
}
@ -823,11 +831,15 @@ fn surrounding_markers(
}
if opening.is_none() {
for (ch, range) in movement::chars_before(map, point) {
for ((ch, range), (before_ch, _)) in movement::chars_before(map, point).tuple_windows() {
if ch == '\n' && !search_across_lines {
break;
}
if before_ch == '\\' {
continue;
}
if ch == open_marker {
if matched_closes == 0 {
opening = Some(range);
@ -839,15 +851,18 @@ fn surrounding_markers(
}
}
}
if opening.is_none() {
for (ch, range) in movement::chars_after(map, point) {
if ch == open_marker {
opening = Some(range);
break;
} else if ch == close_marker {
break;
if before_ch != '\\' {
if ch == open_marker {
opening = Some(range);
break;
} else if ch == close_marker {
break;
}
}
before_ch = ch;
}
}
@ -857,21 +872,28 @@ fn surrounding_markers(
let mut matched_opens = 0;
let mut closing = None;
before_ch = match movement::chars_before(map, opening.end).next() {
Some((ch, _)) => ch,
_ => '\0',
};
for (ch, range) in movement::chars_after(map, opening.end) {
if ch == '\n' && !search_across_lines {
break;
}
if ch == close_marker {
if matched_opens == 0 {
closing = Some(range);
break;
if before_ch != '\\' {
if ch == close_marker {
if matched_opens == 0 {
closing = Some(range);
break;
}
matched_opens -= 1;
} else if ch == open_marker {
matched_opens += 1;
}
matched_opens -= 1;
} else if ch == open_marker {
matched_opens += 1;
}
before_ch = ch;
}
let Some(mut closing) = closing else {
@ -1467,6 +1489,32 @@ mod test {
.await;
}
#[gpui::test]
async fn test_singleline_surrounding_character_objects_with_escape(
cx: &mut gpui::TestAppContext,
) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state(indoc! {
"h\"e\\\"lˇlo \\\"world\"!"
})
.await;
cx.simulate_shared_keystrokes(["v", "i", "\""]).await;
cx.assert_shared_state(indoc! {
"h\"«e\\\"llo \\\"worldˇ»\"!"
})
.await;
cx.set_shared_state(indoc! {
"hello \"teˇst \\\"inside\\\" world\""
})
.await;
cx.simulate_shared_keystrokes(["v", "i", "\""]).await;
cx.assert_shared_state(indoc! {
"hello \"«test \\\"inside\\\" worldˇ»\""
})
.await;
}
#[gpui::test]
async fn test_vertical_bars(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;

View File

@ -0,0 +1,10 @@
{"Put":{"state":"h\"e\\\"lˇlo \\\"world\"!"}}
{"Key":"v"}
{"Key":"i"}
{"Key":"\""}
{"Get":{"state":"h\"«e\\\"llo \\\"worldˇ»\"!","mode":"Visual"}}
{"Put":{"state":"hello \"teˇst \\\"inside\\\" world\""}}
{"Key":"v"}
{"Key":"i"}
{"Key":"\""}
{"Get":{"state":"hello \"«test \\\"inside\\\" worldˇ»\"","mode":"Visual"}}