mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 05:51:14 +00:00
vim: Add C preprocessor check in matching function (#55515)
Closes #24820 This PR fixes the bug specified in issue https://github.com/zed-industries/zed/issues/24820, now the matching function checks if the cursor is above a comment or a directive before defaulting to a bracket range as neovim does. It also fixes fixes the `line_end` calculations so that when `%` is pressed inside a bracket range https://github.com/user-attachments/assets/f59daa6f-9769-45e8-bb8c-2d533470b59d Release Notes: - `fn matching()` checks for `preprocessor directives` or `comments` before defaulting to any bracket range. - In `fn matching()`line_end calculations avoid expanding a blank current line into start..EOF.
This commit is contained in:
parent
5dd9082d05
commit
0bcf71f786
3 changed files with 96 additions and 58 deletions
|
|
@ -2452,7 +2452,7 @@ fn find_matching_bracket_text_based(
|
|||
.find_map(|(ch, char_offset)| get_bracket_pair(ch).map(|info| (info, char_offset)));
|
||||
|
||||
if bracket_info.is_none() {
|
||||
return find_matching_c_preprocessor_directive(map, line_range);
|
||||
return find_matching_c_preprocessor_directive(map, line_range, offset);
|
||||
}
|
||||
|
||||
let (open, close, is_opening) = bracket_info?.0;
|
||||
|
|
@ -2489,18 +2489,20 @@ fn find_matching_bracket_text_based(
|
|||
fn find_matching_c_preprocessor_directive(
|
||||
map: &DisplaySnapshot,
|
||||
line_range: Range<MultiBufferOffset>,
|
||||
offset: MultiBufferOffset,
|
||||
) -> Option<MultiBufferOffset> {
|
||||
let line_start = map
|
||||
.buffer_chars_at(line_range.start)
|
||||
.skip_while(|(c, _)| *c == ' ' || *c == '\t')
|
||||
.take_while(|(c, char_offset)| *char_offset < line_range.end && !c.is_whitespace())
|
||||
.map(|(c, _)| c)
|
||||
.take(6)
|
||||
.collect::<String>();
|
||||
|
||||
if line_start.starts_with("#if")
|
||||
|| line_start.starts_with("#else")
|
||||
|| line_start.starts_with("#elif")
|
||||
{
|
||||
if line_range.start + line_start.len() < offset {
|
||||
return None;
|
||||
}
|
||||
|
||||
if line_start.starts_with("#if") || line_start.starts_with("#el") {
|
||||
let mut depth = 0i32;
|
||||
for (ch, char_offset) in map.buffer_chars_at(line_range.end) {
|
||||
if ch != '\n' {
|
||||
|
|
@ -2618,8 +2620,30 @@ fn matching(
|
|||
|
||||
// Ensure the range is contained by the current line.
|
||||
let mut line_end = map.next_line_boundary(point).0;
|
||||
if line_end == point {
|
||||
line_end = map.max_point().to_point(map);
|
||||
let max_point = map.max_point().to_point(map);
|
||||
|
||||
// Only widen to EOF when the cursor is actually at EOF.
|
||||
// This avoids expanding a blank current line into start..EOF.
|
||||
if line_end == point && point == max_point {
|
||||
line_end = max_point;
|
||||
}
|
||||
|
||||
let line_range = map.prev_line_boundary(point).0..line_end;
|
||||
let line_range = line_range.start.to_offset(&map.buffer_snapshot())
|
||||
..line_range.end.to_offset(&map.buffer_snapshot());
|
||||
|
||||
if let Some(preproc_range) = find_matching_c_preprocessor_directive(map, line_range, offset) {
|
||||
return preproc_range.to_display_point(map);
|
||||
}
|
||||
|
||||
if let Some((open_range, close_range)) = comment_delimiter_pair(map, offset) {
|
||||
if open_range.contains(&offset) {
|
||||
return close_range.start.to_display_point(map);
|
||||
}
|
||||
|
||||
if close_range.contains(&offset) {
|
||||
return open_range.start.to_display_point(map);
|
||||
}
|
||||
}
|
||||
|
||||
let is_quote_char = |ch: char| matches!(ch, '\'' | '"' | '`');
|
||||
|
|
@ -2729,32 +2753,6 @@ fn matching(
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some((open_range, close_range)) = comment_delimiter_pair(map, offset) {
|
||||
if open_range.contains(&offset) {
|
||||
return close_range.start.to_display_point(map);
|
||||
}
|
||||
|
||||
if close_range.contains(&offset) {
|
||||
return open_range.start.to_display_point(map);
|
||||
}
|
||||
|
||||
let open_candidate = (open_range.start >= offset
|
||||
&& line_range.contains(&open_range.start))
|
||||
.then_some((open_range.start.saturating_sub(offset), close_range.start));
|
||||
|
||||
let close_candidate = (close_range.start >= offset
|
||||
&& line_range.contains(&close_range.start))
|
||||
.then_some((close_range.start.saturating_sub(offset), open_range.start));
|
||||
|
||||
if let Some((_, destination)) = [open_candidate, close_candidate]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.min_by_key(|(distance, _)| *distance)
|
||||
{
|
||||
return destination.to_display_point(map);
|
||||
}
|
||||
}
|
||||
|
||||
closest_pair_destination
|
||||
.map(|destination| destination.to_display_point(map))
|
||||
.unwrap_or_else(|| {
|
||||
|
|
@ -3663,6 +3661,10 @@ mod test {
|
|||
cx.shared_state().await.assert_eq(indoc! {r"/*
|
||||
this is a comment
|
||||
ˇ*/"});
|
||||
cx.simulate_shared_keystrokes("k %").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"/*
|
||||
ˇ this is a comment
|
||||
*/"});
|
||||
|
||||
cx.set_shared_state("ˇ// comment").await;
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
|
|
@ -3673,48 +3675,53 @@ mod test {
|
|||
async fn test_matching_preprocessor_directives(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {r"#ˇif
|
||||
cx.set_shared_state(indoc! {r"
|
||||
#ˇif
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
#endif
|
||||
"})
|
||||
#endif
|
||||
"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"#if
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
#if
|
||||
|
||||
ˇ#else
|
||||
|
||||
#endif
|
||||
"});
|
||||
"});
|
||||
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"#if
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
#if
|
||||
|
||||
#else
|
||||
|
||||
ˇ#endif
|
||||
"});
|
||||
"});
|
||||
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"ˇ#if
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
ˇ#if
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
"});
|
||||
"});
|
||||
|
||||
cx.set_shared_state(indoc! {r"
|
||||
#ˇif
|
||||
#if
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
#ˇif
|
||||
#if
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
"})
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
"})
|
||||
.await;
|
||||
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
|
|
@ -3727,8 +3734,9 @@ mod test {
|
|||
#endif
|
||||
|
||||
ˇ#else
|
||||
|
||||
#endif
|
||||
"});
|
||||
"});
|
||||
|
||||
cx.simulate_shared_keystrokes("% %").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
|
|
@ -3740,8 +3748,9 @@ mod test {
|
|||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
"});
|
||||
"});
|
||||
cx.simulate_shared_keystrokes("j % % %").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
#if
|
||||
|
|
@ -3752,8 +3761,28 @@ mod test {
|
|||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
"});
|
||||
"});
|
||||
|
||||
cx.set_shared_state(indoc! {r"
|
||||
#if definedˇ(something)
|
||||
|
||||
#endif
|
||||
"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("%").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
#if defined(somethingˇ)
|
||||
|
||||
#endif
|
||||
"});
|
||||
cx.simulate_shared_keystrokes("0 %").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {r"
|
||||
#if defined(something)
|
||||
|
||||
ˇ#endif
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
{"Get":{"state":"ˇ/*\n this is a comment\n*/","mode":"Normal"}}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"/*\n this is a comment\nˇ*/","mode":"Normal"}}
|
||||
{"Key":"k"}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"/*\nˇ this is a comment\n*/","mode":"Normal"}}
|
||||
{"Put":{"state":"ˇ// comment"}}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"ˇ// comment","mode":"Normal"}}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,20 @@
|
|||
{"Get":{"state":"#if\n\n#else\n\nˇ#endif\n","mode":"Normal"}}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"ˇ#if\n\n#else\n\n#endif\n","mode":"Normal"}}
|
||||
{"Put":{"state":"#ˇif\n #if\n\n #else\n\n #endif\n\n#else\n#endif\n"}}
|
||||
{"Put":{"state":"#ˇif\n #if\n\n #else\n\n #endif\n\n#else\n\n#endif\n"}}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"#if\n #if\n\n #else\n\n #endif\n\nˇ#else\n#endif\n","mode":"Normal"}}
|
||||
{"Get":{"state":"#if\n #if\n\n #else\n\n #endif\n\nˇ#else\n\n#endif\n","mode":"Normal"}}
|
||||
{"Key":"%"}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"ˇ#if\n #if\n\n #else\n\n #endif\n\n#else\n#endif\n","mode":"Normal"}}
|
||||
{"Get":{"state":"ˇ#if\n #if\n\n #else\n\n #endif\n\n#else\n\n#endif\n","mode":"Normal"}}
|
||||
{"Key":"j"}
|
||||
{"Key":"%"}
|
||||
{"Key":"%"}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"#if\n ˇ#if\n\n #else\n\n #endif\n\n#else\n#endif\n","mode":"Normal"}}
|
||||
{"Get":{"state":"#if\n ˇ#if\n\n #else\n\n #endif\n\n#else\n\n#endif\n","mode":"Normal"}}
|
||||
{"Put":{"state":"#if definedˇ(something)\n\n#endif\n"}}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"#if defined(somethingˇ)\n\n#endif\n","mode":"Normal"}}
|
||||
{"Key":"0"}
|
||||
{"Key":"%"}
|
||||
{"Get":{"state":"#if defined(something)\n\nˇ#endif\n","mode":"Normal"}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue