Introduce `Content::anchor_range_multimap`

Antonio Scandurra created

Change summary

crates/buffer/src/anchor.rs               | 91 ++++++++++++++----------
crates/buffer/src/lib.rs                  | 55 ++++++++++++++
crates/editor/src/display_map/fold_map.rs |  2 
crates/project/src/worktree.rs            |  1 
crates/sum_tree/src/cursor.rs             | 14 +-
crates/sum_tree/src/lib.rs                |  2 
6 files changed, 115 insertions(+), 50 deletions(-)

Detailed changes

crates/buffer/src/anchor.rs 🔗

@@ -31,26 +31,26 @@ pub struct AnchorRangeMap<T> {
 pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>);
 
 pub struct AnchorRangeMultimap<T: Clone> {
-    entries: SumTree<AnchorRangeMultimapEntry<T>>,
+    pub(crate) entries: SumTree<AnchorRangeMultimapEntry<T>>,
     pub(crate) version: clock::Global,
     pub(crate) start_bias: Bias,
     pub(crate) end_bias: Bias,
 }
 
 #[derive(Clone)]
-struct AnchorRangeMultimapEntry<T> {
-    range: FullOffsetRange,
-    value: T,
+pub(crate) struct AnchorRangeMultimapEntry<T> {
+    pub(crate) range: FullOffsetRange,
+    pub(crate) value: T,
 }
 
 #[derive(Clone, Debug)]
-struct FullOffsetRange {
-    start: usize,
-    end: usize,
+pub(crate) struct FullOffsetRange {
+    pub(crate) start: usize,
+    pub(crate) end: usize,
 }
 
 #[derive(Clone, Debug)]
-struct AnchorRangeMultimapSummary {
+pub(crate) struct AnchorRangeMultimapSummary {
     start: usize,
     end: usize,
     min_start: usize,
@@ -165,46 +165,61 @@ impl AnchorRangeSet {
 }
 
 impl<T: Clone> AnchorRangeMultimap<T> {
-    fn intersecting_point_ranges<'a, O: ToOffset>(
+    fn intersecting_point_ranges<'a>(
         &'a self,
-        range: Range<O>,
-        content: impl Into<Content<'a>>,
+        range: Range<Anchor>,
+        content: &'a Content<'a>,
         inclusive: bool,
     ) -> impl Iterator<Item = (usize, Range<Point>, &T)> + 'a {
         use super::ToPoint as _;
 
-        let content = content.into();
-        let start = range.start.to_full_offset(&content, self.start_bias);
-        let end = range.end.to_full_offset(&content, self.end_bias);
         let mut cursor = self.entries.filter::<_, usize>(
-            move |summary: &AnchorRangeMultimapSummary| {
-                if inclusive {
-                    start <= summary.max_end && end >= summary.min_start
-                } else {
-                    start < summary.max_end && end > summary.min_start
+            {
+                let mut endpoint = Anchor {
+                    full_offset: 0,
+                    bias: Bias::Right,
+                    version: self.version.clone(),
+                };
+                move |summary: &AnchorRangeMultimapSummary| {
+                    endpoint.full_offset = summary.max_end;
+                    endpoint.bias = self.end_bias;
+                    let start_cmp = range.start.cmp(&endpoint, content).unwrap();
+
+                    endpoint.full_offset = summary.min_start;
+                    endpoint.bias = self.start_bias;
+                    let end_cmp = range.end.cmp(&endpoint, content).unwrap();
+
+                    if inclusive {
+                        start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
+                    } else {
+                        start_cmp == Ordering::Less && end_cmp == Ordering::Greater
+                    }
                 }
             },
             &(),
         );
-        let mut anchor = Anchor {
-            full_offset: 0,
-            bias: Bias::Left,
-            version: self.version.clone(),
-        };
-        std::iter::from_fn(move || {
-            if let Some(item) = cursor.item() {
-                let ix = *cursor.start();
-                anchor.full_offset = item.range.start;
-                anchor.bias = self.start_bias;
-                let start = anchor.to_point(&content);
-                anchor.full_offset = item.range.end;
-                anchor.bias = self.end_bias;
-                let end = anchor.to_point(&content);
-                let value = &item.value;
-                cursor.next(&());
-                Some((ix, start..end, value))
-            } else {
-                None
+
+        std::iter::from_fn({
+            let mut endpoint = Anchor {
+                full_offset: 0,
+                bias: Bias::Left,
+                version: self.version.clone(),
+            };
+            move || {
+                if let Some(item) = cursor.item() {
+                    let ix = *cursor.start();
+                    endpoint.full_offset = item.range.start;
+                    endpoint.bias = self.start_bias;
+                    let start = endpoint.to_point(content);
+                    endpoint.full_offset = item.range.end;
+                    endpoint.bias = self.end_bias;
+                    let end = endpoint.to_point(content);
+                    let value = &item.value;
+                    cursor.next(&());
+                    Some((ix, start..end, value))
+                } else {
+                    None
+                }
             }
         })
     }

crates/buffer/src/lib.rs 🔗

@@ -20,6 +20,7 @@ use rpc::proto;
 pub use selection::*;
 use std::{
     cmp,
+    collections::{BTreeMap, BTreeSet},
     convert::{TryFrom, TryInto},
     iter::Iterator,
     ops::Range,
@@ -315,7 +316,7 @@ impl UndoMap {
     }
 }
 
-struct Edits<'a, F: Fn(&FragmentSummary) -> bool> {
+struct Edits<'a, F: FnMut(&FragmentSummary) -> bool> {
     visible_text: &'a Rope,
     deleted_text: &'a Rope,
     cursor: Option<FilterCursor<'a, F, Fragment, FragmentTextSummary>>,
@@ -1836,6 +1837,56 @@ impl<'a> Content<'a> {
         AnchorRangeSet(self.anchor_range_map(entries.into_iter().map(|range| (range, ()))))
     }
 
+    pub fn anchor_range_multimap<T, E, O>(
+        &self,
+        start_bias: Bias,
+        end_bias: Bias,
+        entries: E,
+    ) -> AnchorRangeMultimap<T>
+    where
+        T: Clone,
+        E: IntoIterator<Item = (Range<O>, T)>,
+        O: ToOffset,
+    {
+        let mut items = Vec::new();
+        let mut endpoints = BTreeMap::new();
+        for (ix, (range, value)) in entries.into_iter().enumerate() {
+            items.push(AnchorRangeMultimapEntry {
+                range: FullOffsetRange { start: 0, end: 0 },
+                value,
+            });
+            endpoints
+                .entry((range.start.to_offset(self), start_bias))
+                .or_insert(Vec::new())
+                .push((ix, true));
+            endpoints
+                .entry((range.end.to_offset(self), end_bias))
+                .or_insert(Vec::new())
+                .push((ix, false));
+        }
+
+        let mut cursor = self.fragments.cursor::<FragmentTextSummary>();
+        for ((endpoint, bias), item_ixs) in endpoints {
+            cursor.seek_forward(&endpoint, bias, &None);
+            let full_offset = cursor.start().deleted + endpoint;
+            for (item_ix, is_start) in item_ixs {
+                if is_start {
+                    items[item_ix].range.start = full_offset;
+                } else {
+                    items[item_ix].range.end = full_offset;
+                }
+            }
+        }
+        items.sort_unstable_by_key(|i| (i.range.start, i.range.end));
+
+        AnchorRangeMultimap {
+            entries: SumTree::from_iter(items, &()),
+            version: self.version.clone(),
+            start_bias,
+            end_bias,
+        }
+    }
+
     fn full_offset_for_anchor(&self, anchor: &Anchor) -> usize {
         let cx = Some(anchor.version.clone());
         let mut cursor = self
@@ -1917,7 +1968,7 @@ impl<'a> RopeBuilder<'a> {
     }
 }
 
-impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> {
+impl<'a, F: FnMut(&FragmentSummary) -> bool> Iterator for Edits<'a, F> {
     type Item = Edit;
 
     fn next(&mut self) -> Option<Self::Item> {

crates/editor/src/display_map/fold_map.rs 🔗

@@ -720,7 +720,7 @@ fn intersecting_folds<'a, T>(
     folds: &'a SumTree<Fold>,
     range: Range<T>,
     inclusive: bool,
-) -> FilterCursor<'a, impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>
+) -> FilterCursor<'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize>
 where
     T: ToOffset,
 {

crates/project/src/worktree.rs 🔗

@@ -1190,7 +1190,6 @@ impl LocalWorktree {
         diagnostics: lsp::PublishDiagnosticsParams,
         cx: &mut ModelContext<Worktree>,
     ) {
-        //
     }
 }
 

crates/sum_tree/src/cursor.rs 🔗

@@ -184,9 +184,9 @@ where
         self.next_internal(|_| true, cx)
     }
 
-    fn next_internal<F>(&mut self, filter_node: F, cx: &<T::Summary as Summary>::Context)
+    fn next_internal<F>(&mut self, mut filter_node: F, cx: &<T::Summary as Summary>::Context)
     where
-        F: Fn(&T::Summary) -> bool,
+        F: FnMut(&T::Summary) -> bool,
     {
         let mut descend = false;
 
@@ -509,24 +509,24 @@ where
     }
 }
 
-pub struct FilterCursor<'a, F: Fn(&T::Summary) -> bool, T: Item, D> {
+pub struct FilterCursor<'a, F: FnMut(&T::Summary) -> bool, T: Item, D> {
     cursor: Cursor<'a, T, D>,
     filter_node: F,
 }
 
 impl<'a, F, T, D> FilterCursor<'a, F, T, D>
 where
-    F: Fn(&T::Summary) -> bool,
+    F: FnMut(&T::Summary) -> bool,
     T: Item,
     D: Dimension<'a, T::Summary>,
 {
     pub fn new(
         tree: &'a SumTree<T>,
-        filter_node: F,
+        mut filter_node: F,
         cx: &<T::Summary as Summary>::Context,
     ) -> Self {
         let mut cursor = tree.cursor::<D>();
-        cursor.next_internal(&filter_node, cx);
+        cursor.next_internal(&mut filter_node, cx);
         Self {
             cursor,
             filter_node,
@@ -542,7 +542,7 @@ where
     }
 
     pub fn next(&mut self, cx: &<T::Summary as Summary>::Context) {
-        self.cursor.next_internal(&self.filter_node, cx);
+        self.cursor.next_internal(&mut self.filter_node, cx);
     }
 }
 

crates/sum_tree/src/lib.rs 🔗

@@ -163,7 +163,7 @@ impl<T: Item> SumTree<T> {
         cx: &<T::Summary as Summary>::Context,
     ) -> FilterCursor<F, T, U>
     where
-        F: Fn(&T::Summary) -> bool,
+        F: FnMut(&T::Summary) -> bool,
         U: Dimension<'a, T::Summary>,
     {
         FilterCursor::new(self, filter_node, cx)