text: Speed up offset_for_anchor and fragment_id_for_anchor conversions (#46989)
And most importantly, speed `Anchor::cmp` by doing so. Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
1 parent
f8d5a324a1
commit
b0bbd1e7c8
5 files changed
+150
-72
No files matched your search
@@ -109,10 +109,6 @@ impl SelectionsCollection {
|
||||
self.pending.as_ref().map(|pending| &pending.selection)
|
||||
}
|
||||
|
||||
pub fn pending_anchor_mut(&mut self) -> Option<&mut Selection<Anchor>> {
|
||||
self.pending.as_mut().map(|pending| &mut pending.selection)
|
||||
}
|
||||
|
||||
pub fn pending<D>(&self, snapshot: &DisplaySnapshot) -> Option<Selection<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
@@ -545,6 +541,11 @@ impl SelectionsCollection {
|
||||
);
|
||||
if cfg!(debug_assertions) {
|
||||
mutable_collection.disjoint.iter().for_each(|selection| {
|
||||
assert!(
|
||||
selection.start.cmp(&selection.end, &snapshot).is_le(),
|
||||
"disjoint selection has start > end: {:?}",
|
||||
mutable_collection.disjoint
|
||||
);
|
||||
assert!(
|
||||
snapshot.can_resolve(&selection.start),
|
||||
"disjoint selection start is not resolvable for the given snapshot:\n{selection:?}, {excerpt:?}",
|
||||
@@ -556,8 +557,20 @@ impl SelectionsCollection {
|
||||
excerpt = snapshot.buffer_for_excerpt(selection.end.excerpt_id).map(|snapshot| snapshot.remote_id()),
|
||||
);
|
||||
});
|
||||
assert!(
|
||||
mutable_collection
|
||||
.disjoint
|
||||
.is_sorted_by(|first, second| first.end.cmp(&second.start, &snapshot).is_le()),
|
||||
"disjoint selections are not sorted: {:?}",
|
||||
mutable_collection.disjoint
|
||||
);
|
||||
if let Some(pending) = &mutable_collection.pending {
|
||||
let selection = &pending.selection;
|
||||
assert!(
|
||||
selection.start.cmp(&selection.end, &snapshot).is_le(),
|
||||
"pending selection has start > end: {:?}",
|
||||
selection
|
||||
);
|
||||
assert!(
|
||||
snapshot.can_resolve(&selection.start),
|
||||
"pending selection start is not resolvable for the given snapshot: {pending:?}, {excerpt:?}",
|
||||
@@ -933,7 +946,6 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
for selection in disjoint
|
||||
.iter()
|
||||
.sorted_by(|first, second| Ord::cmp(&second.id, &first.id))
|
||||
.collect::<Vec<&Selection<Anchor>>>()
|
||||
{
|
||||
new_selections.push(Selection {
|
||||
id: self.new_selection_id(),
|
||||
@@ -1062,6 +1074,11 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
self.select(new_selections);
|
||||
}
|
||||
|
||||
pub fn pending_anchor_mut(&mut self) -> Option<&mut Selection<Anchor>> {
|
||||
self.selections_changed = true;
|
||||
self.pending.as_mut().map(|pending| &mut pending.selection)
|
||||
}
|
||||
|
||||
/// Compute new ranges for any selections that were located in excerpts that have
|
||||
/// since been removed.
|
||||
///
|
||||
|
||||
@@ -30,7 +30,7 @@ impl<T: Item + fmt::Debug, D: fmt::Debug> fmt::Debug for StackEntry<'_, T, D> {
|
||||
pub struct Cursor<'a, 'b, T: Item, D> {
|
||||
tree: &'a SumTree<T>,
|
||||
stack: ArrayVec<StackEntry<'a, T, D>, 16>,
|
||||
position: D,
|
||||
pub position: D,
|
||||
did_seek: bool,
|
||||
at_end: bool,
|
||||
cx: <T::Summary as Summary>::Context<'b>,
|
||||
|
||||
@@ -491,6 +491,97 @@ impl<T: Item> SumTree<T> {
|
||||
None
|
||||
}
|
||||
|
||||
/// A more efficient version of `Cursor::new()` + `Cursor::seek()` + `Cursor::item()`
|
||||
#[instrument(skip_all)]
|
||||
pub fn find_with_prev<'a, 'slf, D, Target>(
|
||||
&'slf self,
|
||||
cx: <T::Summary as Summary>::Context<'a>,
|
||||
target: &Target,
|
||||
bias: Bias,
|
||||
) -> (D, D, Option<(Option<&'slf T>, &'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_with_prev::<_, _, false>(
|
||||
cx, target, bias, &mut pos, self, None,
|
||||
) {
|
||||
Some((prev, item, end)) => (pos, end, Some((prev, item))),
|
||||
None => (pos.clone(), pos, None),
|
||||
};
|
||||
}
|
||||
|
||||
fn find_recurse_with_prev<'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>,
|
||||
prev: Option<&'tree T>,
|
||||
) -> Option<(Option<&'tree T>, &'tree T, D)>
|
||||
where
|
||||
D: Dimension<'tree, T::Summary>,
|
||||
Target: SeekTarget<'tree, T::Summary, D>,
|
||||
{
|
||||
match &*this.0 {
|
||||
Node::Internal {
|
||||
child_summaries,
|
||||
child_trees,
|
||||
..
|
||||
} => {
|
||||
let mut prev = prev;
|
||||
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_with_prev::<D, Target, EXACT>(
|
||||
cx, target, bias, position, child_tree, prev,
|
||||
);
|
||||
}
|
||||
prev = child_tree.last();
|
||||
*position = child_end;
|
||||
}
|
||||
}
|
||||
Node::Leaf {
|
||||
items,
|
||||
item_summaries,
|
||||
..
|
||||
} => {
|
||||
let mut prev = prev;
|
||||
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((prev, item, child_end));
|
||||
}
|
||||
|
||||
prev = Some(item);
|
||||
*position = child_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn cursor<'a, 'b, D>(
|
||||
&'a self,
|
||||
cx: <T::Summary as Summary>::Context<'b>,
|
||||
|
||||
+26
-58
@@ -2248,7 +2248,6 @@ impl BufferSnapshot {
|
||||
A: 'a + IntoIterator<Item = (&'a Anchor, T)>,
|
||||
{
|
||||
let anchors = anchors.into_iter();
|
||||
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(());
|
||||
let mut fragment_cursor = self
|
||||
.fragments
|
||||
.cursor::<Dimensions<Option<&Locator>, usize>>(&None);
|
||||
@@ -2262,24 +2261,7 @@ impl BufferSnapshot {
|
||||
return (D::from_text_summary(&self.visible_text.summary()), payload);
|
||||
}
|
||||
|
||||
let anchor_key = InsertionFragmentKey {
|
||||
timestamp: anchor.timestamp,
|
||||
split_offset: anchor.offset,
|
||||
};
|
||||
insertion_cursor.seek(&anchor_key, anchor.bias);
|
||||
if let Some(insertion) = insertion_cursor.item() {
|
||||
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
|
||||
if comparison == Ordering::Greater
|
||||
|| (anchor.bias == Bias::Left
|
||||
&& comparison == Ordering::Equal
|
||||
&& anchor.offset > 0)
|
||||
{
|
||||
insertion_cursor.prev();
|
||||
}
|
||||
} else {
|
||||
insertion_cursor.prev();
|
||||
}
|
||||
let Some(insertion) = insertion_cursor.item() else {
|
||||
let Some(insertion) = self.try_find_fragment(anchor) else {
|
||||
panic!(
|
||||
"invalid insertion for buffer {}@{:?} with anchor {:?}",
|
||||
self.remote_id(),
|
||||
@@ -2328,28 +2310,8 @@ impl BufferSnapshot {
|
||||
anchor.timestamp,
|
||||
self.version
|
||||
);
|
||||
let anchor_key = InsertionFragmentKey {
|
||||
timestamp: anchor.timestamp,
|
||||
split_offset: anchor.offset,
|
||||
};
|
||||
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(());
|
||||
insertion_cursor.seek(&anchor_key, anchor.bias);
|
||||
if let Some(insertion) = insertion_cursor.item() {
|
||||
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
|
||||
if comparison == Ordering::Greater
|
||||
|| (anchor.bias == Bias::Left
|
||||
&& comparison == Ordering::Equal
|
||||
&& anchor.offset > 0)
|
||||
{
|
||||
insertion_cursor.prev();
|
||||
}
|
||||
} else {
|
||||
insertion_cursor.prev();
|
||||
}
|
||||
|
||||
let Some(insertion) = insertion_cursor
|
||||
.item()
|
||||
.filter(|insertion| insertion.timestamp == anchor.timestamp)
|
||||
let item = self.try_find_fragment(anchor);
|
||||
let Some(insertion) = item.filter(|insertion| insertion.timestamp == anchor.timestamp)
|
||||
else {
|
||||
self.panic_bad_anchor(anchor);
|
||||
};
|
||||
@@ -2401,31 +2363,37 @@ impl BufferSnapshot {
|
||||
} else if anchor.is_max() {
|
||||
Some(Locator::max_ref())
|
||||
} else {
|
||||
let anchor_key = InsertionFragmentKey {
|
||||
timestamp: anchor.timestamp,
|
||||
split_offset: anchor.offset,
|
||||
};
|
||||
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(());
|
||||
insertion_cursor.seek(&anchor_key, anchor.bias);
|
||||
if let Some(insertion) = insertion_cursor.item() {
|
||||
let item = self.try_find_fragment(anchor);
|
||||
item.filter(|insertion| {
|
||||
!cfg!(debug_assertions) || insertion.timestamp == anchor.timestamp
|
||||
})
|
||||
.map(|insertion| &insertion.fragment_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn try_find_fragment(&self, anchor: &Anchor) -> Option<&InsertionFragment> {
|
||||
let anchor_key = InsertionFragmentKey {
|
||||
timestamp: anchor.timestamp,
|
||||
split_offset: anchor.offset,
|
||||
};
|
||||
match self.insertions.find_with_prev::<InsertionFragmentKey, _>(
|
||||
(),
|
||||
&anchor_key,
|
||||
anchor.bias,
|
||||
) {
|
||||
(_, _, Some((prev, insertion))) => {
|
||||
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
|
||||
if comparison == Ordering::Greater
|
||||
|| (anchor.bias == Bias::Left
|
||||
&& comparison == Ordering::Equal
|
||||
&& anchor.offset > 0)
|
||||
{
|
||||
insertion_cursor.prev();
|
||||
prev
|
||||
} else {
|
||||
Some(insertion)
|
||||
}
|
||||
} else {
|
||||
insertion_cursor.prev();
|
||||
}
|
||||
|
||||
insertion_cursor
|
||||
.item()
|
||||
.filter(|insertion| {
|
||||
!cfg!(debug_assertions) || insertion.timestamp == anchor.timestamp
|
||||
})
|
||||
.map(|insertion| &insertion.fragment_id)
|
||||
_ => self.insertions.last(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-8
@@ -1245,14 +1245,16 @@ impl Vim {
|
||||
|
||||
if should_extend_pending {
|
||||
let snapshot = s.display_snapshot();
|
||||
if let Some(pending) = s.pending_anchor_mut() {
|
||||
let end = pending.end.to_point(&snapshot.buffer_snapshot());
|
||||
let end = end.to_display_point(&snapshot);
|
||||
let new_end = movement::right(&snapshot, end);
|
||||
pending.end = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_before(new_end.to_point(&snapshot));
|
||||
}
|
||||
s.change_with(&snapshot, |map| {
|
||||
if let Some(pending) = map.pending_anchor_mut() {
|
||||
let end = pending.end.to_point(&snapshot.buffer_snapshot());
|
||||
let end = end.to_display_point(&snapshot);
|
||||
let new_end = movement::right(&snapshot, end);
|
||||
pending.end = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_before(new_end.to_point(&snapshot));
|
||||
}
|
||||
});
|
||||
vim.extended_pending_selection_id = s.pending_anchor().map(|p| p.id)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user