bracket_colorization.rs

   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::ops::Range;
   6
   7use crate::Editor;
   8use collections::HashMap;
   9use gpui::{Context, HighlightStyle};
  10use language::language_settings;
  11use multi_buffer::{Anchor, ExcerptId};
  12use ui::{ActiveTheme, utils::ensure_minimum_contrast};
  13
  14struct ColorizedBracketsHighlight;
  15
  16impl Editor {
  17    pub(crate) fn colorize_brackets(&mut self, invalidate: bool, cx: &mut Context<Editor>) {
  18        if !self.mode.is_full() {
  19            return;
  20        }
  21
  22        if invalidate {
  23            self.fetched_tree_sitter_chunks.clear();
  24        }
  25
  26        let accents_count = cx.theme().accents().0.len();
  27        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
  28        let all_excerpts = self.buffer().read(cx).excerpt_ids();
  29        let anchor_in_multi_buffer = |current_excerpt: ExcerptId, text_anchor: text::Anchor| {
  30            multi_buffer_snapshot
  31                .anchor_in_excerpt(current_excerpt, text_anchor)
  32                .or_else(|| {
  33                    all_excerpts
  34                        .iter()
  35                        .filter(|&&excerpt_id| excerpt_id != current_excerpt)
  36                        .find_map(|&excerpt_id| {
  37                            multi_buffer_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)
  38                        })
  39                })
  40        };
  41
  42        let bracket_matches_by_accent = self.visible_excerpts(cx).into_iter().fold(
  43            HashMap::default(),
  44            |mut acc, (excerpt_id, (buffer, buffer_version, buffer_range))| {
  45                let buffer_snapshot = buffer.read(cx).snapshot();
  46                if language_settings::language_settings(
  47                    buffer_snapshot.language().map(|language| language.name()),
  48                    buffer_snapshot.file(),
  49                    cx,
  50                )
  51                .colorize_brackets
  52                {
  53                    let fetched_chunks = self
  54                        .fetched_tree_sitter_chunks
  55                        .entry(excerpt_id)
  56                        .or_default();
  57
  58                    let brackets_by_accent = buffer_snapshot
  59                        .fetch_bracket_ranges(
  60                            buffer_range.start..buffer_range.end,
  61                            Some((&buffer_version, fetched_chunks)),
  62                        )
  63                        .into_iter()
  64                        .flat_map(|(chunk_range, pairs)| {
  65                            if fetched_chunks.insert(chunk_range) {
  66                                pairs
  67                            } else {
  68                                Vec::new()
  69                            }
  70                        })
  71                        .filter_map(|pair| {
  72                            let color_index = pair.color_index?;
  73
  74                            let buffer_open_range = buffer_snapshot
  75                                .anchor_before(pair.open_range.start)
  76                                ..buffer_snapshot.anchor_after(pair.open_range.end);
  77                            let buffer_close_range = buffer_snapshot
  78                                .anchor_before(pair.close_range.start)
  79                                ..buffer_snapshot.anchor_after(pair.close_range.end);
  80                            let multi_buffer_open_range =
  81                                anchor_in_multi_buffer(excerpt_id, buffer_open_range.start)
  82                                    .zip(anchor_in_multi_buffer(excerpt_id, buffer_open_range.end));
  83                            let multi_buffer_close_range =
  84                                anchor_in_multi_buffer(excerpt_id, buffer_close_range.start).zip(
  85                                    anchor_in_multi_buffer(excerpt_id, buffer_close_range.end),
  86                                );
  87
  88                            let mut ranges = Vec::with_capacity(2);
  89                            if let Some((open_start, open_end)) = multi_buffer_open_range {
  90                                ranges.push(open_start..open_end);
  91                            }
  92                            if let Some((close_start, close_end)) = multi_buffer_close_range {
  93                                ranges.push(close_start..close_end);
  94                            }
  95                            if ranges.is_empty() {
  96                                None
  97                            } else {
  98                                Some((color_index % accents_count, ranges))
  99                            }
 100                        });
 101
 102                    for (accent_number, new_ranges) in brackets_by_accent {
 103                        let ranges = acc
 104                            .entry(accent_number)
 105                            .or_insert_with(Vec::<Range<Anchor>>::new);
 106
 107                        for new_range in new_ranges {
 108                            let i = ranges
 109                                .binary_search_by(|probe| {
 110                                    probe.start.cmp(&new_range.start, &multi_buffer_snapshot)
 111                                })
 112                                .unwrap_or_else(|i| i);
 113                            ranges.insert(i, new_range);
 114                        }
 115                    }
 116                }
 117
 118                acc
 119            },
 120        );
 121
 122        if invalidate {
 123            self.clear_highlights::<ColorizedBracketsHighlight>(cx);
 124        }
 125
 126        let editor_background = cx.theme().colors().editor_background;
 127        for (accent_number, bracket_highlights) in bracket_matches_by_accent {
 128            let bracket_color = cx.theme().accents().color_for_index(accent_number as u32);
 129            let adjusted_color = ensure_minimum_contrast(bracket_color, editor_background, 55.0);
 130            let style = HighlightStyle {
 131                color: Some(adjusted_color),
 132                ..HighlightStyle::default()
 133            };
 134
 135            self.highlight_text_key::<ColorizedBracketsHighlight>(
 136                accent_number,
 137                bracket_highlights,
 138                style,
 139                true,
 140                cx,
 141            );
 142        }
 143    }
 144}
 145
 146#[cfg(test)]
 147mod tests {
 148    use std::{cmp, sync::Arc, time::Duration};
 149
 150    use super::*;
 151    use crate::{
 152        DisplayPoint, EditorSnapshot, MoveToBeginning, MoveToEnd, MoveUp,
 153        display_map::{DisplayRow, ToDisplayPoint},
 154        editor_tests::init_test,
 155        test::{
 156            editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
 157        },
 158    };
 159    use collections::HashSet;
 160    use fs::FakeFs;
 161    use gpui::{AppContext as _, UpdateGlobal as _};
 162    use indoc::indoc;
 163    use itertools::Itertools;
 164    use language::{Capability, markdown_lang};
 165    use languages::rust_lang;
 166    use multi_buffer::{ExcerptRange, MultiBuffer};
 167    use pretty_assertions::assert_eq;
 168    use project::Project;
 169    use rope::Point;
 170    use serde_json::json;
 171    use settings::{AccentContent, SettingsStore};
 172    use text::{Bias, OffsetRangeExt, ToOffset};
 173    use theme::ThemeStyleContent;
 174    use ui::SharedString;
 175    use util::{path, post_inc};
 176
 177    #[gpui::test]
 178    async fn test_basic_bracket_colorization(cx: &mut gpui::TestAppContext) {
 179        init_test(cx, |language_settings| {
 180            language_settings.defaults.colorize_brackets = Some(true);
 181        });
 182        let mut cx = EditorLspTestContext::new(
 183            Arc::into_inner(rust_lang()).unwrap(),
 184            lsp::ServerCapabilities::default(),
 185            cx,
 186        )
 187        .await;
 188
 189        cx.set_state(indoc! {r#"ˇuse std::{collections::HashMap, future::Future};
 190
 191fn main() {
 192    let a = one((), { () }, ());
 193    println!("{a}");
 194    println!("{a}");
 195    for i in 0..a {
 196        println!("{i}");
 197    }
 198
 199    let b = {
 200        {
 201            {
 202                [([([([([([([([([([((), ())])])])])])])])])])]
 203            }
 204        }
 205    };
 206}
 207
 208#[rustfmt::skip]
 209fn one(a: (), (): (), c: ()) -> usize { 1 }
 210
 211fn two<T>(a: HashMap<String, Vec<Option<T>>>) -> usize
 212where
 213    T: Future<Output = HashMap<String, Vec<Option<Box<()>>>>>,
 214{
 215    2
 216}
 217"#});
 218        cx.executor().advance_clock(Duration::from_millis(100));
 219        cx.executor().run_until_parked();
 220
 221        assert_eq!(
 222            r#"use std::«1{collections::HashMap, future::Future}1»;
 223
 224fn main«1()1» «1{
 225    let a = one«2(«3()3», «3{ «4()4» }3», «3()3»)2»;
 226    println!«2("{a}")2»;
 227    println!«2("{a}")2»;
 228    for i in 0..a «2{
 229        println!«3("{i}")3»;
 230    }2»
 231
 232    let b = «2{
 233        «3{
 234            «4{
 235                «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»
 236            }4»
 237        }3»
 238    }2»;
 239}1»
 240
 241#«1[rustfmt::skip]1»
 242fn one«1(a: «2()2», «2()2»: «2()2», c: «2()2»)1» -> usize «1{ 1 }1»
 243
 244fn two«1<T>1»«1(a: HashMap«2<String, Vec«3<Option«4<T>4»>3»>2»)1» -> usize
 245where
 246    T: Future«1<Output = HashMap«2<String, Vec«3<Option«4<Box«5<«6()6»>5»>4»>3»>2»>1»,
 247«1{
 248    2
 249}1»
 250
 2511 hsla(207.80, 16.20%, 69.19%, 1.00)
 2522 hsla(29.00, 54.00%, 65.88%, 1.00)
 2533 hsla(286.00, 51.00%, 75.25%, 1.00)
 2544 hsla(187.00, 47.00%, 59.22%, 1.00)
 2555 hsla(355.00, 65.00%, 75.94%, 1.00)
 2566 hsla(95.00, 38.00%, 62.00%, 1.00)
 2577 hsla(39.00, 67.00%, 69.00%, 1.00)
 258"#,
 259            &bracket_colors_markup(&mut cx),
 260            "All brackets should be colored based on their depth"
 261        );
 262    }
 263
 264    #[gpui::test]
 265    async fn test_markdown_bracket_colorization(cx: &mut gpui::TestAppContext) {
 266        init_test(cx, |language_settings| {
 267            language_settings.defaults.colorize_brackets = Some(true);
 268        });
 269        let mut cx = EditorLspTestContext::new(
 270            Arc::into_inner(markdown_lang()).unwrap(),
 271            lsp::ServerCapabilities::default(),
 272            cx,
 273        )
 274        .await;
 275
 276        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)"#});
 277        cx.executor().advance_clock(Duration::from_millis(100));
 278        cx.executor().run_until_parked();
 279
 280        assert_eq!(
 281            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»
 2821 hsla(207.80, 16.20%, 69.19%, 1.00)
 283"#,
 284            &bracket_colors_markup(&mut cx),
 285            "All markdown brackets should be colored based on their depth"
 286        );
 287    }
 288
 289    #[gpui::test]
 290    async fn test_bracket_colorization_when_editing(cx: &mut gpui::TestAppContext) {
 291        init_test(cx, |language_settings| {
 292            language_settings.defaults.colorize_brackets = Some(true);
 293        });
 294        let mut cx = EditorLspTestContext::new(
 295            Arc::into_inner(rust_lang()).unwrap(),
 296            lsp::ServerCapabilities::default(),
 297            cx,
 298        )
 299        .await;
 300
 301        cx.set_state(indoc! {r#"
 302struct Foo<'a, T> {
 303    data: Vec<Option<&'a T>>,
 304}
 305
 306fn process_data() {
 307    let map:ˇ
 308}
 309"#});
 310
 311        cx.update_editor(|editor, window, cx| {
 312            editor.handle_input(" Result<", window, cx);
 313        });
 314        cx.executor().advance_clock(Duration::from_millis(100));
 315        cx.executor().run_until_parked();
 316        assert_eq!(
 317            indoc! {r#"
 318struct Foo«1<'a, T>1» «1{
 319    data: Vec«2<Option«3<&'a T>3»>2»,
 320}1»
 321
 322fn process_data«1()1» «1{
 323    let map: Result<
 324}1»
 325
 3261 hsla(207.80, 16.20%, 69.19%, 1.00)
 3272 hsla(29.00, 54.00%, 65.88%, 1.00)
 3283 hsla(286.00, 51.00%, 75.25%, 1.00)
 329"#},
 330            &bracket_colors_markup(&mut cx),
 331            "Brackets without pairs should be ignored and not colored"
 332        );
 333
 334        cx.update_editor(|editor, window, cx| {
 335            editor.handle_input("Option<Foo<'_, ()", window, cx);
 336        });
 337        cx.executor().advance_clock(Duration::from_millis(100));
 338        cx.executor().run_until_parked();
 339        assert_eq!(
 340            indoc! {r#"
 341struct Foo«1<'a, T>1» «1{
 342    data: Vec«2<Option«3<&'a T>3»>2»,
 343}1»
 344
 345fn process_data«1()1» «1{
 346    let map: Result<Option<Foo<'_, «2()2»
 347}1»
 348
 3491 hsla(207.80, 16.20%, 69.19%, 1.00)
 3502 hsla(29.00, 54.00%, 65.88%, 1.00)
 3513 hsla(286.00, 51.00%, 75.25%, 1.00)
 352"#},
 353            &bracket_colors_markup(&mut cx),
 354        );
 355
 356        cx.update_editor(|editor, window, cx| {
 357            editor.handle_input(">", window, cx);
 358        });
 359        cx.executor().advance_clock(Duration::from_millis(100));
 360        cx.executor().run_until_parked();
 361        assert_eq!(
 362            indoc! {r#"
 363struct Foo«1<'a, T>1» «1{
 364    data: Vec«2<Option«3<&'a T>3»>2»,
 365}1»
 366
 367fn process_data«1()1» «1{
 368    let map: Result<Option<Foo«2<'_, «3()3»>2»
 369}1»
 370
 3711 hsla(207.80, 16.20%, 69.19%, 1.00)
 3722 hsla(29.00, 54.00%, 65.88%, 1.00)
 3733 hsla(286.00, 51.00%, 75.25%, 1.00)
 374"#},
 375            &bracket_colors_markup(&mut cx),
 376            "When brackets start to get closed, inner brackets are re-colored based on their depth"
 377        );
 378
 379        cx.update_editor(|editor, window, cx| {
 380            editor.handle_input(">", window, cx);
 381        });
 382        cx.executor().advance_clock(Duration::from_millis(100));
 383        cx.executor().run_until_parked();
 384        assert_eq!(
 385            indoc! {r#"
 386struct Foo«1<'a, T>1» «1{
 387    data: Vec«2<Option«3<&'a T>3»>2»,
 388}1»
 389
 390fn process_data«1()1» «1{
 391    let map: Result<Option«2<Foo«3<'_, «4()4»>3»>2»
 392}1»
 393
 3941 hsla(207.80, 16.20%, 69.19%, 1.00)
 3952 hsla(29.00, 54.00%, 65.88%, 1.00)
 3963 hsla(286.00, 51.00%, 75.25%, 1.00)
 3974 hsla(187.00, 47.00%, 59.22%, 1.00)
 398"#},
 399            &bracket_colors_markup(&mut cx),
 400        );
 401
 402        cx.update_editor(|editor, window, cx| {
 403            editor.handle_input(", ()> = unimplemented!();", window, cx);
 404        });
 405        cx.executor().advance_clock(Duration::from_millis(100));
 406        cx.executor().run_until_parked();
 407        assert_eq!(
 408            indoc! {r#"
 409struct Foo«1<'a, T>1» «1{
 410    data: Vec«2<Option«3<&'a T>3»>2»,
 411}1»
 412
 413fn process_data«1()1» «1{
 414    let map: Result«2<Option«3<Foo«4<'_, «5()5»>4»>3», «3()3»>2» = unimplemented!«2()2»;
 415}1»
 416
 4171 hsla(207.80, 16.20%, 69.19%, 1.00)
 4182 hsla(29.00, 54.00%, 65.88%, 1.00)
 4193 hsla(286.00, 51.00%, 75.25%, 1.00)
 4204 hsla(187.00, 47.00%, 59.22%, 1.00)
 4215 hsla(355.00, 65.00%, 75.94%, 1.00)
 422"#},
 423            &bracket_colors_markup(&mut cx),
 424        );
 425    }
 426
 427    #[gpui::test]
 428    async fn test_bracket_colorization_chunks(cx: &mut gpui::TestAppContext) {
 429        let comment_lines = 100;
 430
 431        init_test(cx, |language_settings| {
 432            language_settings.defaults.colorize_brackets = Some(true);
 433        });
 434        let mut cx = EditorLspTestContext::new(
 435            Arc::into_inner(rust_lang()).unwrap(),
 436            lsp::ServerCapabilities::default(),
 437            cx,
 438        )
 439        .await;
 440
 441        cx.set_state(&separate_with_comment_lines(
 442            indoc! {r#"
 443mod foo {
 444    ˇfn process_data_1() {
 445        let map: Option<Vec<()>> = None;
 446    }
 447"#},
 448            indoc! {r#"
 449    fn process_data_2() {
 450        let map: Option<Vec<()>> = None;
 451    }
 452}
 453"#},
 454            comment_lines,
 455        ));
 456
 457        cx.executor().advance_clock(Duration::from_millis(100));
 458        cx.executor().run_until_parked();
 459        assert_eq!(
 460            &separate_with_comment_lines(
 461                indoc! {r#"
 462mod foo «1{
 463    fn process_data_1«2()2» «2{
 464        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 465    }2»
 466"#},
 467                indoc! {r#"
 468    fn process_data_2() {
 469        let map: Option<Vec<()>> = None;
 470    }
 471}1»
 472
 4731 hsla(207.80, 16.20%, 69.19%, 1.00)
 4742 hsla(29.00, 54.00%, 65.88%, 1.00)
 4753 hsla(286.00, 51.00%, 75.25%, 1.00)
 4764 hsla(187.00, 47.00%, 59.22%, 1.00)
 4775 hsla(355.00, 65.00%, 75.94%, 1.00)
 478"#},
 479                comment_lines,
 480            ),
 481            &bracket_colors_markup(&mut cx),
 482            "First, the only visible chunk is getting the bracket highlights"
 483        );
 484
 485        cx.update_editor(|editor, window, cx| {
 486            editor.move_to_end(&MoveToEnd, window, cx);
 487            editor.move_up(&MoveUp, window, cx);
 488        });
 489        cx.executor().advance_clock(Duration::from_millis(100));
 490        cx.executor().run_until_parked();
 491        assert_eq!(
 492            &separate_with_comment_lines(
 493                indoc! {r#"
 494mod foo «1{
 495    fn process_data_1«2()2» «2{
 496        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 497    }2»
 498"#},
 499                indoc! {r#"
 500    fn process_data_2«2()2» «2{
 501        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 502    }2»
 503}1»
 504
 5051 hsla(207.80, 16.20%, 69.19%, 1.00)
 5062 hsla(29.00, 54.00%, 65.88%, 1.00)
 5073 hsla(286.00, 51.00%, 75.25%, 1.00)
 5084 hsla(187.00, 47.00%, 59.22%, 1.00)
 5095 hsla(355.00, 65.00%, 75.94%, 1.00)
 510"#},
 511                comment_lines,
 512            ),
 513            &bracket_colors_markup(&mut cx),
 514            "After scrolling to the bottom, both chunks should have the highlights"
 515        );
 516
 517        cx.update_editor(|editor, window, cx| {
 518            editor.handle_input("{{}}}", window, cx);
 519        });
 520        cx.executor().advance_clock(Duration::from_millis(100));
 521        cx.executor().run_until_parked();
 522        assert_eq!(
 523            &separate_with_comment_lines(
 524                indoc! {r#"
 525mod foo «1{
 526    fn process_data_1() {
 527        let map: Option<Vec<()>> = None;
 528    }
 529"#},
 530                indoc! {r#"
 531    fn process_data_2«2()2» «2{
 532        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 533    }
 534    «3{«4{}4»}3»}2»}1»
 535
 5361 hsla(207.80, 16.20%, 69.19%, 1.00)
 5372 hsla(29.00, 54.00%, 65.88%, 1.00)
 5383 hsla(286.00, 51.00%, 75.25%, 1.00)
 5394 hsla(187.00, 47.00%, 59.22%, 1.00)
 5405 hsla(355.00, 65.00%, 75.94%, 1.00)
 541"#},
 542                comment_lines,
 543            ),
 544            &bracket_colors_markup(&mut cx),
 545            "First chunk's brackets are invalidated after an edit, and only 2nd (visible) chunk is re-colorized"
 546        );
 547
 548        cx.update_editor(|editor, window, cx| {
 549            editor.move_to_beginning(&MoveToBeginning, window, cx);
 550        });
 551        cx.executor().advance_clock(Duration::from_millis(100));
 552        cx.executor().run_until_parked();
 553        assert_eq!(
 554            &separate_with_comment_lines(
 555                indoc! {r#"
 556mod foo «1{
 557    fn process_data_1«2()2» «2{
 558        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 559    }2»
 560"#},
 561                indoc! {r#"
 562    fn process_data_2«2()2» «2{
 563        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 564    }
 565    «3{«4{}4»}3»}2»}1»
 566
 5671 hsla(207.80, 16.20%, 69.19%, 1.00)
 5682 hsla(29.00, 54.00%, 65.88%, 1.00)
 5693 hsla(286.00, 51.00%, 75.25%, 1.00)
 5704 hsla(187.00, 47.00%, 59.22%, 1.00)
 5715 hsla(355.00, 65.00%, 75.94%, 1.00)
 572"#},
 573                comment_lines,
 574            ),
 575            &bracket_colors_markup(&mut cx),
 576            "Scrolling back to top should re-colorize all chunks' brackets"
 577        );
 578
 579        cx.update(|_, cx| {
 580            SettingsStore::update_global(cx, |store, cx| {
 581                store.update_user_settings(cx, |settings| {
 582                    settings.project.all_languages.defaults.colorize_brackets = Some(false);
 583                });
 584            });
 585        });
 586        assert_eq!(
 587            &separate_with_comment_lines(
 588                indoc! {r#"
 589mod foo {
 590    fn process_data_1() {
 591        let map: Option<Vec<()>> = None;
 592    }
 593"#},
 594                r#"    fn process_data_2() {
 595        let map: Option<Vec<()>> = None;
 596    }
 597    {{}}}}
 598
 599"#,
 600                comment_lines,
 601            ),
 602            &bracket_colors_markup(&mut cx),
 603            "Turning bracket colorization off should remove all bracket colors"
 604        );
 605
 606        cx.update(|_, cx| {
 607            SettingsStore::update_global(cx, |store, cx| {
 608                store.update_user_settings(cx, |settings| {
 609                    settings.project.all_languages.defaults.colorize_brackets = Some(true);
 610                });
 611            });
 612        });
 613        assert_eq!(
 614            &separate_with_comment_lines(
 615                indoc! {r#"
 616mod foo «1{
 617    fn process_data_1«2()2» «2{
 618        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
 619    }2»
 620"#},
 621                r#"    fn process_data_2() {
 622        let map: Option<Vec<()>> = None;
 623    }
 624    {{}}}}1»
 625
 6261 hsla(207.80, 16.20%, 69.19%, 1.00)
 6272 hsla(29.00, 54.00%, 65.88%, 1.00)
 6283 hsla(286.00, 51.00%, 75.25%, 1.00)
 6294 hsla(187.00, 47.00%, 59.22%, 1.00)
 6305 hsla(355.00, 65.00%, 75.94%, 1.00)
 631"#,
 632                comment_lines,
 633            ),
 634            &bracket_colors_markup(&mut cx),
 635            "Turning bracket colorization back on refreshes the visible excerpts' bracket colors"
 636        );
 637    }
 638
 639    #[gpui::test]
 640    async fn test_rainbow_bracket_highlights(cx: &mut gpui::TestAppContext) {
 641        init_test(cx, |language_settings| {
 642            language_settings.defaults.colorize_brackets = Some(true);
 643        });
 644        let mut cx = EditorLspTestContext::new(
 645            Arc::into_inner(rust_lang()).unwrap(),
 646            lsp::ServerCapabilities::default(),
 647            cx,
 648        )
 649        .await;
 650
 651        // taken from r-a https://github.com/rust-lang/rust-analyzer/blob/d733c07552a2dc0ec0cc8f4df3f0ca969a93fd90/crates/ide/src/inlay_hints.rs#L81-L297
 652        cx.set_state(indoc! {r# 653            pub(crate) fn inlay_hints(
 654                db: &RootDatabase,
 655                file_id: FileId,
 656                range_limit: Option<TextRange>,
 657                config: &InlayHintsConfig,
 658            ) -> Vec<InlayHint> {
 659                let _p = tracing::info_span!("inlay_hints").entered();
 660                let sema = Semantics::new(db);
 661                let file_id = sema
 662                    .attach_first_edition(file_id)
 663                    .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
 664                let file = sema.parse(file_id);
 665                let file = file.syntax();
 666
 667                let mut acc = Vec::new();
 668
 669                let Some(scope) = sema.scope(file) else {
 670                    return acc;
 671                };
 672                let famous_defs = FamousDefs(&sema, scope.krate());
 673                let display_target = famous_defs.1.to_display_target(sema.db);
 674
 675                let ctx = &mut InlayHintCtx::default();
 676                let mut hints = |event| {
 677                    if let Some(node) = handle_event(ctx, event) {
 678                        hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
 679                    }
 680                };
 681                let mut preorder = file.preorder();
 682                salsa::attach(sema.db, || {
 683                    while let Some(event) = preorder.next() {
 684                        if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none())
 685                        {
 686                            preorder.skip_subtree();
 687                            continue;
 688                        }
 689                        hints(event);
 690                    }
 691                });
 692                if let Some(range_limit) = range_limit {
 693                    acc.retain(|hint| range_limit.contains_range(hint.range));
 694                }
 695                acc
 696            }
 697
 698            #[derive(Default)]
 699            struct InlayHintCtx {
 700                lifetime_stacks: Vec<Vec<SmolStr>>,
 701                extern_block_parent: Option<ast::ExternBlock>,
 702            }
 703
 704            pub(crate) fn inlay_hints_resolve(
 705                db: &RootDatabase,
 706                file_id: FileId,
 707                resolve_range: TextRange,
 708                hash: u64,
 709                config: &InlayHintsConfig,
 710                hasher: impl Fn(&InlayHint) -> u64,
 711            ) -> Option<InlayHint> {
 712                let _p = tracing::info_span!("inlay_hints_resolve").entered();
 713                let sema = Semantics::new(db);
 714                let file_id = sema
 715                    .attach_first_edition(file_id)
 716                    .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
 717                let file = sema.parse(file_id);
 718                let file = file.syntax();
 719
 720                let scope = sema.scope(file)?;
 721                let famous_defs = FamousDefs(&sema, scope.krate());
 722                let mut acc = Vec::new();
 723
 724                let display_target = famous_defs.1.to_display_target(sema.db);
 725
 726                let ctx = &mut InlayHintCtx::default();
 727                let mut hints = |event| {
 728                    if let Some(node) = handle_event(ctx, event) {
 729                        hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
 730                    }
 731                };
 732
 733                let mut preorder = file.preorder();
 734                while let Some(event) = preorder.next() {
 735                    // This can miss some hints that require the parent of the range to calculate
 736                    if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none())
 737                    {
 738                        preorder.skip_subtree();
 739                        continue;
 740                    }
 741                    hints(event);
 742                }
 743                acc.into_iter().find(|hint| hasher(hint) == hash)
 744            }
 745
 746            fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<SyntaxNode> {
 747                match node {
 748                    WalkEvent::Enter(node) => {
 749                        if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
 750                            let params = node
 751                                .generic_param_list()
 752                                .map(|it| {
 753                                    it.lifetime_params()
 754                                        .filter_map(|it| {
 755                                            it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..]))
 756                                        })
 757                                        .collect()
 758                                })
 759                                .unwrap_or_default();
 760                            ctx.lifetime_stacks.push(params);
 761                        }
 762                        if let Some(node) = ast::ExternBlock::cast(node.clone()) {
 763                            ctx.extern_block_parent = Some(node);
 764                        }
 765                        Some(node)
 766                    }
 767                    WalkEvent::Leave(n) => {
 768                        if ast::AnyHasGenericParams::can_cast(n.kind()) {
 769                            ctx.lifetime_stacks.pop();
 770                        }
 771                        if ast::ExternBlock::can_cast(n.kind()) {
 772                            ctx.extern_block_parent = None;
 773                        }
 774                        None
 775                    }
 776                }
 777            }
 778
 779            // At some point when our hir infra is fleshed out enough we should flip this and traverse the
 780            // HIR instead of the syntax tree.
 781            fn hints(
 782                hints: &mut Vec<InlayHint>,
 783                ctx: &mut InlayHintCtx,
 784                famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>,
 785                config: &InlayHintsConfig,
 786                file_id: EditionedFileId,
 787                display_target: DisplayTarget,
 788                node: SyntaxNode,
 789            ) {
 790                closing_brace::hints(
 791                    hints,
 792                    sema,
 793                    config,
 794                    display_target,
 795                    InRealFile { file_id, value: node.clone() },
 796                );
 797                if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
 798                    generic_param::hints(hints, famous_defs, config, any_has_generic_args);
 799                }
 800
 801                match_ast! {
 802                    match node {
 803                        ast::Expr(expr) => {
 804                            chaining::hints(hints, famous_defs, config, display_target, &expr);
 805                            adjustment::hints(hints, famous_defs, config, display_target, &expr);
 806                            match expr {
 807                                ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
 808                                ast::Expr::MethodCallExpr(it) => {
 809                                    param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
 810                                }
 811                                ast::Expr::ClosureExpr(it) => {
 812                                    closure_captures::hints(hints, famous_defs, config, it.clone());
 813                                    closure_ret::hints(hints, famous_defs, config, display_target, it)
 814                                },
 815                                ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
 816                                _ => Some(()),
 817                            }
 818                        },
 819                        ast::Pat(it) => {
 820                            binding_mode::hints(hints, famous_defs, config, &it);
 821                            match it {
 822                                ast::Pat::IdentPat(it) => {
 823                                    bind_pat::hints(hints, famous_defs, config, display_target, &it);
 824                                }
 825                                ast::Pat::RangePat(it) => {
 826                                    range_exclusive::hints(hints, famous_defs, config, it);
 827                                }
 828                                _ => {}
 829                            }
 830                            Some(())
 831                        },
 832                        ast::Item(it) => match it {
 833                            ast::Item::Fn(it) => {
 834                                implicit_drop::hints(hints, famous_defs, config, display_target, &it);
 835                                if let Some(extern_block) = &ctx.extern_block_parent {
 836                                    extern_block::fn_hints(hints, famous_defs, config, &it, extern_block);
 837                                }
 838                                lifetime::fn_hints(hints, ctx, famous_defs, config,  it)
 839                            },
 840                            ast::Item::Static(it) => {
 841                                if let Some(extern_block) = &ctx.extern_block_parent {
 842                                    extern_block::static_hints(hints, famous_defs, config, &it, extern_block);
 843                                }
 844                                implicit_static::hints(hints, famous_defs, config,  Either::Left(it))
 845                            },
 846                            ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, Either::Right(it)),
 847                            ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, it),
 848                            ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, it),
 849                            _ => None,
 850                        },
 851                        // trait object type elisions
 852                        ast::Type(ty) => match ty {
 853                            ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config,  ptr),
 854                            ast::Type::PathType(path) => {
 855                                lifetime::fn_path_hints(hints, ctx, famous_defs, config, &path);
 856                                implied_dyn_trait::hints(hints, famous_defs, config, Either::Left(path));
 857                                Some(())
 858                            },
 859                            ast::Type::DynTraitType(dyn_) => {
 860                                implied_dyn_trait::hints(hints, famous_defs, config, Either::Right(dyn_));
 861                                Some(())
 862                            },
 863                            _ => Some(()),
 864                        },
 865                        ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config,  it),
 866                        _ => Some(()),
 867                    }
 868                };
 869            }
 870        "#});
 871        cx.executor().advance_clock(Duration::from_millis(100));
 872        cx.executor().run_until_parked();
 873
 874        let actual_ranges = cx.update_editor(|editor, window, cx| {
 875            editor
 876                .snapshot(window, cx)
 877                .all_text_highlight_ranges::<ColorizedBracketsHighlight>()
 878        });
 879
 880        let mut highlighted_brackets = HashMap::default();
 881        for (color, range) in actual_ranges.iter().cloned() {
 882            highlighted_brackets.insert(range, color);
 883        }
 884
 885        let last_bracket = actual_ranges
 886            .iter()
 887            .max_by_key(|(_, p)| p.end.row)
 888            .unwrap()
 889            .clone();
 890
 891        cx.update_editor(|editor, window, cx| {
 892            let was_scrolled = editor.set_scroll_position(
 893                gpui::Point::new(0.0, last_bracket.1.end.row as f64 * 2.0),
 894                window,
 895                cx,
 896            );
 897            assert!(was_scrolled.0);
 898        });
 899        cx.executor().advance_clock(Duration::from_millis(100));
 900        cx.executor().run_until_parked();
 901
 902        let ranges_after_scrolling = cx.update_editor(|editor, window, cx| {
 903            editor
 904                .snapshot(window, cx)
 905                .all_text_highlight_ranges::<ColorizedBracketsHighlight>()
 906        });
 907        let new_last_bracket = ranges_after_scrolling
 908            .iter()
 909            .max_by_key(|(_, p)| p.end.row)
 910            .unwrap()
 911            .clone();
 912
 913        assert_ne!(
 914            last_bracket, new_last_bracket,
 915            "After scrolling down, we should have highlighted more brackets"
 916        );
 917
 918        cx.update_editor(|editor, window, cx| {
 919            let was_scrolled = editor.set_scroll_position(gpui::Point::default(), window, cx);
 920            assert!(was_scrolled.0);
 921        });
 922
 923        for _ in 0..200 {
 924            cx.update_editor(|editor, window, cx| {
 925                editor.apply_scroll_delta(gpui::Point::new(0.0, 0.25), window, cx);
 926            });
 927            cx.executor().advance_clock(Duration::from_millis(100));
 928            cx.executor().run_until_parked();
 929
 930            let colored_brackets = cx.update_editor(|editor, window, cx| {
 931                editor
 932                    .snapshot(window, cx)
 933                    .all_text_highlight_ranges::<ColorizedBracketsHighlight>()
 934            });
 935            for (color, range) in colored_brackets.clone() {
 936                assert!(
 937                    highlighted_brackets.entry(range).or_insert(color) == &color,
 938                    "Colors should stay consistent while scrolling!"
 939                );
 940            }
 941
 942            let snapshot = cx.update_editor(|editor, window, cx| editor.snapshot(window, cx));
 943            let scroll_position = snapshot.scroll_position();
 944            let visible_lines =
 945                cx.update_editor(|editor, _, _| editor.visible_line_count().unwrap());
 946            let visible_range = DisplayRow(scroll_position.y as u32)
 947                ..DisplayRow((scroll_position.y + visible_lines) as u32);
 948
 949            let current_highlighted_bracket_set: HashSet<Point> = HashSet::from_iter(
 950                colored_brackets
 951                    .iter()
 952                    .flat_map(|(_, range)| [range.start, range.end]),
 953            );
 954
 955            for highlight_range in highlighted_brackets.keys().filter(|bracket_range| {
 956                visible_range.contains(&bracket_range.start.to_display_point(&snapshot).row())
 957                    || visible_range.contains(&bracket_range.end.to_display_point(&snapshot).row())
 958            }) {
 959                assert!(
 960                    current_highlighted_bracket_set.contains(&highlight_range.start)
 961                        || current_highlighted_bracket_set.contains(&highlight_range.end),
 962                    "Should not lose highlights while scrolling in the visible range!"
 963                );
 964            }
 965
 966            let buffer_snapshot = snapshot.buffer().as_singleton().unwrap().2;
 967            for bracket_match in buffer_snapshot
 968                .fetch_bracket_ranges(
 969                    snapshot
 970                        .display_point_to_point(
 971                            DisplayPoint::new(visible_range.start, 0),
 972                            Bias::Left,
 973                        )
 974                        .to_offset(&buffer_snapshot)
 975                        ..snapshot
 976                            .display_point_to_point(
 977                                DisplayPoint::new(
 978                                    visible_range.end,
 979                                    snapshot.line_len(visible_range.end),
 980                                ),
 981                                Bias::Right,
 982                            )
 983                            .to_offset(&buffer_snapshot),
 984                    None,
 985                )
 986                .iter()
 987                .flat_map(|entry| entry.1)
 988                .filter(|bracket_match| bracket_match.color_index.is_some())
 989            {
 990                let start = bracket_match.open_range.to_point(buffer_snapshot);
 991                let end = bracket_match.close_range.to_point(buffer_snapshot);
 992                let start_bracket = colored_brackets.iter().find(|(_, range)| *range == start);
 993                assert!(
 994                    start_bracket.is_some(),
 995                    "Existing bracket start in the visible range should be highlighted. Missing color for match: \"{}\" at position {:?}",
 996                    buffer_snapshot
 997                        .text_for_range(start.start..end.end)
 998                        .collect::<String>(),
 999                    start
1000                );
1001
1002                let end_bracket = colored_brackets.iter().find(|(_, range)| *range == end);
1003                assert!(
1004                    end_bracket.is_some(),
1005                    "Existing bracket end in the visible range should be highlighted. Missing color for match: \"{}\" at position {:?}",
1006                    buffer_snapshot
1007                        .text_for_range(start.start..end.end)
1008                        .collect::<String>(),
1009                    start
1010                );
1011
1012                assert_eq!(
1013                    start_bracket.unwrap().0,
1014                    end_bracket.unwrap().0,
1015                    "Bracket pair should be highlighted the same color!"
1016                )
1017            }
1018        }
1019    }
1020
1021    #[gpui::test]
1022    async fn test_multi_buffer(cx: &mut gpui::TestAppContext) {
1023        let comment_lines = 100;
1024
1025        init_test(cx, |language_settings| {
1026            language_settings.defaults.colorize_brackets = Some(true);
1027        });
1028        let fs = FakeFs::new(cx.background_executor.clone());
1029        fs.insert_tree(
1030            path!("/a"),
1031            json!({
1032                "main.rs": "fn main() {{()}}",
1033                "lib.rs": separate_with_comment_lines(
1034                    indoc! {r#"
1035    mod foo {
1036        fn process_data_1() {
1037            let map: Option<Vec<()>> = None;
1038            // a
1039            // b
1040            // c
1041        }
1042    "#},
1043                    indoc! {r#"
1044        fn process_data_2() {
1045            let other_map: Option<Vec<()>> = None;
1046        }
1047    }
1048    "#},
1049                    comment_lines,
1050                )
1051            }),
1052        )
1053        .await;
1054
1055        let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
1056        let language_registry = project.read_with(cx, |project, _| project.languages().clone());
1057        language_registry.add(rust_lang());
1058
1059        let buffer_1 = project
1060            .update(cx, |project, cx| {
1061                project.open_local_buffer(path!("/a/lib.rs"), cx)
1062            })
1063            .await
1064            .unwrap();
1065        let buffer_2 = project
1066            .update(cx, |project, cx| {
1067                project.open_local_buffer(path!("/a/main.rs"), cx)
1068            })
1069            .await
1070            .unwrap();
1071
1072        let multi_buffer = cx.new(|cx| {
1073            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
1074            multi_buffer.push_excerpts(
1075                buffer_2.clone(),
1076                [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
1077                cx,
1078            );
1079
1080            let excerpt_rows = 5;
1081            let rest_of_first_except_rows = 3;
1082            multi_buffer.push_excerpts(
1083                buffer_1.clone(),
1084                [
1085                    ExcerptRange::new(Point::new(0, 0)..Point::new(excerpt_rows, 0)),
1086                    ExcerptRange::new(
1087                        Point::new(
1088                            comment_lines as u32 + excerpt_rows + rest_of_first_except_rows,
1089                            0,
1090                        )
1091                            ..Point::new(
1092                                comment_lines as u32
1093                                    + excerpt_rows
1094                                    + rest_of_first_except_rows
1095                                    + excerpt_rows,
1096                                0,
1097                            ),
1098                    ),
1099                ],
1100                cx,
1101            );
1102            multi_buffer
1103        });
1104
1105        let editor = cx.add_window(|window, cx| {
1106            Editor::for_multibuffer(multi_buffer, Some(project.clone()), window, cx)
1107        });
1108        cx.executor().advance_clock(Duration::from_millis(100));
1109        cx.executor().run_until_parked();
1110
1111        let editor_snapshot = editor
1112            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1113            .unwrap();
1114        assert_eq!(
1115            indoc! {r#"
1116
1117
1118fn main«1()1» «1{«2{«3()3»}2»}1»
1119
1120
1121mod foo «1{
1122    fn process_data_1«2()2» «2{
1123        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1124        // a
1125        // b
1126
1127
1128    fn process_data_2«2()2» «2{
1129        let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
1130    }2»
1131}1»
1132
11331 hsla(207.80, 16.20%, 69.19%, 1.00)
11342 hsla(29.00, 54.00%, 65.88%, 1.00)
11353 hsla(286.00, 51.00%, 75.25%, 1.00)
11364 hsla(187.00, 47.00%, 59.22%, 1.00)
11375 hsla(355.00, 65.00%, 75.94%, 1.00)
1138"#,},
1139            &editor_bracket_colors_markup(&editor_snapshot),
1140            "Multi buffers should have their brackets colored even if no excerpts contain the bracket counterpart (after fn `process_data_2()`) \
1141or if the buffer pair spans across multiple excerpts (the one after `mod foo`)"
1142        );
1143
1144        editor
1145            .update(cx, |editor, window, cx| {
1146                editor.handle_input("{[]", window, cx);
1147            })
1148            .unwrap();
1149        cx.executor().advance_clock(Duration::from_millis(100));
1150        cx.executor().run_until_parked();
1151        let editor_snapshot = editor
1152            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1153            .unwrap();
1154        assert_eq!(
1155            indoc! {r#"
1156
1157
1158{«1[]1»fn main«1()1» «1{«2{«3()3»}2»}1»
1159
1160
1161mod foo «1{
1162    fn process_data_1«2()2» «2{
1163        let map: Option«3<Vec«4<«5()5»>4»>3» = None;
1164        // a
1165        // b
1166
1167
1168    fn process_data_2«2()2» «2{
1169        let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
1170    }2»
1171}1»
1172
11731 hsla(207.80, 16.20%, 69.19%, 1.00)
11742 hsla(29.00, 54.00%, 65.88%, 1.00)
11753 hsla(286.00, 51.00%, 75.25%, 1.00)
11764 hsla(187.00, 47.00%, 59.22%, 1.00)
11775 hsla(355.00, 65.00%, 75.94%, 1.00)
1178"#,},
1179            &editor_bracket_colors_markup(&editor_snapshot),
1180        );
1181
1182        cx.update(|cx| {
1183            let theme = cx.theme().name.clone();
1184            SettingsStore::update_global(cx, |store, cx| {
1185                store.update_user_settings(cx, |settings| {
1186                    settings.theme.theme_overrides = HashMap::from_iter([(
1187                        theme.to_string(),
1188                        ThemeStyleContent {
1189                            accents: vec![
1190                                AccentContent(Some(SharedString::new("#ff0000"))),
1191                                AccentContent(Some(SharedString::new("#0000ff"))),
1192                            ],
1193                            ..ThemeStyleContent::default()
1194                        },
1195                    )]);
1196                });
1197            });
1198        });
1199        cx.executor().advance_clock(Duration::from_millis(100));
1200        cx.executor().run_until_parked();
1201        let editor_snapshot = editor
1202            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
1203            .unwrap();
1204        assert_eq!(
1205            indoc! {r#"
1206
1207
1208{«1[]1»fn main«1()1» «1{«2{«1()1»}2»}1»
1209
1210
1211mod foo «1{
1212    fn process_data_1«2()2» «2{
1213        let map: Option«1<Vec«2<«1()1»>2»>1» = None;
1214        // a
1215        // b
1216
1217
1218    fn process_data_2«2()2» «2{
1219        let other_map: Option«1<Vec«2<«1()1»>2»>1» = None;
1220    }2»
1221}1»
1222
12231 hsla(0.00, 100.00%, 78.12%, 1.00)
12242 hsla(240.00, 100.00%, 82.81%, 1.00)
1225"#,},
1226            &editor_bracket_colors_markup(&editor_snapshot),
1227            "After updating theme accents, the editor should update the bracket coloring"
1228        );
1229    }
1230
1231    fn separate_with_comment_lines(head: &str, tail: &str, comment_lines: usize) -> String {
1232        let mut result = head.to_string();
1233        result.push_str("\n");
1234        result.push_str(&"//\n".repeat(comment_lines));
1235        result.push_str(tail);
1236        result
1237    }
1238
1239    fn bracket_colors_markup(cx: &mut EditorTestContext) -> String {
1240        cx.update_editor(|editor, window, cx| {
1241            editor_bracket_colors_markup(&editor.snapshot(window, cx))
1242        })
1243    }
1244
1245    fn editor_bracket_colors_markup(snapshot: &EditorSnapshot) -> String {
1246        fn display_point_to_offset(text: &str, point: DisplayPoint) -> usize {
1247            let mut offset = 0;
1248            for (row_idx, line) in text.lines().enumerate() {
1249                if row_idx < point.row().0 as usize {
1250                    offset += line.len() + 1; // +1 for newline
1251                } else {
1252                    offset += point.column() as usize;
1253                    break;
1254                }
1255            }
1256            offset
1257        }
1258
1259        let actual_ranges = snapshot.all_text_highlight_ranges::<ColorizedBracketsHighlight>();
1260        let editor_text = snapshot.text();
1261
1262        let mut next_index = 1;
1263        let mut color_to_index = HashMap::default();
1264        let mut annotations = Vec::new();
1265        for (color, range) in &actual_ranges {
1266            let color_index = *color_to_index
1267                .entry(*color)
1268                .or_insert_with(|| post_inc(&mut next_index));
1269            let start = snapshot.point_to_display_point(range.start, Bias::Left);
1270            let end = snapshot.point_to_display_point(range.end, Bias::Right);
1271            let start_offset = display_point_to_offset(&editor_text, start);
1272            let end_offset = display_point_to_offset(&editor_text, end);
1273            let bracket_text = &editor_text[start_offset..end_offset];
1274            let bracket_char = bracket_text.chars().next().unwrap();
1275
1276            if matches!(bracket_char, '{' | '[' | '(' | '<') {
1277                annotations.push((start_offset, format!("«{color_index}")));
1278            } else {
1279                annotations.push((end_offset, format!("{color_index}»")));
1280            }
1281        }
1282
1283        annotations.sort_by(|(pos_a, text_a), (pos_b, text_b)| {
1284            pos_a.cmp(pos_b).reverse().then_with(|| {
1285                let a_is_opening = text_a.starts_with('«');
1286                let b_is_opening = text_b.starts_with('«');
1287                match (a_is_opening, b_is_opening) {
1288                    (true, false) => cmp::Ordering::Less,
1289                    (false, true) => cmp::Ordering::Greater,
1290                    _ => cmp::Ordering::Equal,
1291                }
1292            })
1293        });
1294        annotations.dedup();
1295
1296        let mut markup = editor_text;
1297        for (offset, text) in annotations {
1298            markup.insert_str(offset, &text);
1299        }
1300
1301        markup.push_str("\n");
1302        for (index, color) in color_to_index
1303            .iter()
1304            .map(|(color, index)| (*index, *color))
1305            .sorted_by_key(|(index, _)| *index)
1306        {
1307            markup.push_str(&format!("{index} {color}\n"));
1308        }
1309
1310        markup
1311    }
1312}