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