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        .enclosing_bracket_ranges(head..head)
 21    {
 22        editor.highlight_background::<MatchingBracketHighlight>(
 23            vec![
 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 indoc::indoc;
 36
 37    use language::{BracketPair, Language, LanguageConfig};
 38
 39    use crate::test::EditorLspTestContext;
 40
 41    use super::*;
 42
 43    #[gpui::test]
 44    async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
 45        let mut cx = EditorLspTestContext::new(
 46            Language::new(
 47                LanguageConfig {
 48                    name: "Rust".into(),
 49                    path_suffixes: vec!["rs".to_string()],
 50                    brackets: vec![
 51                        BracketPair {
 52                            start: "{".to_string(),
 53                            end: "}".to_string(),
 54                            close: false,
 55                            newline: true,
 56                        },
 57                        BracketPair {
 58                            start: "(".to_string(),
 59                            end: ")".to_string(),
 60                            close: false,
 61                            newline: true,
 62                        },
 63                    ],
 64                    ..Default::default()
 65                },
 66                Some(tree_sitter_rust::language()),
 67            )
 68            .with_brackets_query(indoc! {r#"
 69                ("{" @open "}" @close)
 70                ("(" @open ")" @close)
 71                "#})
 72            .unwrap(),
 73            Default::default(),
 74            cx,
 75        )
 76        .await;
 77
 78        // positioning cursor inside bracket highlights both
 79        cx.set_state_by(
 80            vec!['|'.into()],
 81            indoc! {r#"
 82                pub fn test("Test |argument") {
 83                    another_test(1, 2, 3);
 84                }"#},
 85        );
 86        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
 87                pub fn test[(]"Test argument"[)] {
 88                    another_test(1, 2, 3);
 89                }"#});
 90
 91        cx.set_state_by(
 92            vec!['|'.into()],
 93            indoc! {r#"
 94                pub fn test("Test argument") {
 95                    another_test(1, |2, 3);
 96                }"#},
 97        );
 98        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
 99            pub fn test("Test argument") {
100                another_test[(]1, 2, 3[)];
101            }"#});
102
103        cx.set_state_by(
104            vec!['|'.into()],
105            indoc! {r#"
106                pub fn test("Test argument") {
107                    another|_test(1, 2, 3);
108                }"#},
109        );
110        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
111            pub fn test("Test argument") [{]
112                another_test(1, 2, 3);
113            [}]"#});
114
115        // positioning outside of brackets removes highlight
116        cx.set_state_by(
117            vec!['|'.into()],
118            indoc! {r#"
119                pub f|n test("Test argument") {
120                    another_test(1, 2, 3);
121                }"#},
122        );
123        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
124            pub fn test("Test argument") {
125                another_test(1, 2, 3);
126            }"#});
127
128        // non empty selection dismisses highlight
129        // positioning outside of brackets removes highlight
130        cx.set_state_by(
131            vec![('<', '>').into()],
132            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}