From b89bcbead6b95669863a1e571c23763d8769381b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 28 Nov 2025 10:16:59 +0100 Subject: [PATCH] editor: Speed up `colorize_brackets` slightly in large multibuffers (#43713) There is still room for improvement here as `anchor(s)_in_excerpt` is generally a bad API here due to it reseeking the entire excerpt tree from the start on every call which we don't really need. But this at least cuts the seeks down by a factor of 4 for now. Release Notes: - Improved performance of bracket colorization in large multibuffers --- crates/editor/src/bracket_colorization.rs | 33 ++++++++++++++++------- crates/multi_buffer/src/multi_buffer.rs | 22 ++++++++++++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/crates/editor/src/bracket_colorization.rs b/crates/editor/src/bracket_colorization.rs index 65d8c139e99437e37d0c18551dd01475ac824bfd..5631d7f5adc0cefb5adf840fa9f62113179dff2f 100644 --- a/crates/editor/src/bracket_colorization.rs +++ b/crates/editor/src/bracket_colorization.rs @@ -7,6 +7,7 @@ use std::ops::Range; use crate::Editor; use collections::HashMap; use gpui::{Context, HighlightStyle}; +use itertools::Itertools; use language::language_settings; use multi_buffer::{Anchor, ExcerptId}; use ui::{ActiveTheme, utils::ensure_minimum_contrast}; @@ -26,17 +27,20 @@ impl Editor { let accents_count = cx.theme().accents().0.len(); let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx); let all_excerpts = self.buffer().read(cx).excerpt_ids(); - let anchor_in_multi_buffer = |current_excerpt: ExcerptId, text_anchor: text::Anchor| { + let anchors_in_multi_buffer = |current_excerpt: ExcerptId, + text_anchors: [text::Anchor; 4]| + -> Option<[Option<_>; 4]> { multi_buffer_snapshot - .anchor_in_excerpt(current_excerpt, text_anchor) + .anchors_in_excerpt(current_excerpt, text_anchors) .or_else(|| { all_excerpts .iter() .filter(|&&excerpt_id| excerpt_id != current_excerpt) .find_map(|&excerpt_id| { - multi_buffer_snapshot.anchor_in_excerpt(excerpt_id, text_anchor) + multi_buffer_snapshot.anchors_in_excerpt(excerpt_id, text_anchors) }) - }) + })? + .collect_array() }; let bracket_matches_by_accent = self.visible_excerpts(cx).into_iter().fold( @@ -77,13 +81,24 @@ impl Editor { let buffer_close_range = buffer_snapshot .anchor_before(pair.close_range.start) ..buffer_snapshot.anchor_after(pair.close_range.end); + let [ + buffer_open_range_start, + buffer_open_range_end, + buffer_close_range_start, + buffer_close_range_end, + ] = anchors_in_multi_buffer( + excerpt_id, + [ + buffer_open_range.start, + buffer_open_range.end, + buffer_close_range.start, + buffer_close_range.end, + ], + )?; let multi_buffer_open_range = - anchor_in_multi_buffer(excerpt_id, buffer_open_range.start) - .zip(anchor_in_multi_buffer(excerpt_id, buffer_open_range.end)); + buffer_open_range_start.zip(buffer_open_range_end); let multi_buffer_close_range = - anchor_in_multi_buffer(excerpt_id, buffer_close_range.start).zip( - anchor_in_multi_buffer(excerpt_id, buffer_close_range.end), - ); + buffer_close_range_start.zip(buffer_close_range_end); let mut ranges = Vec::with_capacity(2); if let Some((open_start, open_end)) = multi_buffer_open_range { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 2e59eaa621c79bc8d0d0a149704cb55314e9b70d..3e9d113a300fad6f8c29221d0f886497793fafc9 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -5084,8 +5084,8 @@ impl MultiBufferSnapshot { let excerpt = self.excerpt(self.latest_excerpt_id(excerpt_id))?; Some( - self.anchor_in_excerpt_(excerpt, text_anchor.start)? - ..self.anchor_in_excerpt_(excerpt, text_anchor.end)?, + Self::anchor_in_excerpt_(excerpt, text_anchor.start)? + ..Self::anchor_in_excerpt_(excerpt, text_anchor.end)?, ) } @@ -5097,10 +5097,24 @@ impl MultiBufferSnapshot { text_anchor: text::Anchor, ) -> Option { let excerpt = self.excerpt(self.latest_excerpt_id(excerpt_id))?; - self.anchor_in_excerpt_(excerpt, text_anchor) + Self::anchor_in_excerpt_(excerpt, text_anchor) } - fn anchor_in_excerpt_(&self, excerpt: &Excerpt, text_anchor: text::Anchor) -> Option { + /// Same as [`MultiBuffer::anchor_in_excerpt`], but more efficient than calling it multiple times. + pub fn anchors_in_excerpt( + &self, + excerpt_id: ExcerptId, + text_anchors: impl IntoIterator, + ) -> Option>> { + let excerpt = self.excerpt(self.latest_excerpt_id(excerpt_id))?; + Some( + text_anchors + .into_iter() + .map(|text_anchor| Self::anchor_in_excerpt_(excerpt, text_anchor)), + ) + } + + fn anchor_in_excerpt_(excerpt: &Excerpt, text_anchor: text::Anchor) -> Option { match text_anchor.buffer_id { Some(buffer_id) if buffer_id == excerpt.buffer_id => (), Some(_) => return None,