editor: Reduce amount of sumtree traversals in header_and_footer_blocks (#43709)

Introduces new "mapping point cursors" for the different display map
layers allowing one to map multiple points in increasing order more
efficiently than using the one shot operations.

This is used in the `BlockMap::sync` for `header_and_footer_blocks`
which spends a significant time in sumtree traversal due to repeatedly
transforming points between the different layers. This effectively turns
the complexity of those operations from quadratic in the number of
excerpts to linear, as we only go through the respective sumtrees once
instead of restarting from the start over and over again.

Release Notes:

- Improved performance for editors of large multibuffers with many
different files
This commit is contained in:
Lukas Wirth
2025-11-28 09:06:59 +01:00
committed by GitHub
parent fc213f1c26
commit 8f8a92ccf0
8 changed files with 221 additions and 89 deletions
+54 -30
View File
@@ -879,37 +879,16 @@ impl InlaySnapshot {
}
}
}
pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
let mut cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(());
cursor.seek(&point, Bias::Left);
loop {
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
if point == cursor.end().0 {
while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
if inlay.position.bias() == Bias::Right {
break;
} else {
cursor.next();
}
}
return cursor.end().1;
} else {
let overshoot = point - cursor.start().0;
return InlayPoint(cursor.start().1.0 + overshoot);
}
}
Some(Transform::Inlay(inlay)) => {
if inlay.position.bias() == Bias::Left {
cursor.next();
} else {
return cursor.start().1;
}
}
None => {
return self.max_point();
}
}
self.inlay_point_cursor().map(point)
}
pub fn inlay_point_cursor(&self) -> InlayPointCursor<'_> {
let cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(());
InlayPointCursor {
cursor,
transforms: &self.transforms,
}
}
@@ -1162,6 +1141,51 @@ impl InlaySnapshot {
}
}
pub struct InlayPointCursor<'transforms> {
cursor: Cursor<'transforms, 'static, Transform, Dimensions<Point, InlayPoint>>,
transforms: &'transforms SumTree<Transform>,
}
impl InlayPointCursor<'_> {
pub fn map(&mut self, point: Point) -> InlayPoint {
let cursor = &mut self.cursor;
if cursor.did_seek() {
cursor.seek_forward(&point, Bias::Left);
} else {
cursor.seek(&point, Bias::Left);
}
loop {
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
if point == cursor.end().0 {
while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
if inlay.position.bias() == Bias::Right {
break;
} else {
cursor.next();
}
}
return cursor.end().1;
} else {
let overshoot = point - cursor.start().0;
return InlayPoint(cursor.start().1.0 + overshoot);
}
}
Some(Transform::Inlay(inlay)) => {
if inlay.position.bias() == Bias::Left {
cursor.next();
} else {
return cursor.start().1;
}
}
None => {
return InlayPoint(self.transforms.summary().output.lines);
}
}
}
}
}
fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: MBTextSummary) {
if summary.len == MultiBufferOffset(0) {
return;