From 5e3efc640eba13014ce61c2c4df1d0fe4919a094 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 21 Feb 2026 15:41:23 +0100 Subject: [PATCH] editor: Improve `colorize_bracket` highlight performance (#49803) 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 --- 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 05307b4f445b4219c4243fe45047fc1d153cba77..c4308075a8823819fc871f6c9a36b9dea56d2172 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 e17c63c98037b28b38b11a8721d1673d2f2f9398..99db360e17bf91c50b3bfa61e338c8785f5c1061 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); } } }