highlight_matching_bracket.rs

  1use gpui::ViewContext;
  2
  3use crate::{Editor, RangeToAnchorExt};
  4
  5enum MatchingBracketHighlight {}
  6
  7pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
  8    editor.clear_background_highlights::<MatchingBracketHighlight>(cx);
  9
 10    let newest_selection = editor.selections.newest::<usize>(cx);
 11    // Don't highlight brackets if the selection isn't empty
 12    if !newest_selection.is_empty() {
 13        return;
 14    }
 15
 16    let head = newest_selection.head();
 17    let snapshot = editor.snapshot(cx);
 18    if let Some((opening_range, closing_range)) = snapshot
 19        .buffer_snapshot
 20        .innermost_enclosing_bracket_ranges(head..head, None)
 21    {
 22        editor.highlight_background::<MatchingBracketHighlight>(
 23            &[
 24                opening_range.to_anchors(&snapshot.buffer_snapshot),
 25                closing_range.to_anchors(&snapshot.buffer_snapshot),
 26            ],
 27            |theme| theme.editor_document_highlight_read_background,
 28            cx,
 29        )
 30    }
 31}
 32
 33#[cfg(test)]
 34mod tests {
 35    use super::*;
 36    use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
 37    use indoc::indoc;
 38    use language::{BracketPair, BracketPairConfig, Language, LanguageConfig, LanguageMatcher};
 39
 40    #[gpui::test]
 41    async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
 42        init_test(cx, |_| {});
 43
 44        let mut cx = EditorLspTestContext::new(
 45            Language::new(
 46                LanguageConfig {
 47                    name: "Rust".into(),
 48                    matcher: LanguageMatcher {
 49                        path_suffixes: vec!["rs".to_string()],
 50                        ..Default::default()
 51                    },
 52                    brackets: BracketPairConfig {
 53                        pairs: vec![
 54                            BracketPair {
 55                                start: "{".to_string(),
 56                                end: "}".to_string(),
 57                                close: false,
 58                                surround: false,
 59                                newline: true,
 60                            },
 61                            BracketPair {
 62                                start: "(".to_string(),
 63                                end: ")".to_string(),
 64                                close: false,
 65                                surround: false,
 66                                newline: true,
 67                            },
 68                        ],
 69                        ..Default::default()
 70                    },
 71                    ..Default::default()
 72                },
 73                Some(tree_sitter_rust::language()),
 74            )
 75            .with_brackets_query(indoc! {r#"
 76                ("{" @open "}" @close)
 77                ("(" @open ")" @close)
 78                "#})
 79            .unwrap(),
 80            Default::default(),
 81            cx,
 82        )
 83        .await;
 84
 85        // positioning cursor inside bracket highlights both
 86        cx.set_state(indoc! {r#"
 87            pub fn test("Test ˇargument") {
 88                another_test(1, 2, 3);
 89            }
 90        "#});
 91        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
 92            pub fn test«(»"Test argument"«)» {
 93                another_test(1, 2, 3);
 94            }
 95        "#});
 96
 97        cx.set_state(indoc! {r#"
 98            pub fn test("Test argument") {
 99                another_test(1, ˇ2, 3);
100            }
101        "#});
102        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
103            pub fn test("Test argument") {
104                another_test«(»1, 2, 3«)»;
105            }
106        "#});
107
108        cx.set_state(indoc! {r#"
109            pub fn test("Test argument") {
110                anotherˇ_test(1, 2, 3);
111            }
112        "#});
113        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
114            pub fn test("Test argument") «{»
115                another_test(1, 2, 3);
116            «}»
117        "#});
118
119        // positioning outside of brackets removes highlight
120        cx.set_state(indoc! {r#"
121            pub fˇn test("Test argument") {
122                another_test(1, 2, 3);
123            }
124        "#});
125        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
126            pub fn test("Test argument") {
127                another_test(1, 2, 3);
128            }
129        "#});
130
131        // non empty selection dismisses highlight
132        cx.set_state(indoc! {r#"
133            pub fn test("Te«st argˇ»ument") {
134                another_test(1, 2, 3);
135            }
136        "#});
137        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
138            pub fn test("Test argument") {
139                another_test(1, 2, 3);
140            }
141        "#});
142    }
143}