Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:33:01.605Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

bracket_colorization.rs

2089 lines · 75.3 KB · rust
1//! Bracket highlights, also known as "rainbow brackets".
2//! Uses tree-sitter queries from brackets.scm to capture bracket pairs,
3//! and theme accents to colorize those.
4
5use std::cmp::Ordering;
6use std::ops::Range;
7use std::sync::Arc;
8
9use crate::{Editor, HighlightKey};
10use collections::{HashMap, HashSet};
11use gpui::{AppContext as _, Context, HighlightStyle, Hsla};
12use language::{BufferRow, BufferSnapshot, language_settings::LanguageSettings};
13use multi_buffer::{Anchor, BufferOffset, ExcerptRange, MultiBufferSnapshot};
14use text::OffsetRangeExt as _;
15use theme::{Appearance, Oklab, Oklch, hsla_to_oklab, hsla_to_oklch, oklch_to_hsla};
16use ui::utils::apca_contrast;
17
18impl Editor {
19    pub(crate) fn colorize_brackets(&mut self, invalidate: bool, cx: &mut Context<Editor>) {
20        if !self.mode.is_full() {
21            return;
22        }
23
24        if invalidate {
25            self.bracket_fetched_tree_sitter_chunks.clear();
26        }
27
28        let Some(accent_data) = self.accent_data.as_ref() else {
29            return;
30        };
31        let accents = accent_data.colors.0.clone();
32        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
33
34        let visible_excerpts = self.visible_buffer_ranges(cx);
35        let excerpt_data: Vec<(
36            BufferSnapshot,
37            Range<BufferOffset>,
38            ExcerptRange<text::Anchor>,
39        )> = visible_excerpts
40            .into_iter()
41            .filter(|(buffer_snapshot, _, _)| {
42                let Some(buffer) = self.buffer().read(cx).buffer(buffer_snapshot.remote_id())
43                else {
44                    return false;
45                };
46                let buffer = buffer.read(cx);
47                buffer
48                    .language()
49                    .is_some_and(|language| language.grammar().is_some())
50                    && LanguageSettings::for_buffer(buffer, cx).colorize_brackets
51            })
52            .collect();
53
54        if !invalidate && (accents.is_empty() || excerpt_data.is_empty()) {
55            return;
56        }
57
58        let mut fetched_tree_sitter_chunks = excerpt_data
59            .iter()
60            .filter_map(|(_, _, excerpt_range)| {
61                let key = excerpt_range.context.clone();
62                Some((
63                    key.clone(),
64                    self.bracket_fetched_tree_sitter_chunks.get(&key).cloned()?,
65                ))
66            })
67            .collect::<HashMap<Range<text::Anchor>, HashSet<Range<BufferRow>>>>();
68
69        let accents_count = accents.len();
70        let bracket_matches_by_accent = cx.background_spawn(async move {
71            if accents_count == 0 {
72                return (HashMap::default(), fetched_tree_sitter_chunks);
73            }
74
75            let bracket_matches_by_accent: HashMap<usize, Vec<Range<Anchor>>> =
76                excerpt_data.into_iter().fold(
77                    HashMap::default(),
78                    |mut acc, (buffer_snapshot, buffer_range, excerpt_range)| {
79                        let fetched_chunks = fetched_tree_sitter_chunks
80                            .entry(excerpt_range.context.clone())
81                            .or_default();
82
83                        let brackets_by_accent = compute_bracket_ranges(
84                            &multi_buffer_snapshot,
85                            &buffer_snapshot,
86                            buffer_range,
87                            excerpt_range,
88                            fetched_chunks,
89                            accents_count,
90                        );
91
92                        for (accent_number, new_ranges) in brackets_by_accent {
93                            let ranges = acc
94                                .entry(accent_number)
95                                .or_insert_with(Vec::<Range<Anchor>>::new);
96
97                            for new_range in new_ranges {
98                                let i = ranges
99                                    .binary_search_by(|probe| {
100                                        probe.start.cmp(&new_range.start, &multi_buffer_snapshot)
101                                    })
102                                    .unwrap_or_else(|i| i);
103                                ranges.insert(i, new_range);
104                            }
105                        }
106
107                        acc
108                    },
109                );
110
111            (bracket_matches_by_accent, fetched_tree_sitter_chunks)
112        });
113
114        self.colorize_brackets_task = cx.spawn(async move |editor, cx| {
115            if invalidate {
116                editor
117                    .update(cx, |editor, cx| {
118                        editor.clear_highlights_with(
119                            &mut |key| matches!(key, HighlightKey::ColorizeBracket(_)),
120                            cx,
121                        );
122                    })
123                    .ok();
124            }
125
126            let (bracket_matches_by_accent, updated_chunks) = bracket_matches_by_accent.await;
127
128            editor
129                .update(cx, |editor, cx| {
130                    editor
131                        .bracket_fetched_tree_sitter_chunks
132                        .extend(updated_chunks);
133                    for (accent_number, bracket_highlights) in bracket_matches_by_accent {
134                        let Some(&bracket_color) = accents.get(accent_number) else {
135                            continue;
136                        };
137                        let style = HighlightStyle {
138                            color: Some(bracket_color),
139                            ..HighlightStyle::default()
140                        };
141
142                        editor.highlight_text_key(
143                            HighlightKey::ColorizeBracket(accent_number),
144                            bracket_highlights,
145                            style,
146                            true,
147                            cx,
148                        );
149                    }
150                })
151                .ok();
152        });
153    }
154}
155
156const BACKGROUND_APCA_LIGHT: f32 = 35.0;
157const BACKGROUND_APCA_DARK: f32 = 30.0;
158const ADJACENT_OKLAB_LIGHT: f32 = 0.10;
159const ADJACENT_OKLAB_DARK: f32 = 0.08;
160const ADJACENT_OKLAB_LIGHT_INTERVENTION: f32 = 0.095;
161const ADJACENT_OKLAB_DARK_INTERVENTION: f32 = 0.08;
162const LIGHTNESS_CLAMP_MIN: f32 = 0.18;
163const LIGHTNESS_CLAMP_MAX: f32 = 0.92;
164
165pub(crate) fn bracket_colorization_accents(
166    accents: &[Hsla],
167    appearance: Appearance,
168    background: Hsla,
169) -> Arc<[Hsla]> {
170    let (intervention_distance, comfortable_distance, min_background_contrast) = match appearance {
171        Appearance::Light => (
172            ADJACENT_OKLAB_LIGHT_INTERVENTION,
173            ADJACENT_OKLAB_LIGHT,
174            BACKGROUND_APCA_LIGHT,
175        ),
176        Appearance::Dark => (
177            ADJACENT_OKLAB_DARK_INTERVENTION,
178            ADJACENT_OKLAB_DARK,
179            BACKGROUND_APCA_DARK,
180        ),
181    };
182    let background_adjusted = accents
183        .iter()
184        .copied()
185        .map(|accent| adjust_color_for_background(accent, background, min_background_contrast))
186        .collect::<Vec<_>>();
187    let adjusted_min_adj = min_adjacent_oklab_distance(&background_adjusted, background);
188
189    if accents.len() < 3 || adjusted_min_adj >= intervention_distance {
190        return Arc::from(background_adjusted);
191    }
192
193    let reordered = maximize_adjacent_separation(&background_adjusted, background);
194    if min_adjacent_oklab_distance(&reordered, background) >= comfortable_distance {
195        Arc::from(reordered)
196    } else {
197        Arc::from(background_adjusted)
198    }
199}
200
201fn maximize_adjacent_separation(accents: &[Hsla], background: Hsla) -> Vec<Hsla> {
202    let Some((&first, rest)) = accents.split_first() else {
203        return Vec::new();
204    };
205    let mut remaining = rest.to_vec();
206    let mut order = Vec::with_capacity(accents.len());
207    order.push(first);
208    let mut last = first;
209
210    while !remaining.is_empty() {
211        let Some((position, &next)) =
212            remaining
213                .iter()
214                .enumerate()
215                .max_by(|&(_, &left), &(_, &right)| {
216                    compare_candidates(background, last, first, left, right)
217                })
218        else {
219            break;
220        };
221        remaining.swap_remove(position);
222        order.push(next);
223        last = next;
224    }
225
226    order
227}
228
229fn compare_candidates(
230    background: Hsla,
231    last: Hsla,
232    first: Hsla,
233    left: Hsla,
234    right: Hsla,
235) -> Ordering {
236    adjacent_distance(last, left, background)
237        .partial_cmp(&adjacent_distance(last, right, background))
238        .unwrap_or(Ordering::Equal)
239        .then_with(|| {
240            adjacent_distance(first, left, background)
241                .partial_cmp(&adjacent_distance(first, right, background))
242                .unwrap_or(Ordering::Equal)
243        })
244}
245
246fn min_adjacent_oklab_distance(accents: &[Hsla], background: Hsla) -> f32 {
247    if accents.len() < 2 {
248        return f32::MAX;
249    }
250    accents
251        .iter()
252        .copied()
253        .zip(accents.iter().copied().cycle().skip(1))
254        .take(accents.len())
255        .map(|(left, right)| adjacent_distance(left, right, background))
256        .fold(f32::MAX, f32::min)
257}
258
259fn oklab_distance(left: Oklab, right: Oklab) -> f32 {
260    let dl = left.l - right.l;
261    let da = left.a - right.a;
262    let db = left.b - right.b;
263    (dl * dl + da * da + db * db).sqrt()
264}
265
266fn adjacent_distance(left: Hsla, right: Hsla, background: Hsla) -> f32 {
267    oklab_distance(
268        hsla_to_oklab(background.blend(left)),
269        hsla_to_oklab(background.blend(right)),
270    )
271}
272
273fn adjust_color_for_background(
274    color: Hsla,
275    background: Hsla,
276    minimum_background_contrast: f32,
277) -> Hsla {
278    if background_contrast(color, background) >= minimum_background_contrast {
279        return color;
280    }
281
282    let original = hsla_to_oklab(color);
283    let darker_candidate = adjusted_lightness_candidate(
284        color,
285        background,
286        minimum_background_contrast,
287        LIGHTNESS_CLAMP_MIN,
288    );
289    let lighter_candidate = adjusted_lightness_candidate(
290        color,
291        background,
292        minimum_background_contrast,
293        LIGHTNESS_CLAMP_MAX,
294    );
295
296    match (darker_candidate, lighter_candidate) {
297        (Some(darker_candidate), Some(lighter_candidate)) => {
298            let darker_distance = oklab_distance(original, hsla_to_oklab(darker_candidate));
299            let lighter_distance = oklab_distance(original, hsla_to_oklab(lighter_candidate));
300            if darker_distance <= lighter_distance {
301                darker_candidate
302            } else {
303                lighter_candidate
304            }
305        }
306        (Some(darker_candidate), None) => darker_candidate,
307        (None, Some(lighter_candidate)) => lighter_candidate,
308        (None, None) => color,
309    }
310}
311
312fn adjusted_lightness_candidate(
313    color: Hsla,
314    background: Hsla,
315    minimum_background_contrast: f32,
316    target_lightness: f32,
317) -> Option<Hsla> {
318    let original = hsla_to_oklch(color);
319    let lightness_delta = target_lightness - original.l;
320
321    if lightness_delta.abs() <= f32::EPSILON {
322        return None;
323    }
324
325    (1..=128).find_map(|step| {
326        let amount = step as f32 / 128.0;
327        let candidate = oklch_to_hsla(
328            Oklch {
329                l: (original.l + lightness_delta * amount).clamp(0.0, 1.0),
330                chroma: original.chroma,
331                hue: original.hue,
332            },
333            color.a,
334        );
335        (background_contrast(candidate, background) >= minimum_background_contrast)
336            .then_some(candidate)
337    })
338}
339
340fn background_contrast(foreground: Hsla, background: Hsla) -> f32 {
341    apca_contrast(background.blend(foreground), background).abs()
342}
343
344fn compute_bracket_ranges(
345    multi_buffer_snapshot: &MultiBufferSnapshot,
346    buffer_snapshot: &BufferSnapshot,
347    buffer_range: Range<BufferOffset>,
348    excerpt_range: ExcerptRange<text::Anchor>,
349    fetched_chunks: &mut HashSet<Range<BufferRow>>,
350    accents_count: usize,
351) -> Vec<(usize, Vec<Range<Anchor>>)> {
352    let context = excerpt_range.context.to_offset(buffer_snapshot);
353
354    buffer_snapshot
355        .fetch_bracket_ranges(
356            buffer_range.start.0..buffer_range.end.0,
357            Some(fetched_chunks),
358        )
359        .into_iter()
360        .flat_map(|(chunk_range, pairs)| {
361            if fetched_chunks.insert(chunk_range) {
362                pairs
363            } else {
364                Vec::new()
365            }
366        })
367        .filter_map(|pair| {
368            let color_index = pair.color_index?;
369
370            let mut ranges = Vec::new();
371
372            if context.start <= pair.open_range.start && pair.open_range.end <= context.end {
373                let anchors = buffer_snapshot.anchor_range_inside(pair.open_range);
374                ranges.push(
375                    multi_buffer_snapshot.anchor_in_buffer(anchors.start)?
376                        ..multi_buffer_snapshot.anchor_in_buffer(anchors.end)?,
377                );
378            };
379
380            if context.start <= pair.close_range.start && pair.close_range.end <= context.end {
381                let anchors = buffer_snapshot.anchor_range_inside(pair.close_range);
382                ranges.push(
383                    multi_buffer_snapshot.anchor_in_buffer(anchors.start)?
384                        ..multi_buffer_snapshot.anchor_in_buffer(anchors.end)?,
385                );
386            };
387
388            Some((color_index % accents_count, ranges))
389        })
390        .collect()
391}
392
393#[cfg(test)]
394mod tests {
395    use std::{cmp, sync::Arc, time::Duration};
396
397    use super::*;
398    use crate::{
399        DisplayPoint, EditorMode, EditorSnapshot, MoveToBeginning, MoveToEnd, MoveUp,
400        display_map::{DisplayRow, ToDisplayPoint},
401        editor_tests::init_test,
402        test::{
403            editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
404        },
405    };
406    use collections::HashSet;
407    use fs::FakeFs;
408    use gpui::{Rgba, UpdateGlobal as _, hsla};
409    use indoc::indoc;
410    use itertools::Itertools;
411    use language::{Capability, markdown_lang};
412    use languages::rust_lang;
413    use multi_buffer::{MultiBuffer, PathKey};
414    use pretty_assertions::assert_eq;
415    use project::Project;
416    use rope::Point;
417    use serde_json::json;
418    use settings::{AccentContent, SettingsStore};
419    use text::{Bias, OffsetRangeExt, ToOffset};
420    use theme::Appearance;
421    use theme_settings::ThemeStyleContent;
422    use ui::ActiveTheme;
423
424    use util::{path, post_inc};
425
426    fn light_editor_background() -> Hsla {
427        hsla(0.0, 0.0, 0.98, 1.0)
428    }
429
430    fn dark_editor_background() -> Hsla {
431        hsla(0.0, 0.0, 0.12, 1.0)
432    }
433
434    #[test]
435    fn test_auto_bracket_colorization_mode_reorders_weak_palette() {
436        let accents = vec![
437            hsla(0.0, 1.0, 0.68, 1.0),
438            hsla(0.02, 1.0, 0.68, 1.0),
439            hsla(0.34, 1.0, 0.68, 1.0),
440            hsla(0.36, 1.0, 0.68, 1.0),
441        ];
442
443        let original_min_adj = min_adjacent_oklab_distance(&accents, dark_editor_background());
444        let reordered = maximize_adjacent_separation(&accents, dark_editor_background());
445        let reordered_min_adj = min_adjacent_oklab_distance(&reordered, dark_editor_background());
446
447        assert_ne!(reordered.as_slice(), accents.as_slice());
448        assert!(reordered_min_adj > original_min_adj);
449    }
450
451    #[test]
452    fn test_preserves_strong_palette() {
453        let accents = vec![
454            hsla(0.0, 1.0, 0.78, 1.0),
455            hsla(0.16, 1.0, 0.78, 1.0),
456            hsla(0.33, 1.0, 0.78, 1.0),
457            hsla(0.66, 1.0, 0.78, 1.0),
458        ];
459
460        let palette =
461            bracket_colorization_accents(&accents, Appearance::Dark, dark_editor_background());
462
463        assert_eq!(palette.as_ref(), accents.as_slice());
464    }
465
466    #[test]
467    fn test_adjusts_background_failures_preserving_hue_and_chroma() {
468        let accents = vec![
469            hsla(0.58, 1.0, 0.28, 1.0),
470            hsla(0.12, 1.0, 0.28, 1.0),
471            hsla(0.22, 0.9, 0.76, 1.0),
472        ];
473
474        let palette =
475            bracket_colorization_accents(&accents, Appearance::Light, light_editor_background());
476        let original = hsla_to_oklch(accents[2]);
477        let adjusted = hsla_to_oklch(palette[2]);
478
479        assert_ne!(palette.as_ref(), accents.as_slice());
480        assert_eq!(palette.len(), accents.len());
481        assert_eq!(palette[0], accents[0]);
482        assert_eq!(palette[1], accents[1]);
483        assert_ne!(palette[2], accents[2]);
484        assert!((original.chroma - adjusted.chroma).abs() < 0.0001);
485        assert!((original.hue - adjusted.hue).abs() < 0.001);
486        assert_ne!(original.l, adjusted.l);
487        assert!(
488            background_contrast(palette[2], light_editor_background()) >= BACKGROUND_APCA_LIGHT
489        );
490    }
491
492    #[test]
493    fn test_preserves_light_near_miss_palette() {
494        let accents = vec![
495            Hsla::from(Rgba::try_from("#CC241D").expect("valid color")),
496            Hsla::from(Rgba::try_from("#98971A").expect("valid color")),
497            Hsla::from(Rgba::try_from("#D79921").expect("valid color")),
498            Hsla::from(Rgba::try_from("#458588").expect("valid color")),
499            Hsla::from(Rgba::try_from("#B16286").expect("valid color")),
500            Hsla::from(Rgba::try_from("#689D6A").expect("valid color")),
501            Hsla::from(Rgba::try_from("#D65D0E").expect("valid color")),
502        ];
503        let background = Hsla::from(Rgba::try_from("#FBF1C7").expect("valid color"));
504
505        let palette = bracket_colorization_accents(&accents, Appearance::Light, background);
506        let original_min_adj = min_adjacent_oklab_distance(&accents, background);
507
508        assert_eq!(palette.as_ref(), accents.as_slice());
509        assert!(original_min_adj < ADJACENT_OKLAB_LIGHT);
510        assert!(original_min_adj >= ADJACENT_OKLAB_LIGHT_INTERVENTION);
511    }
512
513    #[test]
514    fn test_adjust_color_for_background_prefers_closest_passing_candidate() {
515        // Verify that when both darker and lighter candidates exist,
516        // we pick the one with minimum OKLab distance from the original.
517        let background = hsla(0.0, 0.0, 0.50, 1.0);
518        let min_contrast = 30.0;
519        let color = hsla(0.33, 0.7, 0.46, 1.0);
520
521        assert!(
522            background_contrast(color, background) < min_contrast,
523            "test color must fail contrast check; got {}",
524            background_contrast(color, background)
525        );
526
527        let original = hsla_to_oklab(color);
528        let darker =
529            adjusted_lightness_candidate(color, background, min_contrast, LIGHTNESS_CLAMP_MIN)
530                .expect("fixture must produce a darker passing candidate");
531        let lighter =
532            adjusted_lightness_candidate(color, background, min_contrast, LIGHTNESS_CLAMP_MAX)
533                .expect("fixture must produce a lighter passing candidate");
534
535        let darker_dist = oklab_distance(original, hsla_to_oklab(darker));
536        let lighter_dist = oklab_distance(original, hsla_to_oklab(lighter));
537        let expected = if darker_dist <= lighter_dist {
538            darker
539        } else {
540            lighter
541        };
542        assert_eq!(
543            adjust_color_for_background(color, background, min_contrast),
544            expected
545        );
546    }
547
548    #[test]
549    fn test_background_adjustment_edge_cases() {
550        let color = hsla(0.22, 0.9, 0.76, 1.0);
551        let original_contrast = background_contrast(color, light_editor_background());
552        assert!(original_contrast < 20.0);
553        let palette =
554            bracket_colorization_accents(&[color], Appearance::Light, light_editor_background());
555        assert_ne!(palette.as_ref(), &[color][..]);
556        assert!(
557            background_contrast(palette[0], light_editor_background()) >= BACKGROUND_APCA_LIGHT
558        );
559
560        let impossible_color = hsla(0.58, 1.0, 0.47, 1.0);
561        let impossible_bg = hsla(0.0, 0.0, 0.50, 1.0);
562        assert_eq!(
563            adjust_color_for_background(impossible_color, impossible_bg, 200.0),
564            impossible_color
565        );
566    }
567
568    #[gpui::test]
569    async fn test_basic_bracket_colorization(cx: &mut gpui::TestAppContext) {
570        init_test(cx, |language_settings| {
571            language_settings.defaults.colorize_brackets = Some(true);
572        });
573        let mut cx = EditorLspTestContext::new(
574            Arc::into_inner(rust_lang()).unwrap(),
575            lsp::ServerCapabilities::default(),
576            cx,
577        )
578        .await;
579
580        cx.set_state(indoc! {r#"ˇuse std::{collections::HashMap, future::Future};
581
582fn main() {
583    let a = one((), { () }, ());
584    println!("{a}");
585    println!("{a}");
586    for i in 0..a {
587        println!("{i}");
588    }
589
590    let b = {
591        {
592            {
593                [([([([([([([([([([((), ())])])])])])])])])])]
594            }
595        }
596    };
597}
598
599#[rustfmt::skip]
600fn one(a: (), (): (), c: ()) -> usize { 1 }
601
602fn two<T>(a: HashMap<String, Vec<Option<T>>>) -> usize
603where
604    T: Future<Output = HashMap<String, Vec<Option<Box<()>>>>>,
605{
606    2
607}
608"#});
609        cx.executor().advance_clock(Duration::from_millis(100));
610        cx.executor().run_until_parked();
611
612        assert_eq!(
613            r#"use std::«1{collections::HashMap, future::Future}1»;
614
615fn main«1()1» «1{
616    let a = one«2(«3()3», «3{ «4()4» }3», «3()3»)2»;
617    println!«2("{a}")2»;
618    println!«2("{a}")2»;
619    for i in 0..a «2{
620        println!«3("{i}")3»;
621    }2»
622
623    let b = «2{
624        «3{
625            «4{
626                «5[«6(«7[«1(«2[«3(«4[«5(«6[«7(«1[«2(«3[«4(«5[«6(«7[«1(«2[«3(«4()4», «4()4»)3»]2»)1»]7»)6»]5»)4»]3»)2»]1»)7»]6»)5»]4»)3»]2»)1»]7»)6»]5»
627            }4»
628        }3»
629    }2»;
630}1»
631
632#«1[rustfmt::skip]1»
633fn one«1(a: «2()2», «2()2»: «2()2», c: «2()2»)1» -> usize «1{ 1 }1»
634
635fn two«1<T>1»«1(a: HashMap«2<String, Vec«3<Option«4<T>4»>3»>2»)1» -> usize
636where
637    T: Future«1<Output = HashMap«2<String, Vec«3<Option«4<Box«5<«6()6»>5»>4»>3»>2»>1»,
638«1{
639    2
640}1»
641
6421 hsla(207.80, 81.00%, 66.00%, 1.00)
6432 hsla(29.00, 54.00%, 61.00%, 1.00)
6443 hsla(286.00, 51.00%, 64.00%, 1.00)
6454 hsla(187.00, 47.00%, 55.00%, 1.00)
6465 hsla(355.00, 65.00%, 65.00%, 1.00)
6476 hsla(95.00, 38.00%, 62.00%, 1.00)
6487 hsla(39.00, 67.00%, 69.00%, 1.00)
649"#,
650            &bracket_colors_markup(&mut cx),
651            "All brackets should be colored based on their depth"
652        );
653    }
654
655    #[gpui::test]
656    async fn test_file_less_file_colorization(cx: &mut gpui::TestAppContext) {
657        init_test(cx, |language_settings| {
658            language_settings.defaults.colorize_brackets = Some(true);
659        });
660        let editor = cx.add_window(|window, cx| {
661            let multi_buffer = MultiBuffer::build_simple("fn main() {}", cx);
662            multi_buffer.update(cx, |multi_buffer, cx| {
663                multi_buffer
664                    .as_singleton()
665                    .unwrap()
666                    .update(cx, |buffer, cx| {
667                        buffer.set_language(Some(rust_lang()), cx);
668                    });
669            });
670            Editor::new(EditorMode::full(), multi_buffer, None, window, cx)
671        });
672
673        cx.executor().advance_clock(Duration::from_millis(100));
674        cx.executor().run_until_parked();
675
676        assert_eq!(
677            "fn main«1()1» «1{}1»
6781 hsla(207.80, 81.00%, 66.00%, 1.00)
679",
680            editor
681                .update(cx, |editor, window, cx| {
682                    editor_bracket_colors_markup(&editor.snapshot(window, cx))
683                })
684                .unwrap(),
685            "File-less buffer should still have its brackets colorized"
686        );
687    }
688
689    #[gpui::test]
690    async fn test_markdown_bracket_colorization(cx: &mut gpui::TestAppContext) {
691        init_test(cx, |language_settings| {
692            language_settings.defaults.colorize_brackets = Some(true);
693        });
694        let mut cx = EditorLspTestContext::new(
695            Arc::into_inner(markdown_lang()).unwrap(),
696            lsp::ServerCapabilities::default(),
697            cx,
698        )
699        .await;
700
701        cx.set_state(indoc! {r#"ˇ[LLM-powered features](./ai/overview.md), [bring and configure your own API keys](./ai/llm-providers.md#use-your-own-keys)"#});
702        cx.executor().advance_clock(Duration::from_millis(100));
703        cx.executor().run_until_parked();
704
705        assert_eq!(
706            r#"«1[LLM-powered features]1»«1(./ai/overview.md)1», «1[bring and configure your own API keys]1»«1(./ai/llm-providers.md#use-your-own-keys)1»
7071 hsla(207.80, 81.00%, 66.00%, 1.00)
708"#,
709            &bracket_colors_markup(&mut cx),
710            "All markdown brackets should be colored based on their depth"
711        );
712
713        cx.set_state(indoc! {r#"ˇ{{}}"#});
714        cx.executor().advance_clock(Duration::from_millis(100));
715        cx.executor().run_until_parked();
716
717        assert_eq!(
718            r#"«1{«2{}2»}1»
7191 hsla(207.80, 81.00%, 66.00%, 1.00)
7202 hsla(29.00, 54.00%, 61.00%, 1.00)
721"#,
722            &bracket_colors_markup(&mut cx),
723            "All markdown brackets should be colored based on their depth, again"
724        );
725
726        cx.set_state(indoc! {r#"ˇ('')('')
727
728((''))('')
729
730('')((''))"#});
731        cx.executor().advance_clock(Duration::from_millis(100));
732        cx.executor().run_until_parked();
733
734        assert_eq!(
735            "«1('')1»«1('')1»\n\n«1(«2('')2»)1»«1('')1»\n\n«1('')1»«1(«2('')2»)1»\n1 hsla(207.80, 81.00%, 66.00%, 1.00)\n2 hsla(29.00, 54.00%, 61.00%, 1.00)\n",
736            &bracket_colors_markup(&mut cx),
737            "Markdown quote pairs should not interfere with parenthesis pairing"
738        );
739    }
740
741    #[gpui::test]
742    async fn test_markdown_brackets_in_multiple_hunks(cx: &mut gpui::TestAppContext) {
743        init_test(cx, |language_settings| {
744            language_settings.defaults.colorize_brackets = Some(true);
745        });
746        let mut cx = EditorLspTestContext::new(
747            Arc::into_inner(markdown_lang()).unwrap(),
748            lsp::ServerCapabilities::default(),
749            cx,
750        )
751        .await;
752
753        let rows = 100;
754        let footer = "1 hsla(207.80, 81.00%, 66.00%, 1.00)\n";
755
756        let simple_brackets = (0..rows).map(|_| "ˇ[]\n").collect::<String>();
757        let simple_brackets_highlights = (0..rows).map(|_| "«1[]1»\n").collect::<String>();
758        cx.set_state(&simple_brackets);
759        cx.update_editor(|editor, window, cx| {
760            editor.move_to_end(&MoveToEnd, window, cx);
761        });
762        cx.executor().advance_clock(Duration::from_millis(100));
763        cx.executor().run_until_parked();
764        assert_eq!(
765            format!("{simple_brackets_highlights}\n{footer}"),
766            bracket_colors_markup(&mut cx),
767            "Simple bracket pairs should be colored"
768        );
769
770        let paired_brackets = (0..rows).map(|_| "ˇ[]()\n").collect::<String>();
771        let paired_brackets_highlights = (0..rows).map(|_| "«1[]1»«1()1»\n").collect::<String>();
772        cx.set_state(&paired_brackets);
773        // Wait for reparse to complete after content change
774        cx.executor().advance_clock(Duration::from_millis(100));
775        cx.executor().run_until_parked();
776        cx.update_editor(|editor, _, cx| {
777            // Force invalidation of bracket cache after reparse
778            editor.colorize_brackets(true, cx);
779        });
780        // Scroll to beginning to fetch first chunks
781        cx.update_editor(|editor, window, cx| {
782            editor.move_to_beginning(&MoveToBeginning, window, cx);
783        });
784        cx.executor().advance_clock(Duration::from_millis(100));
785        cx.executor().run_until_parked();
786        // Scroll to end to fetch remaining chunks
787        cx.update_editor(|editor, window, cx| {
788            editor.move_to_end(&MoveToEnd, window, cx);
789        });
790        cx.executor().advance_clock(Duration::from_millis(100));
791        cx.executor().run_until_parked();
792        assert_eq!(
793            format!("{paired_brackets_highlights}\n{footer}"),
794            bracket_colors_markup(&mut cx),
795            "Paired bracket pairs should be colored"
796        );
797    }
798
799    #[gpui::test]
800    async fn test_markdown_code_block_brackets_across_chunks(cx: &mut gpui::TestAppContext) {
801        init_test(cx, |language_settings| {
802            language_settings.defaults.colorize_brackets = Some(true);
803        });
804
805        let language_registry = Arc::new(language::LanguageRegistry::test(cx.executor()));
806        language_registry.add(markdown_lang());
807        language_registry.add(rust_lang());
808
809        let mut cx = EditorTestContext::new(cx).await;
810        cx.update_buffer(|buffer, cx| {
811            buffer.set_language_registry(language_registry.clone());
812            buffer.set_language(Some(markdown_lang()), cx);
813        });
814
815        // The code block is longer than a single tree-sitter data chunk (50 rows),
816        // so the outer brackets open in the first chunk and close in the second one.
817        let filler = (0..58).map(|_| "        \"one\",\n").collect::<String>();
818        let source = format!("ˇ```rs\nfn main() {{\n    let a = vec![\n{filler}    ];\n}}\n```\n");
819        cx.set_state(&source);
820        cx.update_editor(|editor, window, cx| {
821            editor.move_to_end(&MoveToEnd, window, cx);
822        });
823        cx.executor().advance_clock(Duration::from_millis(100));
824        cx.executor().run_until_parked();
825        cx.update_editor(|editor, window, cx| {
826            editor.move_to_beginning(&MoveToBeginning, window, cx);
827        });
828        cx.executor().advance_clock(Duration::from_millis(100));
829        cx.executor().run_until_parked();
830
831        let expected = format!(
832            "```rs\nfn main«1()1» «1{{\n    let a = vec!«2[\n{filler}    ]2»;\n}}1»\n```\n\n1 hsla(207.80, 81.00%, 66.00%, 1.00)\n2 hsla(29.00, 54.00%, 61.00%, 1.00)\n"
833        );
834        assert_eq!(
835            expected,
836            bracket_colors_markup(&mut cx),
837            "Brackets crossing the 50-row chunk boundary inside a markdown code block should keep consistent colors"
838        );
839    }
840
841    #[gpui::test]
842    async fn test_bracket_colorization_after_language_swap(cx: &mut gpui::TestAppContext) {
843        init_test(cx, |language_settings| {
844            language_settings.defaults.colorize_brackets = Some(true);
845        });
846
847        let language_registry = Arc::new(language::LanguageRegistry::test(cx.executor()));
848        language_registry.add(markdown_lang());
849        language_registry.add(rust_lang());
850
851        let mut cx = EditorTestContext::new(cx).await;
852        cx.update_buffer(|buffer, cx| {
853            buffer.set_language_registry(language_registry.clone());
854            buffer.set_language(Some(markdown_lang()), cx);
855        });
856
857        cx.set_state(indoc! {r#"
858            fn main() {
859                let v: Vec<Stringˇ> = vec![];
860            }
861        "#});
862        cx.executor().advance_clock(Duration::from_millis(100));
863        cx.executor().run_until_parked();
864
865        assert_eq!(
866            r#"fn main«1()1» «1{
867    let v: Vec<String> = vec!«2[]2»;
868}1»
869
8701 hsla(207.80, 81.00%, 66.00%, 1.00)
8712 hsla(29.00, 54.00%, 61.00%, 1.00)
872"#,
873            &bracket_colors_markup(&mut cx),
874            "Markdown does not colorize <> brackets"
875        );
876
877        cx.update_buffer(|buffer, cx| {
878            buffer.set_language(Some(rust_lang()), cx);
879        });
880        cx.executor().advance_clock(Duration::from_millis(100));
881        cx.executor().run_until_parked();
882
883        assert_eq!(
884            r#"fn main«1()1» «1{
885    let v: Vec«2<String>2» = vec!«2[]2»;
886}1»
887
8881 hsla(207.80, 81.00%, 66.00%, 1.00)
8892 hsla(29.00, 54.00%, 61.00%, 1.00)
890"#,
891            &bracket_colors_markup(&mut cx),
892            "After switching to Rust, <> brackets are now colorized"
893        );
894    }
895
896    #[gpui::test]
897    async fn test_bracket_colorization_when_editing(cx: &mut gpui::TestAppContext) {
898        init_test(cx, |language_settings| {
899            language_settings.defaults.colorize_brackets = Some(true);
900        });
901        let mut cx = EditorLspTestContext::new(
902            Arc::into_inner(rust_lang()).unwrap(),
903            lsp::ServerCapabilities::default(),
904            cx,
905        )
906        .await;
907
908        cx.set_state(indoc! {r#"
909struct Foo<'a, T> {
910    data: Vec<Option<&'a T>>,
911}
912
913fn process_data() {
914    let map:ˇ
915}
916"#});
917
918        cx.update_editor(|editor, window, cx| {
919            editor.handle_input(" Result<", window, cx);
920        });
921        cx.executor().advance_clock(Duration::from_millis(100));
922        cx.executor().run_until_parked();
923        assert_eq!(
924            indoc! {r#"
925struct Foo«1<'a, T>1» «1{
926    data: Vec«2<Option«3<&'a T>3»>2»,
927}1»
928
929fn process_data«1()1» «1{
930    let map: Result<
931}1»
932
9331 hsla(207.80, 81.00%, 66.00%, 1.00)
9342 hsla(29.00, 54.00%, 61.00%, 1.00)
9353 hsla(286.00, 51.00%, 64.00%, 1.00)
936"#},
937            &bracket_colors_markup(&mut cx),
938            "Brackets without pairs should be ignored and not colored"
939        );
940
941        cx.update_editor(|editor, window, cx| {
942            editor.handle_input("Option<Foo<'_, ()", window, cx);
943        });
944        cx.executor().advance_clock(Duration::from_millis(100));
945        cx.executor().run_until_parked();
946        assert_eq!(
947            indoc! {r#"
948struct Foo«1<'a, T>1» «1{
949    data: Vec«2<Option«3<&'a T>3»>2»,
950}1»
951
952fn process_data«1()1» «1{
953    let map: Result<Option<Foo<'_, «2()2»
954}1»
955
9561 hsla(207.80, 81.00%, 66.00%, 1.00)
9572 hsla(29.00, 54.00%, 61.00%, 1.00)
9583 hsla(286.00, 51.00%, 64.00%, 1.00)
959"#},
960            &bracket_colors_markup(&mut cx),
961        );
962
963        cx.update_editor(|editor, window, cx| {
964            editor.handle_input(">", window, cx);
965        });
966        cx.executor().advance_clock(Duration::from_millis(100));
967        cx.executor().run_until_parked();
968        assert_eq!(
969            indoc! {r#"
970struct Foo«1<'a, T>1» «1{
971    data: Vec«2<Option«3<&'a T>3»>2»,
972}1»
973
974fn process_data«1()1» «1{
975    let map: Result<Option<Foo«2<'_, «3()3»>2»
976}1»
977
9781 hsla(207.80, 81.00%, 66.00%, 1.00)
9792 hsla(29.00, 54.00%, 61.00%, 1.00)
9803 hsla(286.00, 51.00%, 64.00%, 1.00)
981"#},
982            &bracket_colors_markup(&mut cx),
983            "When brackets start to get closed, inner brackets are re-colored based on their depth"
984        );
985
986        cx.update_editor(|editor, window, cx| {
987            editor.handle_input(">", window, cx);
988        });
989        cx.executor().advance_clock(Duration::from_millis(100));
990        cx.executor().run_until_parked();
991        assert_eq!(
992            indoc! {r#"
993struct Foo«1<'a, T>1» «1{
994    data: Vec«2<Option«3<&'a T>3»>2»,
995}1»
996
997fn process_data«1()1» «1{
998    let map: Result<Option«2<Foo«3<'_, «4()4»>3»>2»
999}1»
1000
10011 hsla(207.80, 81.00%, 66.00%, 1.00)
10022 hsla(29.00, 54.00%, 61.00%, 1.00)
10033 hsla(286.00, 51.00%, 64.00%, 1.00)
10044 hsla(187.00, 47.00%, 55.00%, 1.00)
1005"#},
1006            &bracket_colors_markup(&mut cx),
1007        );
1008
1009        cx.update_editor(|editor, window, cx| {
1010            editor.handle_input(", ()> = unimplemented!();", window, cx);
1011        });
1012        cx.executor().advance_clock(Duration::from_millis(100));
1013        cx.executor().run_until_parked();
1014        assert_eq!(
1015            indoc! {r#"
1016struct Foo«1<'a, T>1» «1{
1017    data: Vec«2<Option«3<&'a T>3»>2»,
1018}1»
1019
1020fn process_data«1()1» «1{
1021    let map: Result«2<Option«3<Foo«4<'_, «5()5»>4»>3», «3()3»>2» = unimplemented!«2()2»;
1022}1»
1023
10241 hsla(207.80, 81.00%, 66.00%, 1.00)
10252 hsla(29.00, 54.00%, 61.00%, 1.00)
10263 hsla(286.00, 51.00%, 64.00%, 1.00)
10274 hsla(187.00, 47.00%, 55.00%, 1.00)
10285 hsla(355.00, 65.00%, 65.00%, 1.00)
1029"#},
1030            &bracket_colors_markup(&mut cx),
1031        );
1032    }
1033
1034    #[gpui::test]
1035    async fn test_bracket_colorization_chunks(cx: &mut gpui::TestAppContext) {
1036        let comment_lines = 100;
1037
1038        init_test(cx, |language_settings| {
1039            language_settings.defaults.colorize_brackets = Some(true);
1040        });
1041        let mut cx = EditorLspTestContext::new(
1042            Arc::into_inner(rust_lang()).unwrap(),
1043            lsp::ServerCapabilities::default(),
1044            cx,
1045        )
1046        .await;
1047
1048        cx.set_state(&separate_with_comment_lines(
1049            indoc! {r#"
1050mod foo {
1051    ˇfn process_data_1() {
1052        let map: Option<Vec<()>> = None;
1053    }
1054"#},
1055            indoc! {r#"
1056    fn process_data_2() {
1057        let map: Option<Vec<()>> = None;
1058    }
1059}
1060"#},
1061            comment_lines,
1062        ));
1063
1064        cx.executor().advance_clock(Duration::from_millis(100));
1065        cx.executor().run_until_parked();
1066        assert_eq!(
1067            &separate_with_comment_lines(
1068                indoc! {r#"
1069mod foo «1{
1070    fn process_data_1«2()2» «2{
1071        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1072    }2»
1073"#},
1074                indoc! {r#"
1075    fn process_data_2() {
1076        let map: Option<Vec<()>> = None;
1077    }
1078}1»
1079
10801 hsla(207.80, 81.00%, 66.00%, 1.00)
10812 hsla(29.00, 54.00%, 61.00%, 1.00)
10823 hsla(286.00, 51.00%, 64.00%, 1.00)
10834 hsla(187.00, 47.00%, 55.00%, 1.00)
10845 hsla(355.00, 65.00%, 65.00%, 1.00)
1085"#},
1086                comment_lines,
1087            ),
1088            &bracket_colors_markup(&mut cx),
1089            "First, the only visible chunk is getting the bracket highlights"
1090        );
1091
1092        cx.update_editor(|editor, window, cx| {
1093            editor.move_to_end(&MoveToEnd, window, cx);
1094            editor.move_up(&MoveUp, window, cx);
1095        });
1096        cx.executor().advance_clock(Duration::from_millis(100));
1097        cx.executor().run_until_parked();
1098        assert_eq!(
1099            &separate_with_comment_lines(
1100                indoc! {r#"
1101mod foo «1{
1102    fn process_data_1«2()2» «2{
1103        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1104    }2»
1105"#},
1106                indoc! {r#"
1107    fn process_data_2«2()2» «2{
1108        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1109    }2»
1110}1»
1111
11121 hsla(207.80, 81.00%, 66.00%, 1.00)
11132 hsla(29.00, 54.00%, 61.00%, 1.00)
11143 hsla(286.00, 51.00%, 64.00%, 1.00)
11154 hsla(187.00, 47.00%, 55.00%, 1.00)
11165 hsla(355.00, 65.00%, 65.00%, 1.00)
1117"#},
1118                comment_lines,
1119            ),
1120            &bracket_colors_markup(&mut cx),
1121            "After scrolling to the bottom, both chunks should have the highlights"
1122        );
1123
1124        cx.update_editor(|editor, window, cx| {
1125            editor.handle_input("{{}}}", window, cx);
1126        });
1127        cx.executor().advance_clock(Duration::from_millis(100));
1128        cx.executor().run_until_parked();
1129        assert_eq!(
1130            &separate_with_comment_lines(
1131                indoc! {r#"
1132mod foo «1{
1133    fn process_data_1() {
1134        let map: Option<Vec<()>> = None;
1135    }
1136"#},
1137                indoc! {r#"
1138    fn process_data_2«2()2» «2{
1139        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1140    }
1141    «3{«4{}4»}3»}2»}1»
1142
11431 hsla(207.80, 81.00%, 66.00%, 1.00)
11442 hsla(29.00, 54.00%, 61.00%, 1.00)
11453 hsla(286.00, 51.00%, 64.00%, 1.00)
11464 hsla(187.00, 47.00%, 55.00%, 1.00)
11475 hsla(355.00, 65.00%, 65.00%, 1.00)
1148"#},
1149                comment_lines,
1150            ),
1151            &bracket_colors_markup(&mut cx),
1152            "First chunk's brackets are invalidated after an edit, and only 2nd (visible) chunk is re-colorized"
1153        );
1154
1155        cx.update_editor(|editor, window, cx| {
1156            editor.move_to_beginning(&MoveToBeginning, window, cx);
1157        });
1158        cx.executor().advance_clock(Duration::from_millis(100));
1159        cx.executor().run_until_parked();
1160        assert_eq!(
1161            &separate_with_comment_lines(
1162                indoc! {r#"
1163mod foo «1{
1164    fn process_data_1«2()2» «2{
1165        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1166    }2»
1167"#},
1168                indoc! {r#"
1169    fn process_data_2«2()2» «2{
1170        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1171    }
1172    «3{«4{}4»}3»}2»}1»
1173
11741 hsla(207.80, 81.00%, 66.00%, 1.00)
11752 hsla(29.00, 54.00%, 61.00%, 1.00)
11763 hsla(286.00, 51.00%, 64.00%, 1.00)
11774 hsla(187.00, 47.00%, 55.00%, 1.00)
11785 hsla(355.00, 65.00%, 65.00%, 1.00)
1179"#},
1180                comment_lines,
1181            ),
1182            &bracket_colors_markup(&mut cx),
1183            "Scrolling back to top should re-colorize all chunks' brackets"
1184        );
1185
1186        cx.update(|_, cx| {
1187            SettingsStore::update_global(cx, |store, cx| {
1188                store.update_user_settings(cx, |settings| {
1189                    settings.project.all_languages.defaults.colorize_brackets = Some(false);
1190                });
1191            });
1192        });
1193        cx.executor().run_until_parked();
1194        assert_eq!(
1195            &separate_with_comment_lines(
1196                indoc! {r#"
1197mod foo {
1198    fn process_data_1() {
1199        let map: Option<Vec<()>> = None;
1200    }
1201"#},
1202                r#"    fn process_data_2() {
1203        let map: Option<Vec<()>> = None;
1204    }
1205    {{}}}}
1206
1207"#,
1208                comment_lines,
1209            ),
1210            &bracket_colors_markup(&mut cx),
1211            "Turning bracket colorization off should remove all bracket colors"
1212        );
1213
1214        cx.update(|_, cx| {
1215            SettingsStore::update_global(cx, |store, cx| {
1216                store.update_user_settings(cx, |settings| {
1217                    settings.project.all_languages.defaults.colorize_brackets = Some(true);
1218                });
1219            });
1220        });
1221        cx.executor().run_until_parked();
1222        assert_eq!(
1223            &separate_with_comment_lines(
1224                indoc! {r#"
1225mod foo «1{
1226    fn process_data_1«2()2» «2{
1227        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1228    }2»
1229"#},
1230                r#"    fn process_data_2() {
1231        let map: Option<Vec<()>> = None;
1232    }
1233    {{}}}}1»
1234
12351 hsla(207.80, 81.00%, 66.00%, 1.00)
12362 hsla(29.00, 54.00%, 61.00%, 1.00)
12373 hsla(286.00, 51.00%, 64.00%, 1.00)
12384 hsla(187.00, 47.00%, 55.00%, 1.00)
12395 hsla(355.00, 65.00%, 65.00%, 1.00)
1240"#,
1241                comment_lines,
1242            ),
1243            &bracket_colors_markup(&mut cx),
1244            "Turning bracket colorization back on refreshes the visible excerpts' bracket colors"
1245        );
1246    }
1247
1248    #[gpui::test]
1249    async fn test_rainbow_bracket_highlights(cx: &mut gpui::TestAppContext) {
1250        init_test(cx, |language_settings| {
1251            language_settings.defaults.colorize_brackets = Some(true);
1252        });
1253        let mut cx = EditorLspTestContext::new(
1254            Arc::into_inner(rust_lang()).unwrap(),
1255            lsp::ServerCapabilities::default(),
1256            cx,
1257        )
1258        .await;
1259
1260        // taken from r-a https://github.com/rust-lang/rust-analyzer/blob/d733c07552a2dc0ec0cc8f4df3f0ca969a93fd90/crates/ide/src/inlay_hints.rs#L81-L297
1261        cx.set_state(indoc! {r#"ˇ
1262            pub(crate) fn inlay_hints(
1263                db: &RootDatabase,
1264                file_id: FileId,
1265                range_limit: Option<TextRange>,
1266                config: &InlayHintsConfig,
1267            ) -> Vec<InlayHint> {
1268                let _p = tracing::info_span!("inlay_hints").entered();
1269                let sema = Semantics::new(db);
1270                let file_id = sema
1271                    .attach_first_edition(file_id)
1272                    .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
1273                let file = sema.parse(file_id);
1274                let file = file.syntax();
1275
1276                let mut acc = Vec::new();
1277
1278                let Some(scope) = sema.scope(file) else {
1279                    return acc;
1280                };
1281                let famous_defs = FamousDefs(&sema, scope.krate());
1282                let display_target = famous_defs.1.to_display_target(sema.db);
1283
1284                let ctx = &mut InlayHintCtx::default();
1285                let mut hints = |event| {
1286                    if let Some(node) = handle_event(ctx, event) {
1287                        hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
1288                    }
1289                };
1290                let mut preorder = file.preorder();
1291                salsa::attach(sema.db, || {
1292                    while let Some(event) = preorder.next() {
1293                        if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none())
1294                        {
1295                            preorder.skip_subtree();
1296                            continue;
1297                        }
1298                        hints(event);
1299                    }
1300                });
1301                if let Some(range_limit) = range_limit {
1302                    acc.retain(|hint| range_limit.contains_range(hint.range));
1303                }
1304                acc
1305            }
1306
1307            #[derive(Default)]
1308            struct InlayHintCtx {
1309                lifetime_stacks: Vec<Vec<SmolStr>>,
1310                extern_block_parent: Option<ast::ExternBlock>,
1311            }
1312
1313            pub(crate) fn inlay_hints_resolve(
1314                db: &RootDatabase,
1315                file_id: FileId,
1316                resolve_range: TextRange,
1317                hash: u64,
1318                config: &InlayHintsConfig,
1319                hasher: impl Fn(&InlayHint) -> u64,
1320            ) -> Option<InlayHint> {
1321                let _p = tracing::info_span!("inlay_hints_resolve").entered();
1322                let sema = Semantics::new(db);
1323                let file_id = sema
1324                    .attach_first_edition(file_id)
1325                    .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
1326                let file = sema.parse(file_id);
1327                let file = file.syntax();
1328
1329                let scope = sema.scope(file)?;
1330                let famous_defs = FamousDefs(&sema, scope.krate());
1331                let mut acc = Vec::new();
1332
1333                let display_target = famous_defs.1.to_display_target(sema.db);
1334
1335                let ctx = &mut InlayHintCtx::default();
1336                let mut hints = |event| {
1337                    if let Some(node) = handle_event(ctx, event) {
1338                        hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
1339                    }
1340                };
1341
1342                let mut preorder = file.preorder();
1343                while let Some(event) = preorder.next() {
1344                    // This can miss some hints that require the parent of the range to calculate
1345                    if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none())
1346                    {
1347                        preorder.skip_subtree();
1348                        continue;
1349                    }
1350                    hints(event);
1351                }
1352                acc.into_iter().find(|hint| hasher(hint) == hash)
1353            }
1354
1355            fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<SyntaxNode> {
1356                match node {
1357                    WalkEvent::Enter(node) => {
1358                        if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
1359                            let params = node
1360                                .generic_param_list()
1361                                .map(|it| {
1362                                    it.lifetime_params()
1363                                        .filter_map(|it| {
1364                                            it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..]))
1365                                        })
1366                                        .collect()
1367                                })
1368                                .unwrap_or_default();
1369                            ctx.lifetime_stacks.push(params);
1370                        }
1371                        if let Some(node) = ast::ExternBlock::cast(node.clone()) {
1372                            ctx.extern_block_parent = Some(node);
1373                        }
1374                        Some(node)
1375                    }
1376                    WalkEvent::Leave(n) => {
1377                        if ast::AnyHasGenericParams::can_cast(n.kind()) {
1378                            ctx.lifetime_stacks.pop();
1379                        }
1380                        if ast::ExternBlock::can_cast(n.kind()) {
1381                            ctx.extern_block_parent = None;
1382                        }
1383                        None
1384                    }
1385                }
1386            }
1387
1388            // At some point when our hir infra is fleshed out enough we should flip this and traverse the
1389            // HIR instead of the syntax tree.
1390            fn hints(
1391                hints: &mut Vec<InlayHint>,
1392                ctx: &mut InlayHintCtx,
1393                famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>,
1394                config: &InlayHintsConfig,
1395                file_id: EditionedFileId,
1396                display_target: DisplayTarget,
1397                node: SyntaxNode,
1398            ) {
1399                closing_brace::hints(
1400                    hints,
1401                    sema,
1402                    config,
1403                    display_target,
1404                    InRealFile { file_id, value: node.clone() },
1405                );
1406                if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
1407                    generic_param::hints(hints, famous_defs, config, any_has_generic_args);
1408                }
1409
1410                match_ast! {
1411                    match node {
1412                        ast::Expr(expr) => {
1413                            chaining::hints(hints, famous_defs, config, display_target, &expr);
1414                            adjustment::hints(hints, famous_defs, config, display_target, &expr);
1415                            match expr {
1416                                ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
1417                                ast::Expr::MethodCallExpr(it) => {
1418                                    param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
1419                                }
1420                                ast::Expr::ClosureExpr(it) => {
1421                                    closure_captures::hints(hints, famous_defs, config, it.clone());
1422                                    closure_ret::hints(hints, famous_defs, config, display_target, it)
1423                                },
1424                                ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
1425                                _ => Some(()),
1426                            }
1427                        },
1428                        ast::Pat(it) => {
1429                            binding_mode::hints(hints, famous_defs, config, &it);
1430                            match it {
1431                                ast::Pat::IdentPat(it) => {
1432                                    bind_pat::hints(hints, famous_defs, config, display_target, &it);
1433                                }
1434                                ast::Pat::RangePat(it) => {
1435                                    range_exclusive::hints(hints, famous_defs, config, it);
1436                                }
1437                                _ => {}
1438                            }
1439                            Some(())
1440                        },
1441                        ast::Item(it) => match it {
1442                            ast::Item::Fn(it) => {
1443                                implicit_drop::hints(hints, famous_defs, config, display_target, &it);
1444                                if let Some(extern_block) = &ctx.extern_block_parent {
1445                                    extern_block::fn_hints(hints, famous_defs, config, &it, extern_block);
1446                                }
1447                                lifetime::fn_hints(hints, ctx, famous_defs, config,  it)
1448                            },
1449                            ast::Item::Static(it) => {
1450                                if let Some(extern_block) = &ctx.extern_block_parent {
1451                                    extern_block::static_hints(hints, famous_defs, config, &it, extern_block);
1452                                }
1453                                implicit_static::hints(hints, famous_defs, config,  Either::Left(it))
1454                            },
1455                            ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, Either::Right(it)),
1456                            ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, it),
1457                            ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, it),
1458                            _ => None,
1459                        },
1460                        // trait object type elisions
1461                        ast::Type(ty) => match ty {
1462                            ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config,  ptr),
1463                            ast::Type::PathType(path) => {
1464                                lifetime::fn_path_hints(hints, ctx, famous_defs, config, &path);
1465                                implied_dyn_trait::hints(hints, famous_defs, config, Either::Left(path));
1466                                Some(())
1467                            },
1468                            ast::Type::DynTraitType(dyn_) => {
1469                                implied_dyn_trait::hints(hints, famous_defs, config, Either::Right(dyn_));
1470                                Some(())
1471                            },
1472                            _ => Some(()),
1473                        },
1474                        ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config,  it),
1475                        _ => Some(()),
1476                    }
1477                };
1478            }
1479        "#});
1480        cx.executor().advance_clock(Duration::from_millis(100));
1481        cx.executor().run_until_parked();
1482
1483        let actual_ranges = cx.update_editor(|editor, window, cx| {
1484            editor
1485                .snapshot(window, cx)
1486                .all_text_highlight_ranges(&|key| matches!(key, HighlightKey::ColorizeBracket(_)))
1487        });
1488
1489        let mut highlighted_brackets = HashMap::default();
1490        for (color, range) in actual_ranges.iter().cloned() {
1491            highlighted_brackets.insert(range, color);
1492        }
1493
1494        let last_bracket = actual_ranges
1495            .iter()
1496            .max_by_key(|(_, p)| p.end.row)
1497            .unwrap()
1498            .clone();
1499
1500        cx.update_editor(|editor, window, cx| {
1501            let was_scrolled = editor.set_scroll_position(
1502                gpui::Point::new(0.0, last_bracket.1.end.row as f64 * 2.0),
1503                window,
1504                cx,
1505            );
1506            assert!(was_scrolled.0);
1507        });
1508        cx.executor().advance_clock(Duration::from_millis(100));
1509        cx.executor().run_until_parked();
1510
1511        let ranges_after_scrolling = cx.update_editor(|editor, window, cx| {
1512            editor
1513                .snapshot(window, cx)
1514                .all_text_highlight_ranges(&|key| matches!(key, HighlightKey::ColorizeBracket(_)))
1515        });
1516        let new_last_bracket = ranges_after_scrolling
1517            .iter()
1518            .max_by_key(|(_, p)| p.end.row)
1519            .unwrap()
1520            .clone();
1521
1522        assert_ne!(
1523            last_bracket, new_last_bracket,
1524            "After scrolling down, we should have highlighted more brackets"
1525        );
1526
1527        cx.update_editor(|editor, window, cx| {
1528            let was_scrolled = editor.set_scroll_position(gpui::Point::default(), window, cx);
1529            assert!(was_scrolled.0);
1530        });
1531
1532        for _ in 0..200 {
1533            cx.update_editor(|editor, window, cx| {
1534                editor.apply_scroll_delta(gpui::Point::new(0.0, 0.25), window, cx);
1535            });
1536            cx.executor().advance_clock(Duration::from_millis(100));
1537            cx.executor().run_until_parked();
1538
1539            let colored_brackets = cx.update_editor(|editor, window, cx| {
1540                editor
1541                    .snapshot(window, cx)
1542                    .all_text_highlight_ranges(&|key| {
1543                        matches!(key, HighlightKey::ColorizeBracket(_))
1544                    })
1545            });
1546            for (color, range) in colored_brackets.clone() {
1547                assert!(
1548                    highlighted_brackets.entry(range).or_insert(color) == &color,
1549                    "Colors should stay consistent while scrolling!"
1550                );
1551            }
1552
1553            let snapshot = cx.update_editor(|editor, window, cx| editor.snapshot(window, cx));
1554            let scroll_position = snapshot.scroll_position();
1555            let visible_lines =
1556                cx.update_editor(|editor, _, _| editor.visible_line_count().unwrap());
1557            let visible_range = DisplayRow(scroll_position.y as u32)
1558                ..DisplayRow((scroll_position.y + visible_lines) as u32);
1559
1560            let current_highlighted_bracket_set: HashSet<Point> = HashSet::from_iter(
1561                colored_brackets
1562                    .iter()
1563                    .flat_map(|(_, range)| [range.start, range.end]),
1564            );
1565
1566            for highlight_range in highlighted_brackets.keys().filter(|bracket_range| {
1567                visible_range.contains(&bracket_range.start.to_display_point(&snapshot).row())
1568                    || visible_range.contains(&bracket_range.end.to_display_point(&snapshot).row())
1569            }) {
1570                assert!(
1571                    current_highlighted_bracket_set.contains(&highlight_range.start)
1572                        || current_highlighted_bracket_set.contains(&highlight_range.end),
1573                    "Should not lose highlights while scrolling in the visible range!"
1574                );
1575            }
1576
1577            let buffer_snapshot = snapshot.buffer().as_singleton().unwrap();
1578            for bracket_match in buffer_snapshot
1579                .fetch_bracket_ranges(
1580                    snapshot
1581                        .display_point_to_point(
1582                            DisplayPoint::new(visible_range.start, 0),
1583                            Bias::Left,
1584                        )
1585                        .to_offset(&buffer_snapshot)
1586                        ..snapshot
1587                            .display_point_to_point(
1588                                DisplayPoint::new(
1589                                    visible_range.end,
1590                                    snapshot.line_len(visible_range.end),
1591                                ),
1592                                Bias::Right,
1593                            )
1594                            .to_offset(&buffer_snapshot),
1595                    None,
1596                )
1597                .iter()
1598                .flat_map(|entry| entry.1)
1599                .filter(|bracket_match| bracket_match.color_index.is_some())
1600            {
1601                let start = bracket_match.open_range.to_point(buffer_snapshot);
1602                let end = bracket_match.close_range.to_point(buffer_snapshot);
1603                let start_bracket = colored_brackets.iter().find(|(_, range)| *range == start);
1604                assert!(
1605                    start_bracket.is_some(),
1606                    "Existing bracket start in the visible range should be highlighted. Missing color for match: \"{}\" at position {:?}",
1607                    buffer_snapshot
1608                        .text_for_range(start.start..end.end)
1609                        .collect::<String>(),
1610                    start
1611                );
1612
1613                let end_bracket = colored_brackets.iter().find(|(_, range)| *range == end);
1614                assert!(
1615                    end_bracket.is_some(),
1616                    "Existing bracket end in the visible range should be highlighted. Missing color for match: \"{}\" at position {:?}",
1617                    buffer_snapshot
1618                        .text_for_range(start.start..end.end)
1619                        .collect::<String>(),
1620                    start
1621                );
1622
1623                assert_eq!(
1624                    start_bracket.unwrap().0,
1625                    end_bracket.unwrap().0,
1626                    "Bracket pair should be highlighted the same color!"
1627                )
1628            }
1629        }
1630    }
1631
1632    #[gpui::test]
1633    async fn test_multi_buffer(cx: &mut gpui::TestAppContext) {
1634        let comment_lines = 100;
1635
1636        init_test(cx, |language_settings| {
1637            language_settings.defaults.colorize_brackets = Some(true);
1638        });
1639        let fs = FakeFs::new(cx.background_executor.clone());
1640        fs.insert_tree(
1641            path!("/a"),
1642            json!({
1643                "main.rs": "fn main() {{()}}",
1644                "lib.rs": separate_with_comment_lines(
1645                    indoc! {r#"
1646    mod foo {
1647        fn process_data_1() {
1648            let map: Option<Vec<()>> = None;
1649            // a
1650            // b
1651            // c
1652        }
1653    "#},
1654                    indoc! {r#"
1655        fn process_data_2() {
1656            let other_map: Option<Vec<()>> = None;
1657        }
1658    }
1659    "#},
1660                    comment_lines,
1661                )
1662            }),
1663        )
1664        .await;
1665
1666        let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
1667        let language_registry = project.read_with(cx, |project, _| project.languages().clone());
1668        language_registry.add(rust_lang());
1669
1670        let buffer_1 = project
1671            .update(cx, |project, cx| {
1672                project.open_local_buffer(path!("/a/lib.rs"), cx)
1673            })
1674            .await
1675            .unwrap();
1676        let buffer_2 = project
1677            .update(cx, |project, cx| {
1678                project.open_local_buffer(path!("/a/main.rs"), cx)
1679            })
1680            .await
1681            .unwrap();
1682
1683        let multi_buffer = cx.new(|cx| {
1684            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
1685            multi_buffer.set_excerpts_for_path(
1686                PathKey::sorted(0),
1687                buffer_2.clone(),
1688                [Point::new(0, 0)..Point::new(1, 0)],
1689                0,
1690                cx,
1691            );
1692
1693            let excerpt_rows = 5;
1694            let rest_of_first_except_rows = 3;
1695            multi_buffer.set_excerpts_for_path(
1696                PathKey::sorted(1),
1697                buffer_1.clone(),
1698                [
1699                    Point::new(0, 0)..Point::new(excerpt_rows, 0),
1700                    Point::new(
1701                        comment_lines as u32 + excerpt_rows + rest_of_first_except_rows,
1702                        0,
1703                    )
1704                        ..Point::new(
1705                            comment_lines as u32
1706                                + excerpt_rows
1707                                + rest_of_first_except_rows
1708                                + excerpt_rows,
1709                            0,
1710                        ),
1711                ],
1712                0,
1713                cx,
1714            );
1715            multi_buffer
1716        });
1717
1718        let editor = cx.add_window(|window, cx| {
1719            Editor::for_multibuffer(multi_buffer, Some(project.clone()), window, cx)
1720        });
1721        cx.executor().advance_clock(Duration::from_millis(100));
1722        cx.executor().run_until_parked();
1723
1724        let editor_snapshot = editor
1725            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1726            .unwrap();
1727        assert_eq!(
1728            indoc! {r#"
1729
1730
1731fn main«1()1» «1{«2{«3()3»}2»}1»
1732
1733
1734mod foo «1{
1735    fn process_data_1«2()2» «2{
1736        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1737        // a
1738        // b
1739        // c
1740
1741    fn process_data_2«2()2» «2{
1742        let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
1743    }2»
1744}1»
1745
17461 hsla(207.80, 81.00%, 66.00%, 1.00)
17472 hsla(29.00, 54.00%, 61.00%, 1.00)
17483 hsla(286.00, 51.00%, 64.00%, 1.00)
17494 hsla(187.00, 47.00%, 55.00%, 1.00)
17505 hsla(355.00, 65.00%, 65.00%, 1.00)
1751"#,},
1752            &editor_bracket_colors_markup(&editor_snapshot),
1753            "Multi buffers should have their brackets colored even if no excerpts contain the bracket counterpart (after fn `process_data_2()`) \
1754or if the buffer pair spans across multiple excerpts (the one after `mod foo`)"
1755        );
1756
1757        editor
1758            .update(cx, |editor, window, cx| {
1759                editor.handle_input("{[]", window, cx);
1760            })
1761            .unwrap();
1762        cx.executor().advance_clock(Duration::from_millis(100));
1763        cx.executor().run_until_parked();
1764        let editor_snapshot = editor
1765            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1766            .unwrap();
1767        assert_eq!(
1768            indoc! {r#"
1769
1770
1771{«1[]1»fn main«1()1» «1{«2{«3()3»}2»}1»
1772
1773
1774mod foo «1{
1775    fn process_data_1«2()2» «2{
1776        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1777        // a
1778        // b
1779        // c
1780
1781    fn process_data_2«2()2» «2{
1782        let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
1783    }2»
1784}1»
1785
17861 hsla(207.80, 81.00%, 66.00%, 1.00)
17872 hsla(29.00, 54.00%, 61.00%, 1.00)
17883 hsla(286.00, 51.00%, 64.00%, 1.00)
17894 hsla(187.00, 47.00%, 55.00%, 1.00)
17905 hsla(355.00, 65.00%, 65.00%, 1.00)
1791"#,},
1792            &editor_bracket_colors_markup(&editor_snapshot),
1793        );
1794
1795        cx.update(|cx| {
1796            let theme = cx.theme().name.clone();
1797            SettingsStore::update_global(cx, |store, cx| {
1798                store.update_user_settings(cx, |settings| {
1799                    settings.theme.theme_overrides = HashMap::from_iter([(
1800                        theme.to_string(),
1801                        ThemeStyleContent {
1802                            accents: vec![
1803                                AccentContent(Some("#ff0000".to_string())),
1804                                AccentContent(Some("#0000ff".to_string())),
1805                            ],
1806                            ..ThemeStyleContent::default()
1807                        },
1808                    )]);
1809                });
1810            });
1811        });
1812        cx.executor().advance_clock(Duration::from_millis(100));
1813        cx.executor().run_until_parked();
1814        let editor_snapshot = editor
1815            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1816            .unwrap();
1817        let adjusted_palette = cx.update(|cx| {
1818            bracket_colorization_accents(
1819                &[
1820                    Hsla::from(Rgba::try_from("#ff0000").expect("valid override accent")),
1821                    Hsla::from(Rgba::try_from("#0000ff").expect("valid override accent")),
1822                ],
1823                cx.theme().appearance,
1824                cx.theme().colors().editor_background,
1825            )
1826        });
1827        let expected_markup = format!(
1828            "{}\n1 {}\n2 {}\n",
1829            indoc! {r#"
1830
1831
1832{«1[]1»fn main«1()1» «1{«2{«1()1»}2»}1»
1833
1834
1835mod foo «1{
1836    fn process_data_1«2()2» «2{
1837        let map: Option«1<Vec«2<«1()1»>2»>1» = None;
1838        // a
1839        // b
1840        // c
1841
1842    fn process_data_2«2()2» «2{
1843        let other_map: Option«1<Vec«2<«1()1»>2»>1» = None;
1844    }2»
1845}1»
1846"#,},
1847            adjusted_palette[0],
1848            adjusted_palette[1],
1849        );
1850        assert_eq!(
1851            expected_markup,
1852            editor_bracket_colors_markup(&editor_snapshot),
1853            "After updating theme accents, the editor should update the bracket coloring"
1854        );
1855    }
1856
1857    #[gpui::test]
1858    async fn test_multi_buffer_close_excerpts(cx: &mut gpui::TestAppContext) {
1859        let comment_lines = 5;
1860
1861        init_test(cx, |language_settings| {
1862            language_settings.defaults.colorize_brackets = Some(true);
1863        });
1864        let fs = FakeFs::new(cx.background_executor.clone());
1865        fs.insert_tree(
1866            path!("/a"),
1867            json!({
1868                "lib.rs": separate_with_comment_lines(
1869                    indoc! {r#"
1870    fn process_data_1() {
1871        let map: Option<Vec<()>> = None;
1872    }
1873    "#},
1874                    indoc! {r#"
1875    fn process_data_2() {
1876        let other_map: Option<Vec<()>> = None;
1877    }
1878    "#},
1879                    comment_lines,
1880                )
1881            }),
1882        )
1883        .await;
1884
1885        let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
1886        let language_registry = project.read_with(cx, |project, _| project.languages().clone());
1887        language_registry.add(rust_lang());
1888
1889        let buffer_1 = project
1890            .update(cx, |project, cx| {
1891                project.open_local_buffer(path!("/a/lib.rs"), cx)
1892            })
1893            .await
1894            .unwrap();
1895
1896        let second_excerpt_start = buffer_1.read_with(cx, |buffer, _| {
1897            let text = buffer.text();
1898            text.lines()
1899                .enumerate()
1900                .find(|(_, line)| line.contains("process_data_2"))
1901                .map(|(row, _)| row as u32)
1902                .unwrap()
1903        });
1904
1905        let multi_buffer = cx.new(|cx| {
1906            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
1907            multi_buffer.set_excerpts_for_path(
1908                PathKey::sorted(0),
1909                buffer_1.clone(),
1910                [
1911                    Point::new(0, 0)..Point::new(3, 0),
1912                    Point::new(second_excerpt_start, 0)..Point::new(second_excerpt_start + 3, 0),
1913                ],
1914                0,
1915                cx,
1916            );
1917            multi_buffer
1918        });
1919
1920        let editor = cx.add_window(|window, cx| {
1921            Editor::for_multibuffer(multi_buffer, Some(project.clone()), window, cx)
1922        });
1923        cx.executor().advance_clock(Duration::from_millis(100));
1924        cx.executor().run_until_parked();
1925
1926        let editor_snapshot = editor
1927            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1928            .unwrap();
1929        assert_eq!(
1930            concat!(
1931                "\n",
1932                "\n",
1933                "fn process_data_1\u{00ab}1()1\u{00bb} \u{00ab}1{\n",
1934                "    let map: Option\u{00ab}2<Vec\u{00ab}3<\u{00ab}4()4\u{00bb}>3\u{00bb}>2\u{00bb} = None;\n",
1935                "}1\u{00bb}\n",
1936                "\n",
1937                "\n",
1938                "fn process_data_2\u{00ab}1()1\u{00bb} \u{00ab}1{\n",
1939                "    let other_map: Option\u{00ab}2<Vec\u{00ab}3<\u{00ab}4()4\u{00bb}>3\u{00bb}>2\u{00bb} = None;\n",
1940                "}1\u{00bb}\n",
1941                "\n",
1942                "1 hsla(207.80, 81.00%, 66.00%, 1.00)\n",
1943                "2 hsla(29.00, 54.00%, 61.00%, 1.00)\n",
1944                "3 hsla(286.00, 51.00%, 64.00%, 1.00)\n",
1945                "4 hsla(187.00, 47.00%, 55.00%, 1.00)\n",
1946            ),
1947            &editor_bracket_colors_markup(&editor_snapshot),
1948            "Two close excerpts from the same buffer (within same tree-sitter chunk) should both have bracket colors"
1949        );
1950    }
1951
1952    #[gpui::test]
1953    // reproduction of #47846
1954    async fn test_bracket_colorization_with_folds(cx: &mut gpui::TestAppContext) {
1955        init_test(cx, |language_settings| {
1956            language_settings.defaults.colorize_brackets = Some(true);
1957        });
1958        let mut cx = EditorLspTestContext::new(
1959            Arc::into_inner(rust_lang()).unwrap(),
1960            lsp::ServerCapabilities::default(),
1961            cx,
1962        )
1963        .await;
1964
1965        // Generate a large function body. When folded, this collapses
1966        // to a single display line, making small_function visible on screen.
1967        let mut big_body = String::new();
1968        for i in 0..700 {
1969            big_body.push_str(&format!("    let var_{i:04} = ({i});\n"));
1970        }
1971        let source = format!(
1972            "ˇfn big_function() {{\n{big_body}}}\n\nfn small_function() {{\n    let x = (1, (2, 3));\n}}\n"
1973        );
1974
1975        cx.set_state(&source);
1976        cx.executor().advance_clock(Duration::from_millis(100));
1977        cx.executor().run_until_parked();
1978
1979        cx.update_editor(|editor, window, cx| {
1980            editor.fold_ranges(
1981                vec![Point::new(0, 0)..Point::new(701, 1)],
1982                false,
1983                window,
1984                cx,
1985            );
1986        });
1987        cx.executor().advance_clock(Duration::from_millis(100));
1988        cx.executor().run_until_parked();
1989
1990        assert_eq!(
1991            indoc! {r#"
1992⋯1»2»1»
1993
1994fn small_function«1()1» «1{
1995    let x = «2(1, «3(2, 3)3»)2»;
1996}1»
1997
19981 hsla(207.80, 81.00%, 66.00%, 1.00)
19992 hsla(29.00, 54.00%, 61.00%, 1.00)
20003 hsla(286.00, 51.00%, 64.00%, 1.00)
2001"#,},
2002            bracket_colors_markup(&mut cx),
2003        );
2004    }
2005
2006    fn separate_with_comment_lines(head: &str, tail: &str, comment_lines: usize) -> String {
2007        let mut result = head.to_string();
2008        result.push_str("\n");
2009        result.push_str(&"//\n".repeat(comment_lines));
2010        result.push_str(tail);
2011        result
2012    }
2013
2014    fn bracket_colors_markup(cx: &mut EditorTestContext) -> String {
2015        cx.update_editor(|editor, window, cx| {
2016            editor_bracket_colors_markup(&editor.snapshot(window, cx))
2017        })
2018    }
2019
2020    fn editor_bracket_colors_markup(snapshot: &EditorSnapshot) -> String {
2021        fn display_point_to_offset(text: &str, point: DisplayPoint) -> usize {
2022            let mut offset = 0;
2023            for (row_idx, line) in text.lines().enumerate() {
2024                if row_idx < point.row().0 as usize {
2025                    offset += line.len() + 1; // +1 for newline
2026                } else {
2027                    offset += point.column() as usize;
2028                    break;
2029                }
2030            }
2031            offset
2032        }
2033
2034        let actual_ranges = snapshot
2035            .all_text_highlight_ranges(&|key| matches!(key, HighlightKey::ColorizeBracket(_)));
2036        let editor_text = snapshot.text();
2037
2038        let mut next_index = 1;
2039        let mut color_to_index = HashMap::default();
2040        let mut annotations = Vec::new();
2041        for (color, range) in &actual_ranges {
2042            let color_index = *color_to_index
2043                .entry(*color)
2044                .or_insert_with(|| post_inc(&mut next_index));
2045            let start = snapshot.point_to_display_point(range.start, Bias::Left);
2046            let end = snapshot.point_to_display_point(range.end, Bias::Right);
2047            let start_offset = display_point_to_offset(&editor_text, start);
2048            let end_offset = display_point_to_offset(&editor_text, end);
2049            let bracket_text = &editor_text[start_offset..end_offset];
2050            let bracket_char = bracket_text.chars().next().unwrap();
2051
2052            if matches!(bracket_char, '{' | '[' | '(' | '<') {
2053                annotations.push((start_offset, format!("«{color_index}")));
2054            } else {
2055                annotations.push((end_offset, format!("{color_index}»")));
2056            }
2057        }
2058
2059        annotations.sort_by(|(pos_a, text_a), (pos_b, text_b)| {
2060            pos_a.cmp(pos_b).reverse().then_with(|| {
2061                let a_is_opening = text_a.starts_with('«');
2062                let b_is_opening = text_b.starts_with('«');
2063                match (a_is_opening, b_is_opening) {
2064                    (true, false) => cmp::Ordering::Less,
2065                    (false, true) => cmp::Ordering::Greater,
2066                    _ => cmp::Ordering::Equal,
2067                }
2068            })
2069        });
2070        annotations.dedup();
2071
2072        let mut markup = editor_text;
2073        for (offset, text) in annotations {
2074            markup.insert_str(offset, &text);
2075        }
2076
2077        markup.push_str("\n");
2078        for (index, color) in color_to_index
2079            .iter()
2080            .map(|(color, index)| (*index, *color))
2081            .sorted_by_key(|(index, _)| *index)
2082        {
2083            markup.push_str(&format!("{index} {color}\n"));
2084        }
2085
2086        markup
2087    }
2088}
2089
Served at tenant.openagents/omega Member data and write actions are omitted.