Skip to repository content219 lines · 7.7 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T01:33:32.833Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
highlight_matching_bracket.rs
1use crate::{Editor, HighlightKey, RangeToAnchorExt, display_map::DisplaySnapshot};
2use gpui::{AppContext, Context, HighlightStyle};
3use language::CursorShape;
4use multi_buffer::MultiBufferOffset;
5use theme::ActiveTheme;
6
7impl Editor {
8 #[ztracing::instrument(skip_all)]
9 pub fn refresh_matching_bracket_highlights(
10 &mut self,
11 snapshot: &DisplaySnapshot,
12 cx: &mut Context<Editor>,
13 ) {
14 let newest_selection = self.selections.newest::<MultiBufferOffset>(&snapshot);
15 // Don't highlight brackets if the selection isn't empty
16 if !newest_selection.is_empty() {
17 self.clear_highlights(HighlightKey::MatchingBracket, cx);
18 return;
19 }
20
21 let buffer_snapshot = snapshot.buffer_snapshot();
22 let head = newest_selection.head();
23 if head > buffer_snapshot.len() {
24 log::error!("bug: cursor offset is out of range while refreshing bracket highlights");
25 return;
26 }
27
28 let mut tail = head;
29 if (self.cursor_shape == CursorShape::Block || self.cursor_shape == CursorShape::Hollow)
30 && head < buffer_snapshot.len()
31 {
32 if let Some(tail_ch) = buffer_snapshot.chars_at(tail).next() {
33 tail += tail_ch.len_utf8();
34 }
35 }
36 let task = cx.background_spawn({
37 let buffer_snapshot = buffer_snapshot.clone();
38 async move { buffer_snapshot.innermost_enclosing_bracket_ranges(head..tail, None) }
39 });
40 self.refresh_matching_bracket_highlights_task = cx.spawn({
41 let buffer_snapshot = buffer_snapshot.clone();
42 async move |this, cx| {
43 let bracket_ranges = task.await;
44 let current_ranges = this
45 .read_with(cx, |editor, cx| {
46 editor
47 .display_map
48 .read(cx)
49 .text_highlights(HighlightKey::MatchingBracket)
50 .map(|(_, ranges)| ranges.to_vec())
51 })
52 .ok()
53 .flatten();
54 let new_ranges = bracket_ranges.map(|(opening_range, closing_range)| {
55 vec![
56 opening_range.to_anchors(&buffer_snapshot),
57 closing_range.to_anchors(&buffer_snapshot),
58 ]
59 });
60
61 if current_ranges != new_ranges {
62 this.update(cx, |editor, cx| {
63 editor.clear_highlights(HighlightKey::MatchingBracket, cx);
64 if let Some(new_ranges) = new_ranges {
65 editor.highlight_text(
66 HighlightKey::MatchingBracket,
67 new_ranges,
68 HighlightStyle {
69 background_color: Some(
70 cx.theme()
71 .colors()
72 .editor_document_highlight_bracket_background,
73 ),
74 ..Default::default()
75 },
76 cx,
77 )
78 }
79 })
80 .ok();
81 }
82 }
83 });
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
91 use indoc::indoc;
92 use language::{BracketPair, BracketPairConfig, Language, LanguageConfig, LanguageMatcher};
93
94 #[gpui::test]
95 async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
96 init_test(cx, |_| {});
97
98 let mut cx = EditorLspTestContext::new(
99 Language::new(
100 LanguageConfig {
101 name: "Rust".into(),
102 matcher: (LanguageMatcher {
103 path_suffixes: vec!["rs".to_string()],
104 ..Default::default()
105 })
106 .into(),
107 brackets: BracketPairConfig {
108 pairs: vec![
109 BracketPair {
110 start: "{".to_string(),
111 end: "}".to_string(),
112 close: false,
113 surround: false,
114 newline: true,
115 },
116 BracketPair {
117 start: "(".to_string(),
118 end: ")".to_string(),
119 close: false,
120 surround: false,
121 newline: true,
122 },
123 ],
124 ..Default::default()
125 },
126 ..Default::default()
127 },
128 Some(tree_sitter_rust::LANGUAGE.into()),
129 )
130 .with_brackets_query(indoc! {r#"
131 ("{" @open "}" @close)
132 ("(" @open ")" @close)
133 "#})
134 .unwrap(),
135 Default::default(),
136 cx,
137 )
138 .await;
139
140 // positioning cursor inside bracket highlights both
141 cx.set_state(indoc! {r#"
142 pub fn test("Test ˇargument") {
143 another_test(1, 2, 3);
144 }
145 "#});
146 cx.run_until_parked();
147 cx.assert_editor_text_highlights(
148 HighlightKey::MatchingBracket,
149 indoc! {r#"
150 pub fn test«(»"Test argument"«)» {
151 another_test(1, 2, 3);
152 }
153 "#},
154 );
155
156 cx.set_state(indoc! {r#"
157 pub fn test("Test argument") {
158 another_test(1, ˇ2, 3);
159 }
160 "#});
161 cx.run_until_parked();
162 cx.assert_editor_text_highlights(
163 HighlightKey::MatchingBracket,
164 indoc! {r#"
165 pub fn test("Test argument") {
166 another_test«(»1, 2, 3«)»;
167 }
168 "#},
169 );
170
171 cx.set_state(indoc! {r#"
172 pub fn test("Test argument") {
173 anotherˇ_test(1, 2, 3);
174 }
175 "#});
176 cx.run_until_parked();
177 cx.assert_editor_text_highlights(
178 HighlightKey::MatchingBracket,
179 indoc! {r#"
180 pub fn test("Test argument") «{»
181 another_test(1, 2, 3);
182 «}»
183 "#},
184 );
185
186 // positioning outside of brackets removes highlight
187 cx.set_state(indoc! {r#"
188 pub fˇn test("Test argument") {
189 another_test(1, 2, 3);
190 }
191 "#});
192 cx.run_until_parked();
193 cx.assert_editor_text_highlights(
194 HighlightKey::MatchingBracket,
195 indoc! {r#"
196 pub fn test("Test argument") {
197 another_test(1, 2, 3);
198 }
199 "#},
200 );
201
202 // non empty selection dismisses highlight
203 cx.set_state(indoc! {r#"
204 pub fn test("Te«st argˇ»ument") {
205 another_test(1, 2, 3);
206 }
207 "#});
208 cx.run_until_parked();
209 cx.assert_editor_text_highlights(
210 HighlightKey::MatchingBracket,
211 indoc! {r#"
212 pub fn test«("Test argument") {
213 another_test(1, 2, 3);
214 }
215 "#},
216 );
217 }
218}
219