sum_tree: Implement recursive Sumtree::find, use it over Cursor::seek if possible (#40700)

Reduces peak stack usage in these functions and should generally be a
bit performant.

Display map benchmark results
```
To tab point/to_tab_point/1024
                        time:   [531.40 ns 532.10 ns 532.97 ns]
                        change: [-2.1824% -2.0054% -1.8125%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe

To fold point/to_fold_point/1024
                        time:   [530.81 ns 531.30 ns 531.80 ns]
                        change: [-2.0295% -1.9054% -1.7716%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  2 (2.00%) high mild
  1 (1.00%) high severe
```

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-10-20 17:20:09 +00:00
committed by GitHub
parent b6fb1d0a19
commit d8f4293ac3
13 changed files with 332 additions and 206 deletions
+33 -27
View File
@@ -1521,10 +1521,11 @@ impl BlockSnapshot {
}
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
cursor.seek(&BlockRow(row.0), Bias::Right);
if let Some(transform) = cursor.item() {
let Dimensions(output_start, input_start, _) = cursor.start();
let (start, _, item) =
self.transforms
.find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
if let Some(transform) = item {
let Dimensions(output_start, input_start, _) = start;
let overshoot = row.0 - output_start.0;
if transform.block.is_some() {
0
@@ -1539,15 +1540,13 @@ impl BlockSnapshot {
}
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
cursor.seek(&row, Bias::Right);
cursor.item().is_some_and(|t| t.block.is_some())
let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
item.is_some_and(|t| t.block.is_some())
}
pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
cursor.seek(&row, Bias::Right);
let Some(transform) = cursor.item() else {
let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
let Some(transform) = item else {
return false;
};
matches!(transform.block, Some(Block::FoldedBuffer { .. }))
@@ -1557,9 +1556,10 @@ impl BlockSnapshot {
let wrap_point = self
.wrap_snapshot
.make_wrap_point(Point::new(row.0, 0), Bias::Left);
let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
cursor.item().is_some_and(|transform| {
let (_, _, item) =
self.transforms
.find::<WrapRow, _>((), &WrapRow(wrap_point.row()), Bias::Right);
item.is_some_and(|transform| {
transform
.block
.as_ref()
@@ -1627,13 +1627,16 @@ impl BlockSnapshot {
}
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
if let Some(transform) = cursor.item() {
let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
(),
&WrapRow(wrap_point.row()),
Bias::Right,
);
if let Some(transform) = item {
if transform.block.is_some() {
BlockPoint::new(cursor.start().1.0, 0)
BlockPoint::new(start.1.0, 0)
} else {
let Dimensions(input_start_row, output_start_row, _) = cursor.start();
let Dimensions(input_start_row, output_start_row, _) = start;
let input_start = Point::new(input_start_row.0, 0);
let output_start = Point::new(output_start_row.0, 0);
let input_overshoot = wrap_point.0 - input_start;
@@ -1645,26 +1648,29 @@ impl BlockSnapshot {
}
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
cursor.seek(&BlockRow(block_point.row), Bias::Right);
if let Some(transform) = cursor.item() {
let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
(),
&BlockRow(block_point.row),
Bias::Right,
);
if let Some(transform) = item {
match transform.block.as_ref() {
Some(block) => {
if block.place_below() {
let wrap_row = cursor.start().1.0 - 1;
let wrap_row = start.1.0 - 1;
WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
} else if block.place_above() {
WrapPoint::new(cursor.start().1.0, 0)
WrapPoint::new(start.1.0, 0)
} else if bias == Bias::Left {
WrapPoint::new(cursor.start().1.0, 0)
WrapPoint::new(start.1.0, 0)
} else {
let wrap_row = cursor.end().1.0 - 1;
let wrap_row = end.1.0 - 1;
WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
}
}
None => {
let overshoot = block_point.row - cursor.start().0.0;
let wrap_row = cursor.start().1.0 + overshoot;
let overshoot = block_point.row - start.0.0;
let wrap_row = start.1.0 + overshoot;
WrapPoint::new(wrap_row, block_point.column)
}
}
+41 -46
View File
@@ -98,28 +98,26 @@ impl FoldPoint {
}
pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
let mut cursor = snapshot
let (start, _, _) = snapshot
.transforms
.cursor::<Dimensions<FoldPoint, InlayPoint>>(());
cursor.seek(&self, Bias::Right);
let overshoot = self.0 - cursor.start().0.0;
InlayPoint(cursor.start().1.0 + overshoot)
.find::<Dimensions<FoldPoint, InlayPoint>, _>((), &self, Bias::Right);
let overshoot = self.0 - start.0.0;
InlayPoint(start.1.0 + overshoot)
}
pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
let mut cursor = snapshot
let (start, _, item) = snapshot
.transforms
.cursor::<Dimensions<FoldPoint, TransformSummary>>(());
cursor.seek(&self, Bias::Right);
let overshoot = self.0 - cursor.start().1.output.lines;
let mut offset = cursor.start().1.output.len;
.find::<Dimensions<FoldPoint, TransformSummary>, _>((), &self, Bias::Right);
let overshoot = self.0 - start.1.output.lines;
let mut offset = start.1.output.len;
if !overshoot.is_zero() {
let transform = cursor.item().expect("display point out of range");
let transform = item.expect("display point out of range");
assert!(transform.placeholder.is_none());
let end_inlay_offset = snapshot
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1.input.lines + overshoot));
offset += end_inlay_offset.0 - cursor.start().1.input.len;
.to_offset(InlayPoint(start.1.input.lines + overshoot));
offset += end_inlay_offset.0 - start.1.input.len;
}
FoldOffset(offset)
}
@@ -706,19 +704,18 @@ impl FoldSnapshot {
}
pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
let mut cursor = self
let (start, end, item) = self
.transforms
.cursor::<Dimensions<InlayPoint, FoldPoint>>(());
cursor.seek(&point, Bias::Right);
if cursor.item().is_some_and(|t| t.is_fold()) {
if bias == Bias::Left || point == cursor.start().0 {
cursor.start().1
.find::<Dimensions<InlayPoint, FoldPoint>, _>((), &point, Bias::Right);
if item.is_some_and(|t| t.is_fold()) {
if bias == Bias::Left || point == start.0 {
start.1
} else {
cursor.end().1
end.1
}
} else {
let overshoot = point.0 - cursor.start().0.0;
FoldPoint(cmp::min(cursor.start().1.0 + overshoot, cursor.end().1.0))
let overshoot = point.0 - start.0.0;
FoldPoint(cmp::min(start.1.0 + overshoot, end.1.0))
}
}
@@ -787,9 +784,10 @@ impl FoldSnapshot {
{
let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
let mut cursor = self.transforms.cursor::<InlayOffset>(());
cursor.seek(&inlay_offset, Bias::Right);
cursor.item().is_some_and(|t| t.placeholder.is_some())
let (_, _, item) = self
.transforms
.find::<InlayOffset, _>((), &inlay_offset, Bias::Right);
item.is_some_and(|t| t.placeholder.is_some())
}
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
@@ -891,23 +889,22 @@ impl FoldSnapshot {
}
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
let mut cursor = self
let (start, end, item) = self
.transforms
.cursor::<Dimensions<FoldPoint, InlayPoint>>(());
cursor.seek(&point, Bias::Right);
if let Some(transform) = cursor.item() {
let transform_start = cursor.start().0.0;
.find::<Dimensions<FoldPoint, InlayPoint>, _>((), &point, Bias::Right);
if let Some(transform) = item {
let transform_start = start.0.0;
if transform.placeholder.is_some() {
if point.0 == transform_start || matches!(bias, Bias::Left) {
FoldPoint(transform_start)
} else {
FoldPoint(cursor.end().0.0)
FoldPoint(end.0.0)
}
} else {
let overshoot = InlayPoint(point.0 - transform_start);
let inlay_point = cursor.start().1 + overshoot;
let inlay_point = start.1 + overshoot;
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
FoldPoint(cursor.start().0.0 + (clipped_inlay_point - cursor.start().1).0)
FoldPoint(start.0.0 + (clipped_inlay_point - start.1).0)
}
} else {
FoldPoint(self.transforms.summary().output.lines)
@@ -1480,28 +1477,26 @@ pub struct FoldOffset(pub usize);
impl FoldOffset {
pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
let mut cursor = snapshot
let (start, _, item) = snapshot
.transforms
.cursor::<Dimensions<FoldOffset, TransformSummary>>(());
cursor.seek(&self, Bias::Right);
let overshoot = if cursor.item().is_none_or(|t| t.is_fold()) {
Point::new(0, (self.0 - cursor.start().0.0) as u32)
.find::<Dimensions<FoldOffset, TransformSummary>, _>((), &self, Bias::Right);
let overshoot = if item.is_none_or(|t| t.is_fold()) {
Point::new(0, (self.0 - start.0.0) as u32)
} else {
let inlay_offset = cursor.start().1.input.len + self.0 - cursor.start().0.0;
let inlay_offset = start.1.input.len + self.0 - start.0.0;
let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset));
inlay_point.0 - cursor.start().1.input.lines
inlay_point.0 - start.1.input.lines
};
FoldPoint(cursor.start().1.output.lines + overshoot)
FoldPoint(start.1.output.lines + overshoot)
}
#[cfg(test)]
pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
let mut cursor = snapshot
let (start, _, _) = snapshot
.transforms
.cursor::<Dimensions<FoldOffset, InlayOffset>>(());
cursor.seek(&self, Bias::Right);
let overshoot = self.0 - cursor.start().0.0;
InlayOffset(cursor.start().1.0 + overshoot)
.find::<Dimensions<FoldOffset, InlayOffset>, _>((), &self, Bias::Right);
let overshoot = self.0 - start.0.0;
InlayOffset(start.1.0 + overshoot)
}
}
+28 -28
View File
@@ -825,22 +825,21 @@ impl InlayMap {
impl InlaySnapshot {
pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
let mut cursor = self
let (start, _, item) = self
.transforms
.cursor::<Dimensions<InlayOffset, InlayPoint, usize>>(());
cursor.seek(&offset, Bias::Right);
let overshoot = offset.0 - cursor.start().0.0;
match cursor.item() {
.find::<Dimensions<InlayOffset, InlayPoint, usize>, _>((), &offset, Bias::Right);
let overshoot = offset.0 - start.0.0;
match item {
Some(Transform::Isomorphic(_)) => {
let buffer_offset_start = cursor.start().2;
let buffer_offset_start = start.2;
let buffer_offset_end = buffer_offset_start + overshoot;
let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
InlayPoint(cursor.start().1.0 + (buffer_end - buffer_start))
InlayPoint(start.1.0 + (buffer_end - buffer_start))
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text().offset_to_point(overshoot);
InlayPoint(cursor.start().1.0 + overshoot)
InlayPoint(start.1.0 + overshoot)
}
None => self.max_point(),
}
@@ -855,47 +854,48 @@ impl InlaySnapshot {
}
pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
let mut cursor = self
let (start, _, item) = self
.transforms
.cursor::<Dimensions<InlayPoint, InlayOffset, Point>>(());
cursor.seek(&point, Bias::Right);
let overshoot = point.0 - cursor.start().0.0;
match cursor.item() {
.find::<Dimensions<InlayPoint, InlayOffset, Point>, _>((), &point, Bias::Right);
let overshoot = point.0 - start.0.0;
match item {
Some(Transform::Isomorphic(_)) => {
let buffer_point_start = cursor.start().2;
let buffer_point_start = start.2;
let buffer_point_end = buffer_point_start + overshoot;
let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
InlayOffset(cursor.start().1.0 + (buffer_offset_end - buffer_offset_start))
InlayOffset(start.1.0 + (buffer_offset_end - buffer_offset_start))
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text().point_to_offset(overshoot);
InlayOffset(cursor.start().1.0 + overshoot)
InlayOffset(start.1.0 + overshoot)
}
None => self.len(),
}
}
pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(());
cursor.seek(&point, Bias::Right);
match cursor.item() {
let (start, _, item) =
self.transforms
.find::<Dimensions<InlayPoint, Point>, _>((), &point, Bias::Right);
match item {
Some(Transform::Isomorphic(_)) => {
let overshoot = point.0 - cursor.start().0.0;
cursor.start().1 + overshoot
let overshoot = point.0 - start.0.0;
start.1 + overshoot
}
Some(Transform::Inlay(_)) => cursor.start().1,
Some(Transform::Inlay(_)) => start.1,
None => self.buffer.max_point(),
}
}
pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
cursor.seek(&offset, Bias::Right);
match cursor.item() {
let (start, _, item) =
self.transforms
.find::<Dimensions<InlayOffset, usize>, _>((), &offset, Bias::Right);
match item {
Some(Transform::Isomorphic(_)) => {
let overshoot = offset - cursor.start().0;
cursor.start().1 + overshoot.0
let overshoot = offset - start.0;
start.1 + overshoot.0
}
Some(Transform::Inlay(_)) => cursor.start().1,
Some(Transform::Inlay(_)) => start.1,
None => self.buffer.len(),
}
}
+32 -31
View File
@@ -568,14 +568,17 @@ impl WrapSnapshot {
let mut old_start = old_cursor.start().output.lines;
old_start += tab_edit.old.start.0 - old_cursor.start().input.lines;
// todo(lw): Should these be seek_forward?
old_cursor.seek(&tab_edit.old.end, Bias::Right);
let mut old_end = old_cursor.start().output.lines;
old_end += tab_edit.old.end.0 - old_cursor.start().input.lines;
// todo(lw): Should these be seek_forward?
new_cursor.seek(&tab_edit.new.start, Bias::Right);
let mut new_start = new_cursor.start().output.lines;
new_start += tab_edit.new.start.0 - new_cursor.start().input.lines;
// todo(lw): Should these be seek_forward?
new_cursor.seek(&tab_edit.new.end, Bias::Right);
let mut new_end = new_cursor.start().output.lines;
new_end += tab_edit.new.end.0 - new_cursor.start().input.lines;
@@ -628,24 +631,22 @@ impl WrapSnapshot {
}
pub fn line_len(&self, row: u32) -> u32 {
let mut cursor = self
.transforms
.cursor::<Dimensions<WrapPoint, TabPoint>>(());
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left);
if cursor
.item()
.is_some_and(|transform| transform.is_isomorphic())
{
let overshoot = row - cursor.start().0.row();
let tab_row = cursor.start().1.row() + overshoot;
let (start, _, item) = self.transforms.find::<Dimensions<WrapPoint, TabPoint>, _>(
(),
&WrapPoint::new(row + 1, 0),
Bias::Left,
);
if item.is_some_and(|transform| transform.is_isomorphic()) {
let overshoot = row - start.0.row();
let tab_row = start.1.row() + overshoot;
let tab_line_len = self.tab_snapshot.line_len(tab_row);
if overshoot == 0 {
cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
start.0.column() + (tab_line_len - start.1.column())
} else {
tab_line_len
}
} else {
cursor.start().0.column()
start.0.column()
}
}
@@ -711,9 +712,10 @@ impl WrapSnapshot {
}
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
let mut cursor = self.transforms.cursor::<WrapPoint>(());
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right);
cursor.item().and_then(|transform| {
let (.., item) =
self.transforms
.find::<WrapPoint, _>((), &WrapPoint::new(row + 1, 0), Bias::Right);
item.and_then(|transform| {
if transform.is_isomorphic() {
None
} else {
@@ -749,13 +751,12 @@ impl WrapSnapshot {
}
pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
let mut cursor = self
.transforms
.cursor::<Dimensions<WrapPoint, TabPoint>>(());
cursor.seek(&point, Bias::Right);
let mut tab_point = cursor.start().1.0;
if cursor.item().is_some_and(|t| t.is_isomorphic()) {
tab_point += point.0 - cursor.start().0.0;
let (start, _, item) =
self.transforms
.find::<Dimensions<WrapPoint, TabPoint>, _>((), &point, Bias::Right);
let mut tab_point = start.1.0;
if item.is_some_and(|t| t.is_isomorphic()) {
tab_point += point.0 - start.0.0;
}
TabPoint(tab_point)
}
@@ -769,19 +770,19 @@ impl WrapSnapshot {
}
pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
let mut cursor = self
.transforms
.cursor::<Dimensions<TabPoint, WrapPoint>>(());
cursor.seek(&point, Bias::Right);
WrapPoint(cursor.start().1.0 + (point.0 - cursor.start().0.0))
let (start, ..) =
self.transforms
.find::<Dimensions<TabPoint, WrapPoint>, _>((), &point, Bias::Right);
WrapPoint(start.1.0 + (point.0 - start.0.0))
}
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
if bias == Bias::Left {
let mut cursor = self.transforms.cursor::<WrapPoint>(());
cursor.seek(&point, Bias::Right);
if cursor.item().is_some_and(|t| !t.is_isomorphic()) {
point = *cursor.start();
let (start, _, item) = self
.transforms
.find::<WrapPoint, _>((), &point, Bias::Right);
if item.is_some_and(|t| !t.is_isomorphic()) {
point = start;
*point.column_mut() -= 1;
}
}
+16 -11
View File
@@ -509,10 +509,11 @@ impl StateInner {
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
self.logical_scroll_top = None;
} else {
let mut cursor = self.items.cursor::<ListItemSummary>(());
cursor.seek(&Height(new_scroll_top), Bias::Right);
let item_ix = cursor.start().count;
let offset_in_item = new_scroll_top - cursor.start().height;
let (start, ..) =
self.items
.find::<ListItemSummary, _>((), &Height(new_scroll_top), Bias::Right);
let item_ix = start.count;
let offset_in_item = new_scroll_top - start.height;
self.logical_scroll_top = Some(ListOffset {
item_ix,
offset_in_item,
@@ -550,9 +551,12 @@ impl StateInner {
}
fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
let mut cursor = self.items.cursor::<ListItemSummary>(());
cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right);
cursor.start().height + logical_scroll_top.offset_in_item
let (start, ..) = self.items.find::<ListItemSummary, _>(
(),
&Count(logical_scroll_top.item_ix),
Bias::Right,
);
start.height + logical_scroll_top.offset_in_item
}
fn layout_all_items(
@@ -882,11 +886,12 @@ impl StateInner {
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
self.logical_scroll_top = None;
} else {
let mut cursor = self.items.cursor::<ListItemSummary>(());
cursor.seek(&Height(new_scroll_top), Bias::Right);
let (start, _, _) =
self.items
.find::<ListItemSummary, _>((), &Height(new_scroll_top), Bias::Right);
let item_ix = cursor.start().count;
let offset_in_item = new_scroll_top - cursor.start().height;
let item_ix = start.count;
let offset_in_item = new_scroll_top - start.height;
self.logical_scroll_top = Some(ListOffset {
item_ix,
offset_in_item,
+2 -3
View File
@@ -6140,9 +6140,8 @@ impl MultiBufferSnapshot {
} else if id == ExcerptId::max() {
Locator::max_ref()
} else {
let mut cursor = self.excerpt_ids.cursor::<ExcerptId>(());
cursor.seek(&id, Bias::Left);
if let Some(entry) = cursor.item()
let (_, _, item) = self.excerpt_ids.find::<ExcerptId, _>((), &id, Bias::Left);
if let Some(entry) = item
&& entry.id == id
{
return &entry.locator;
@@ -123,14 +123,16 @@ impl NotificationStore {
return None;
}
let ix = count - 1 - ix;
let mut cursor = self.notifications.cursor::<Count>(());
cursor.seek(&Count(ix), Bias::Right);
cursor.item()
let (.., item) = self
.notifications
.find::<Count, _>((), &Count(ix), Bias::Right);
item
}
pub fn notification_for_id(&self, id: u64) -> Option<&NotificationEntry> {
let mut cursor = self.notifications.cursor::<NotificationId>(());
cursor.seek(&NotificationId(id), Bias::Left);
if let Some(item) = cursor.item()
let (.., item) =
self.notifications
.find::<NotificationId, _>((), &NotificationId(id), Bias::Left);
if let Some(item) = item
&& item.id == id
{
return Some(item);
+17 -21
View File
@@ -41,12 +41,9 @@ impl Rope {
if self.chunks.is_empty() {
return offset == 0;
}
let mut cursor = self.chunks.cursor::<usize>(());
cursor.seek(&offset, Bias::Left);
let chunk_offset = offset - cursor.start();
cursor
.item()
.map(|chunk| chunk.text.is_char_boundary(chunk_offset))
let (start, _, item) = self.chunks.find::<usize, _>((), &offset, Bias::Left);
let chunk_offset = offset - start;
item.map(|chunk| chunk.text.is_char_boundary(chunk_offset))
.unwrap_or(false)
}
@@ -60,10 +57,9 @@ impl Rope {
(u8 as i8) >= -0x40
}
let mut cursor = self.chunks.cursor::<usize>(());
cursor.seek(&index, Bias::Left);
let chunk_offset = index - cursor.start();
let lower_idx = cursor.item().map(|chunk| {
let (start, _, item) = self.chunks.find::<usize, _>((), &index, Bias::Left);
let chunk_offset = index - start;
let lower_idx = item.map(|chunk| {
let lower_bound = chunk_offset.saturating_sub(3);
chunk
.text
@@ -78,7 +74,7 @@ impl Rope {
})
.unwrap_or(chunk.text.len())
});
lower_idx.map_or_else(|| self.len(), |idx| cursor.start() + idx)
lower_idx.map_or_else(|| self.len(), |idx| start + idx)
}
}
@@ -92,10 +88,9 @@ impl Rope {
(u8 as i8) >= -0x40
}
let mut cursor = self.chunks.cursor::<usize>(());
cursor.seek(&index, Bias::Left);
let chunk_offset = index - cursor.start();
let upper_idx = cursor.item().map(|chunk| {
let (start, _, item) = self.chunks.find::<usize, _>((), &index, Bias::Left);
let chunk_offset = index - start;
let upper_idx = item.map(|chunk| {
let upper_bound = Ord::min(chunk_offset + 4, chunk.text.len());
chunk.text.as_bytes()[chunk_offset..upper_bound]
.iter()
@@ -103,7 +98,7 @@ impl Rope {
.map_or(upper_bound, |pos| pos + chunk_offset)
});
upper_idx.map_or_else(|| self.len(), |idx| cursor.start() + idx)
upper_idx.map_or_else(|| self.len(), |idx| start + idx)
}
}
@@ -356,11 +351,12 @@ impl Rope {
if offset >= self.summary().len {
return self.summary().len_utf16;
}
let mut cursor = self.chunks.cursor::<Dimensions<usize, OffsetUtf16>>(());
cursor.seek(&offset, Bias::Left);
let overshoot = offset - cursor.start().0;
cursor.start().1
+ cursor.item().map_or(Default::default(), |chunk| {
let (start, _, item) =
self.chunks
.find::<Dimensions<usize, OffsetUtf16>, _>((), &offset, Bias::Left);
let overshoot = offset - start.0;
start.1
+ item.map_or(Default::default(), |chunk| {
chunk.as_slice().offset_to_offset_utf16(overshoot)
})
}
+3 -1
View File
@@ -388,6 +388,7 @@ where
T: Item,
D: Dimension<'a, T::Summary>,
{
/// Returns whether we found the item you were seeking for.
#[track_caller]
pub fn seek<Target>(&mut self, pos: &Target, bias: Bias) -> bool
where
@@ -397,6 +398,7 @@ where
self.seek_internal(pos, bias, &mut ())
}
/// Returns whether we found the item you were seeking for.
#[track_caller]
pub fn seek_forward<Target>(&mut self, pos: &Target, bias: Bias) -> bool
where
@@ -437,7 +439,7 @@ where
summary.0
}
/// Returns whether we found the item you were seeking for
/// Returns whether we found the item you were seeking for.
#[track_caller]
fn seek_internal(
&mut self,
+120 -6
View File
@@ -82,6 +82,11 @@ pub trait Dimension<'a, S: Summary>: Clone {
fn zero(cx: S::Context<'_>) -> Self;
fn add_summary(&mut self, summary: &'a S, cx: S::Context<'_>);
#[must_use]
fn with_added_summary(mut self, summary: &'a S, cx: S::Context<'_>) -> Self {
self.add_summary(summary, cx);
self
}
fn from_summary(summary: &'a S, cx: S::Context<'_>) -> Self {
let mut dimension = Self::zero(cx);
@@ -371,12 +376,122 @@ impl<T: Item> SumTree<T> {
Iter::new(self)
}
pub fn cursor<'a, 'b, S>(
/// A more efficient version of `Cursor::new()` + `Cursor::seek()` + `Cursor::item()`.
///
/// Only returns the item that exactly has the target match.
pub fn find_exact<'a, 'slf, D, Target>(
&'slf self,
cx: <T::Summary as Summary>::Context<'a>,
target: &Target,
bias: Bias,
) -> (D, D, Option<&'slf T>)
where
D: Dimension<'slf, T::Summary>,
Target: SeekTarget<'slf, T::Summary, D>,
{
let tree_end = D::zero(cx).with_added_summary(self.summary(), cx);
let comparison = target.cmp(&tree_end, cx);
if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == Bias::Right)
{
return (tree_end.clone(), tree_end, None);
}
let mut pos = D::zero(cx);
return match Self::find_recurse::<_, _, true>(cx, target, bias, &mut pos, self) {
Some((item, end)) => (pos, end, Some(item)),
None => (pos.clone(), pos, None),
};
}
/// A more efficient version of `Cursor::new()` + `Cursor::seek()` + `Cursor::item()`
pub fn find<'a, 'slf, D, Target>(
&'slf self,
cx: <T::Summary as Summary>::Context<'a>,
target: &Target,
bias: Bias,
) -> (D, D, Option<&'slf T>)
where
D: Dimension<'slf, T::Summary>,
Target: SeekTarget<'slf, T::Summary, D>,
{
let tree_end = D::zero(cx).with_added_summary(self.summary(), cx);
let comparison = target.cmp(&tree_end, cx);
if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == Bias::Right)
{
return (tree_end.clone(), tree_end, None);
}
let mut pos = D::zero(cx);
return match Self::find_recurse::<_, _, false>(cx, target, bias, &mut pos, self) {
Some((item, end)) => (pos, end, Some(item)),
None => (pos.clone(), pos, None),
};
}
fn find_recurse<'tree, 'a, D, Target, const EXACT: bool>(
cx: <T::Summary as Summary>::Context<'a>,
target: &Target,
bias: Bias,
position: &mut D,
this: &'tree SumTree<T>,
) -> Option<(&'tree T, D)>
where
D: Dimension<'tree, T::Summary>,
Target: SeekTarget<'tree, T::Summary, D>,
{
match &*this.0 {
Node::Internal {
child_summaries,
child_trees,
..
} => {
for (child_tree, child_summary) in child_trees.iter().zip(child_summaries) {
let child_end = position.clone().with_added_summary(child_summary, cx);
let comparison = target.cmp(&child_end, cx);
let target_in_child = comparison == Ordering::Less
|| (comparison == Ordering::Equal && bias == Bias::Left);
if target_in_child {
return Self::find_recurse::<D, Target, EXACT>(
cx, target, bias, position, child_tree,
);
}
*position = child_end;
}
}
Node::Leaf {
items,
item_summaries,
..
} => {
for (item, item_summary) in items.iter().zip(item_summaries) {
let mut child_end = position.clone();
child_end.add_summary(item_summary, cx);
let comparison = target.cmp(&child_end, cx);
let entry_found = if EXACT {
comparison == Ordering::Equal
} else {
comparison == Ordering::Less
|| (comparison == Ordering::Equal && bias == Bias::Left)
};
if entry_found {
return Some((item, child_end));
}
*position = child_end;
}
}
}
None
}
pub fn cursor<'a, 'b, D>(
&'a self,
cx: <T::Summary as Summary>::Context<'b>,
) -> Cursor<'a, 'b, T, S>
) -> Cursor<'a, 'b, T, D>
where
S: Dimension<'a, T::Summary>,
D: Dimension<'a, T::Summary>,
{
Cursor::new(self, cx)
}
@@ -787,9 +902,8 @@ impl<T: KeyedItem> SumTree<T> {
key: &T::Key,
cx: <T::Summary as Summary>::Context<'a>,
) -> Option<&'a T> {
let mut cursor = self.cursor::<T::Key>(cx);
if cursor.seek(key, Bias::Left) {
cursor.item()
if let (_, _, Some(item)) = self.find_exact::<T::Key, _>(cx, key, Bias::Left) {
Some(item)
} else {
None
}
+4 -3
View File
@@ -54,9 +54,10 @@ impl<K: Clone + Ord, V: Clone> TreeMap<K, V> {
}
pub fn get(&self, key: &K) -> Option<&V> {
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>(());
cursor.seek(&MapKeyRef(Some(key)), Bias::Left);
if let Some(item) = cursor.item() {
let (.., item) = self
.0
.find::<MapKeyRef<'_, K>, _>((), &MapKeyRef(Some(key)), Bias::Left);
if let Some(item) = item {
if Some(key) == item.key().0.as_ref() {
Some(&item.value)
} else {
+7 -6
View File
@@ -99,13 +99,14 @@ impl Anchor {
let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
return false;
};
let mut fragment_cursor = buffer
let (.., item) = buffer
.fragments
.cursor::<Dimensions<Option<&Locator>, usize>>(&None);
fragment_cursor.seek(&Some(fragment_id), Bias::Left);
fragment_cursor
.item()
.is_some_and(|fragment| fragment.visible)
.find::<Dimensions<Option<&Locator>, usize>, _>(
&None,
&Some(fragment_id),
Bias::Left,
);
item.is_some_and(|fragment| fragment.visible)
}
}
}
+21 -17
View File
@@ -2323,12 +2323,15 @@ impl BufferSnapshot {
);
};
let mut fragment_cursor = self
let (start, _, item) = self
.fragments
.cursor::<Dimensions<Option<&Locator>, usize>>(&None);
fragment_cursor.seek(&Some(&insertion.fragment_id), Bias::Left);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
.find::<Dimensions<Option<&Locator>, usize>, _>(
&None,
&Some(&insertion.fragment_id),
Bias::Left,
);
let fragment = item.unwrap();
let mut fragment_offset = start.1;
if fragment.visible {
fragment_offset += anchor.offset - insertion.split_offset;
}
@@ -2410,10 +2413,9 @@ impl BufferSnapshot {
offset, ch, char_range,
);
}
let mut fragment_cursor = self.fragments.cursor::<usize>(&None);
fragment_cursor.seek(&offset, bias);
let fragment = fragment_cursor.item().unwrap();
let overshoot = offset - *fragment_cursor.start();
let (start, _, item) = self.fragments.find::<usize, _>(&None, &offset, bias);
let fragment = item.unwrap();
let overshoot = offset - start;
Anchor {
timestamp: fragment.timestamp,
offset: fragment.insertion_offset + overshoot,
@@ -2494,15 +2496,17 @@ impl BufferSnapshot {
cursor.next();
Some(cursor)
};
let mut cursor = self
.fragments
.cursor::<Dimensions<Option<&Locator>, FragmentTextSummary>>(&None);
let start_fragment_id = self.fragment_id_for_anchor(&range.start);
cursor.seek(&Some(start_fragment_id), Bias::Left);
let mut visible_start = cursor.start().1.visible;
let mut deleted_start = cursor.start().1.deleted;
if let Some(fragment) = cursor.item() {
let (start, _, item) = self
.fragments
.find::<Dimensions<Option<&Locator>, FragmentTextSummary>, _>(
&None,
&Some(start_fragment_id),
Bias::Left,
);
let mut visible_start = start.1.visible;
let mut deleted_start = start.1.deleted;
if let Some(fragment) = item {
let overshoot = range.start.offset - fragment.insertion_offset;
if fragment.visible {
visible_start += overshoot;