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
This commit is contained in:
Lukas Wirth 2026-02-21 15:41:23 +01:00 committed by GitHub
parent 76c878ddc4
commit 5e3efc640e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 27 additions and 22 deletions

View file

@ -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),

View file

@ -1207,26 +1207,26 @@ impl DisplayMap {
pub fn highlight_text(
&mut self,
key: HighlightKey,
ranges: Vec<Range<Anchor>>,
mut ranges: Vec<Range<Anchor>>,
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);
}

View file

@ -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.),

View file

@ -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<Hsla>);
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::<Vec<_>>();
if !colors.is_empty() {
self.0 = colors;
self.0 = Arc::from(colors);
}
}
}