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