From ca31469c0c98ac1bcf101f727bea8b5a589e8510 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 14:51:02 +0000 Subject: [PATCH] editor: Improve `colorize_bracket` highlight performance (#49803) (cherry-pick to preview) (#49808) Cherry-pick of #49803 to preview ---- The binary search insertion scheme in `highlight_text` works fine for small numbers of elements but does not handle large amounts of ranges well, as that will cause constant memcpying of the latter half of the vec. Bracket colorization tends to have a huge amount of entries though, so this can cause massive lags on the foreground thread. The better approach here is to just collect all elements and re-sort them once. Release Notes: - Reduced mini-stutters occuring due to large amount of bracket colorization in big buffers and agent diffs Co-authored-by: Lukas Wirth --- crates/editor/src/bracket_colorization.rs | 3 ++- crates/editor/src/display_map.rs | 28 +++++++++++------------ crates/theme/src/fallback_themes.rs | 4 +++- crates/theme/src/styles/accents.rs | 14 +++++++----- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/bracket_colorization.rs b/crates/editor/src/bracket_colorization.rs index 3710016b51f4a89a3f99feea5a1699c7c477cc11..dd6189be828621f80a67b0893766e97770b55e43 100644 --- a/crates/editor/src/bracket_colorization.rs +++ b/crates/editor/src/bracket_colorization.rs @@ -129,8 +129,9 @@ impl Editor { } let editor_background = cx.theme().colors().editor_background; + let accents = cx.theme().accents().clone(); for (accent_number, bracket_highlights) in bracket_matches_by_accent { - let bracket_color = cx.theme().accents().color_for_index(accent_number as u32); + let bracket_color = accents.color_for_index(accent_number as u32); let adjusted_color = ensure_minimum_contrast(bracket_color, editor_background, 55.0); let style = HighlightStyle { color: Some(adjusted_color), diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 2131f5c5ccbe6ea73159fdd47fc28d9750d234de..af30936b293c517064a217cc77167d03b9fd0a00 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1207,26 +1207,26 @@ impl DisplayMap { pub fn highlight_text( &mut self, key: HighlightKey, - ranges: Vec>, + mut ranges: Vec>, style: HighlightStyle, merge: bool, cx: &App, ) { let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx); - let to_insert = match self.text_highlights.remove(&key).filter(|_| merge) { - Some(previous) => { - let mut merged_ranges = previous.1.clone(); - for new_range in ranges { - let i = merged_ranges - .binary_search_by(|probe| { - probe.start.cmp(&new_range.start, &multi_buffer_snapshot) - }) - .unwrap_or_else(|i| i); - merged_ranges.insert(i, new_range); + let to_insert = match self.text_highlights.remove(&key) { + Some(mut previous) if merge => match Arc::get_mut(&mut previous) { + Some((_, previous_ranges)) => { + previous_ranges.extend(ranges.iter().cloned()); + previous_ranges.sort_by(|a, b| a.start.cmp(&b.start, &multi_buffer_snapshot)); + previous } - Arc::new((style, merged_ranges)) - } - None => Arc::new((style, ranges)), + None => Arc::new((style, { + ranges.extend(previous.1.iter().cloned()); + ranges.sort_by(|a, b| a.start.cmp(&b.start, &multi_buffer_snapshot)); + ranges + })), + }, + _ => Arc::new((style, ranges)), }; self.text_highlights.insert(key, to_insert); } diff --git a/crates/theme/src/fallback_themes.rs b/crates/theme/src/fallback_themes.rs index c251488391af2d73907b0d4663a0ec5ff4889a33..b04d676774626bf708e2ef58dca6ff6b1b87d2b0 100644 --- a/crates/theme/src/fallback_themes.rs +++ b/crates/theme/src/fallback_themes.rs @@ -109,7 +109,9 @@ pub(crate) fn zed_default_dark() -> Theme { styles: ThemeStyles { window_background_appearance: WindowBackgroundAppearance::Opaque, system: SystemColors::default(), - accents: AccentColors(vec![blue, orange, purple, teal, red, green, yellow]), + accents: AccentColors(Arc::from(vec![ + blue, orange, purple, teal, red, green, yellow, + ])), colors: ThemeColors { border: hsla(225. / 360., 13. / 100., 12. / 100., 1.), border_variant: hsla(228. / 360., 8. / 100., 25. / 100., 1.), diff --git a/crates/theme/src/styles/accents.rs b/crates/theme/src/styles/accents.rs index 49ae755bf811da4f5cbd53eb521ae3b300278c99..7e42ffe2e5bfa6449a64203ffcd5e49720382d06 100644 --- a/crates/theme/src/styles/accents.rs +++ b/crates/theme/src/styles/accents.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use gpui::Hsla; use serde::Deserialize; @@ -8,7 +10,7 @@ use crate::{ /// A collection of colors that are used to color indent aware lines in the editor. #[derive(Clone, Debug, Deserialize, PartialEq)] -pub struct AccentColors(pub Vec); +pub struct AccentColors(pub Arc<[Hsla]>); impl Default for AccentColors { /// Don't use this! @@ -22,7 +24,7 @@ impl Default for AccentColors { impl AccentColors { /// Returns the set of dark accent colors. pub fn dark() -> Self { - Self(vec![ + Self(Arc::from(vec![ blue().dark().step_9(), orange().dark().step_9(), pink().dark().step_9(), @@ -36,12 +38,12 @@ impl AccentColors { grass().dark().step_9(), indigo().dark().step_9(), iris().dark().step_9(), - ]) + ])) } /// Returns the set of light accent colors. pub fn light() -> Self { - Self(vec![ + Self(Arc::from(vec![ blue().light().step_9(), orange().light().step_9(), pink().light().step_9(), @@ -55,7 +57,7 @@ impl AccentColors { grass().light().step_9(), indigo().light().step_9(), iris().light().step_9(), - ]) + ])) } } @@ -82,7 +84,7 @@ impl AccentColors { .collect::>(); if !colors.is_empty() { - self.0 = colors; + self.0 = Arc::from(colors); } } }