Merge branch 'master' into new-file

Max Brunsfeld created

Change summary

Cargo.toml                             |   3 
zed/src/editor/buffer/mod.rs           | 294 +++++++++++-------
zed/src/editor/buffer/selection.rs     |   4 
zed/src/editor/buffer/text.rs          |  32 +
zed/src/editor/buffer_view.rs          | 209 ++++++++++++
zed/src/editor/display_map/fold_map.rs | 436 +++++++++++++++++++--------
zed/src/editor/display_map/mod.rs      |   2 
zed/src/operation_queue.rs             |  16 
zed/src/sum_tree/cursor.rs             | 292 ++++++++---------
zed/src/sum_tree/mod.rs                | 248 +++++++++------
zed/src/util.rs                        |  39 --
zed/src/worktree.rs                    |  43 +-
12 files changed, 1,025 insertions(+), 593 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -12,3 +12,6 @@ core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "025
 
 [profile.dev]
 split-debuginfo = "unpacked"
+
+[profile.release]
+debug = true

zed/src/editor/buffer/mod.rs 🔗

@@ -393,39 +393,48 @@ impl Buffer {
 
         insertion_splits.insert(
             base_insertion.id,
-            SumTree::from_item(InsertionSplit {
-                fragment_id: FragmentId::min_value().clone(),
-                extent: 0,
-            }),
+            SumTree::from_item(
+                InsertionSplit {
+                    fragment_id: FragmentId::min_value().clone(),
+                    extent: 0,
+                },
+                &(),
+            ),
+        );
+        fragments.push(
+            Fragment {
+                id: FragmentId::min_value().clone(),
+                insertion: base_insertion.clone(),
+                text: base_insertion.text.slice(0..0),
+                deletions: Default::default(),
+                max_undos: Default::default(),
+                visible: true,
+            },
+            &(),
         );
-        fragments.push(Fragment {
-            id: FragmentId::min_value().clone(),
-            insertion: base_insertion.clone(),
-            text: base_insertion.text.slice(0..0),
-            deletions: Default::default(),
-            max_undos: Default::default(),
-            visible: true,
-        });
 
         if base_insertion.text.len() > 0 {
             let base_fragment_id =
                 FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value());
 
-            insertion_splits
-                .get_mut(&base_insertion.id)
-                .unwrap()
-                .push(InsertionSplit {
+            insertion_splits.get_mut(&base_insertion.id).unwrap().push(
+                InsertionSplit {
                     fragment_id: base_fragment_id.clone(),
                     extent: base_insertion.text.len(),
-                });
-            fragments.push(Fragment {
-                id: base_fragment_id,
-                text: base_insertion.text.clone(),
-                insertion: base_insertion,
-                deletions: Default::default(),
-                max_undos: Default::default(),
-                visible: true,
-            });
+                },
+                &(),
+            );
+            fragments.push(
+                Fragment {
+                    id: base_fragment_id,
+                    text: base_insertion.text.clone(),
+                    insertion: base_insertion,
+                    deletions: Default::default(),
+                    max_undos: Default::default(),
+                    visible: true,
+                },
+                &(),
+            );
         }
 
         Self {
@@ -507,22 +516,22 @@ impl Buffer {
         let mut summary = TextSummary::default();
 
         let mut cursor = self.fragments.cursor::<usize, usize>();
-        cursor.seek(&range.start, SeekBias::Right);
+        cursor.seek(&range.start, SeekBias::Right, &());
 
         if let Some(fragment) = cursor.item() {
             let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
             let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
-            summary += &fragment.text.slice(summary_start..summary_end).summary();
+            summary += fragment.text.slice(summary_start..summary_end).summary();
             cursor.next();
         }
 
         if range.end > *cursor.start() {
-            summary += &cursor.summary::<TextSummary>(&range.end, SeekBias::Right);
+            summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
 
             if let Some(fragment) = cursor.item() {
                 let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
                 let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
-                summary += &fragment.text.slice(summary_start..summary_end).summary();
+                summary += fragment.text.slice(summary_start..summary_end).summary();
             }
         }
 
@@ -552,22 +561,22 @@ impl Buffer {
         let mut summary = TextSummary::default();
 
         let mut cursor = self.fragments.cursor::<usize, usize>();
-        cursor.seek(&range.start, SeekBias::Right);
+        cursor.seek(&range.start, SeekBias::Right, &());
 
         if let Some(fragment) = cursor.item() {
             let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
             let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
-            summary += &fragment.text.slice(summary_start..summary_end).summary();
+            summary += fragment.text.slice(summary_start..summary_end).summary();
             cursor.next();
         }
 
         if range.end > *cursor.start() {
-            summary += &cursor.summary::<TextSummary>(&range.end, SeekBias::Right);
+            summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
 
             if let Some(fragment) = cursor.item() {
                 let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
                 let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
-                summary += &fragment.text.slice(summary_start..summary_end).summary();
+                summary += fragment.text.slice(summary_start..summary_end).summary();
             }
         }
 
@@ -970,10 +979,10 @@ impl Buffer {
 
         let mut cursor = old_fragments.cursor::<FragmentIdRef, ()>();
         let mut new_fragments =
-            cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left);
+            cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
 
         if start_offset == cursor.item().unwrap().end_offset() {
-            new_fragments.push(cursor.item().unwrap().clone());
+            new_fragments.push(cursor.item().unwrap().clone(), &());
             cursor.next();
         }
 
@@ -1012,30 +1021,33 @@ impl Buffer {
                     None
                 };
                 if let Some(fragment) = before_range {
-                    new_fragments.push(fragment);
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(fragment) = insertion {
-                    new_fragments.push(fragment);
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(mut fragment) = within_range {
                     if fragment.was_visible(&version_in_range, &self.undo_map) {
                         fragment.deletions.insert(local_timestamp);
                         fragment.visible = false;
                     }
-                    new_fragments.push(fragment);
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(fragment) = after_range {
-                    new_fragments.push(fragment);
+                    new_fragments.push(fragment, &());
                 }
             } else {
                 if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
-                    new_fragments.push(self.build_fragment_to_insert(
-                        cursor.prev_item().as_ref().unwrap(),
-                        Some(&fragment),
-                        new_text.take().unwrap(),
-                        local_timestamp,
-                        lamport_timestamp,
-                    ));
+                    new_fragments.push(
+                        self.build_fragment_to_insert(
+                            cursor.prev_item().as_ref().unwrap(),
+                            Some(&fragment),
+                            new_text.take().unwrap(),
+                            local_timestamp,
+                            lamport_timestamp,
+                        ),
+                        &(),
+                    );
                 }
 
                 if fragment.id < end_fragment_id
@@ -1044,23 +1056,26 @@ impl Buffer {
                     fragment.deletions.insert(local_timestamp);
                     fragment.visible = false;
                 }
-                new_fragments.push(fragment);
+                new_fragments.push(fragment, &());
             }
 
             cursor.next();
         }
 
         if let Some(new_text) = new_text {
-            new_fragments.push(self.build_fragment_to_insert(
-                cursor.prev_item().as_ref().unwrap(),
-                None,
-                new_text,
-                local_timestamp,
-                lamport_timestamp,
-            ));
+            new_fragments.push(
+                self.build_fragment_to_insert(
+                    cursor.prev_item().as_ref().unwrap(),
+                    None,
+                    new_text,
+                    local_timestamp,
+                    lamport_timestamp,
+                ),
+                &(),
+            );
         }
 
-        new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right));
+        new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
         self.fragments = new_fragments;
         self.local_clock.observe(local_timestamp);
         self.lamport_clock.observe(lamport_timestamp);
@@ -1148,23 +1163,26 @@ impl Buffer {
             let mut insertion_splits = splits.cursor::<(), ()>().map(|s| &s.fragment_id).peekable();
 
             let first_split_id = insertion_splits.next().unwrap();
-            new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left);
+            new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &());
 
             loop {
                 let mut fragment = cursor.item().unwrap().clone();
                 fragment.visible = fragment.is_visible(&self.undo_map);
                 fragment.max_undos.observe(undo.id);
-                new_fragments.push(fragment);
+                new_fragments.push(fragment, &());
                 cursor.next();
                 if let Some(split_id) = insertion_splits.next() {
-                    new_fragments
-                        .push_tree(cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left));
+                    new_fragments.push_tree(
+                        cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()),
+                        &(),
+                    );
                 } else {
                     break;
                 }
             }
         } else {
-            new_fragments = cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left);
+            new_fragments =
+                cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
             while let Some(fragment) = cursor.item() {
                 if fragment.id > end_fragment_id {
                     break;
@@ -1176,13 +1194,13 @@ impl Buffer {
                         fragment.visible = fragment.is_visible(&self.undo_map);
                         fragment.max_undos.observe(undo.id);
                     }
-                    new_fragments.push(fragment);
+                    new_fragments.push(fragment, &());
                     cursor.next();
                 }
             }
         }
 
-        new_fragments.push_tree(cursor.suffix());
+        new_fragments.push_tree(cursor.suffix(&()), &());
         drop(cursor);
         self.fragments = new_fragments;
 
@@ -1246,7 +1264,7 @@ impl Buffer {
             .get(&edit_id)
             .ok_or_else(|| anyhow!("invalid operation"))?;
         let mut cursor = split_tree.cursor::<usize, ()>();
-        cursor.seek(&offset, SeekBias::Left);
+        cursor.seek(&offset, SeekBias::Left, &());
         Ok(cursor
             .item()
             .ok_or_else(|| anyhow!("invalid operation"))?
@@ -1268,7 +1286,10 @@ impl Buffer {
         let old_fragments = self.fragments.clone();
         let mut cursor = old_fragments.cursor::<usize, usize>();
         let mut new_fragments = SumTree::new();
-        new_fragments.push_tree(cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right));
+        new_fragments.push_tree(
+            cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
+            &(),
+        );
 
         let mut start_id = None;
         let mut start_offset = None;
@@ -1290,7 +1311,8 @@ impl Buffer {
                 .remove(&fragment.insertion.id)
                 .unwrap();
             let mut splits_cursor = old_split_tree.cursor::<usize, ()>();
-            let mut new_split_tree = splits_cursor.slice(&fragment.start_offset(), SeekBias::Right);
+            let mut new_split_tree =
+                splits_cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
 
             // Find all splices that start or end within the current fragment. Then, split the
             // fragment and reassemble it in both trees accounting for the deleted and the newly
@@ -1303,11 +1325,14 @@ impl Buffer {
                     prefix.id =
                         FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
                     fragment.set_start_offset(prefix.end_offset());
-                    new_fragments.push(prefix.clone());
-                    new_split_tree.push(InsertionSplit {
-                        extent: prefix.end_offset() - prefix.start_offset(),
-                        fragment_id: prefix.id,
-                    });
+                    new_fragments.push(prefix.clone(), &());
+                    new_split_tree.push(
+                        InsertionSplit {
+                            extent: prefix.end_offset() - prefix.start_offset(),
+                            fragment_id: prefix.id,
+                        },
+                        &(),
+                    );
                     fragment_start = range.start;
                 }
 
@@ -1331,7 +1356,7 @@ impl Buffer {
                             local_timestamp,
                             lamport_timestamp,
                         );
-                        new_fragments.push(new_fragment);
+                        new_fragments.push(new_fragment, &());
                     }
                 }
 
@@ -1347,11 +1372,14 @@ impl Buffer {
                             prefix.visible = false;
                         }
                         fragment.set_start_offset(prefix.end_offset());
-                        new_fragments.push(prefix.clone());
-                        new_split_tree.push(InsertionSplit {
-                            extent: prefix.end_offset() - prefix.start_offset(),
-                            fragment_id: prefix.id,
-                        });
+                        new_fragments.push(prefix.clone(), &());
+                        new_split_tree.push(
+                            InsertionSplit {
+                                extent: prefix.end_offset() - prefix.start_offset(),
+                                fragment_id: prefix.id,
+                            },
+                            &(),
+                        );
                         fragment_start = range.end;
                         end_id = Some(fragment.insertion.id);
                         end_offset = Some(fragment.start_offset());
@@ -1395,16 +1423,21 @@ impl Buffer {
                     break;
                 }
             }
-            new_split_tree.push(InsertionSplit {
-                extent: fragment.end_offset() - fragment.start_offset(),
-                fragment_id: fragment.id.clone(),
-            });
+            new_split_tree.push(
+                InsertionSplit {
+                    extent: fragment.end_offset() - fragment.start_offset(),
+                    fragment_id: fragment.id.clone(),
+                },
+                &(),
+            );
             splits_cursor.next();
-            new_split_tree
-                .push_tree(splits_cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right));
+            new_split_tree.push_tree(
+                splits_cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right, &()),
+                &(),
+            );
             self.insertion_splits
                 .insert(fragment.insertion.id, new_split_tree);
-            new_fragments.push(fragment);
+            new_fragments.push(fragment, &());
 
             // Scan forward until we find a fragment that is not fully contained by the current splice.
             cursor.next();
@@ -1420,7 +1453,7 @@ impl Buffer {
                             new_fragment.deletions.insert(local_timestamp);
                             new_fragment.visible = false;
                         }
-                        new_fragments.push(new_fragment);
+                        new_fragments.push(new_fragment, &());
                         cursor.next();
 
                         if range.end == fragment_end {
@@ -1462,7 +1495,8 @@ impl Buffer {
                 // and push all the fragments in between into the new tree.
                 if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) {
                     new_fragments.push_tree(
-                        cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right),
+                        cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
+                        &(),
                     );
                 }
             }
@@ -1494,11 +1528,13 @@ impl Buffer {
                     local_timestamp,
                     lamport_timestamp,
                 );
-                new_fragments.push(new_fragment);
+                new_fragments.push(new_fragment, &());
             }
         } else {
-            new_fragments
-                .push_tree(cursor.slice(&old_fragments.extent::<usize>(), SeekBias::Right));
+            new_fragments.push_tree(
+                cursor.slice(&old_fragments.extent::<usize>(), SeekBias::Right, &()),
+                &(),
+            );
         }
 
         self.fragments = new_fragments;
@@ -1556,32 +1592,43 @@ impl Buffer {
                 .remove(&fragment.insertion.id)
                 .unwrap();
             let mut cursor = old_split_tree.cursor::<usize, ()>();
-            let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right);
+            let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
 
             if let Some(ref fragment) = before_range {
-                new_split_tree.push(InsertionSplit {
-                    extent: range.start - fragment.start_offset(),
-                    fragment_id: fragment.id.clone(),
-                });
+                new_split_tree.push(
+                    InsertionSplit {
+                        extent: range.start - fragment.start_offset(),
+                        fragment_id: fragment.id.clone(),
+                    },
+                    &(),
+                );
             }
 
             if let Some(ref fragment) = within_range {
-                new_split_tree.push(InsertionSplit {
-                    extent: range.end - range.start,
-                    fragment_id: fragment.id.clone(),
-                });
+                new_split_tree.push(
+                    InsertionSplit {
+                        extent: range.end - range.start,
+                        fragment_id: fragment.id.clone(),
+                    },
+                    &(),
+                );
             }
 
             if let Some(ref fragment) = after_range {
-                new_split_tree.push(InsertionSplit {
-                    extent: fragment.end_offset() - range.end,
-                    fragment_id: fragment.id.clone(),
-                });
+                new_split_tree.push(
+                    InsertionSplit {
+                        extent: fragment.end_offset() - range.end,
+                        fragment_id: fragment.id.clone(),
+                    },
+                    &(),
+                );
             }
 
             cursor.next();
-            new_split_tree
-                .push_tree(cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right));
+            new_split_tree.push_tree(
+                cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right, &()),
+                &(),
+            );
 
             self.insertion_splits
                 .insert(fragment.insertion.id, new_split_tree);
@@ -1606,10 +1653,13 @@ impl Buffer {
         );
 
         let mut split_tree = SumTree::new();
-        split_tree.push(InsertionSplit {
-            extent: text.len(),
-            fragment_id: new_fragment_id.clone(),
-        });
+        split_tree.push(
+            InsertionSplit {
+                extent: text.len(),
+                fragment_id: new_fragment_id.clone(),
+            },
+            &(),
+        );
         self.insertion_splits.insert(local_timestamp, split_tree);
 
         Fragment::new(
@@ -1658,7 +1708,7 @@ impl Buffer {
         };
 
         let mut cursor = self.fragments.cursor::<usize, usize>();
-        cursor.seek(&offset, seek_bias);
+        cursor.seek(&offset, seek_bias, &());
         let fragment = cursor.item().unwrap();
         let offset_in_fragment = offset - cursor.start();
         let offset_in_insertion = fragment.start_offset() + offset_in_fragment;
@@ -1690,7 +1740,7 @@ impl Buffer {
                     .get(&insertion_id)
                     .ok_or_else(|| anyhow!("split does not exist for insertion id"))?;
                 let mut splits_cursor = splits.cursor::<usize, ()>();
-                splits_cursor.seek(offset, seek_bias);
+                splits_cursor.seek(offset, seek_bias, &());
                 splits_cursor
                     .item()
                     .ok_or_else(|| anyhow!("split offset is out of range"))
@@ -1718,13 +1768,13 @@ impl Buffer {
                     .get(&insertion_id)
                     .ok_or_else(|| anyhow!("split does not exist for insertion id"))?;
                 let mut splits_cursor = splits.cursor::<usize, ()>();
-                splits_cursor.seek(offset, seek_bias);
+                splits_cursor.seek(offset, seek_bias, &());
                 let split = splits_cursor
                     .item()
                     .ok_or_else(|| anyhow!("split offset is out of range"))?;
 
                 let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, TextSummary>();
-                fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left);
+                fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &());
                 let fragment = fragments_cursor
                     .item()
                     .ok_or_else(|| anyhow!("fragment id does not exist"))?;
@@ -1744,7 +1794,7 @@ impl Buffer {
     #[allow(dead_code)]
     pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
         let mut fragments_cursor = self.fragments.cursor::<usize, TextSummary>();
-        fragments_cursor.seek(&offset, SeekBias::Left);
+        fragments_cursor.seek(&offset, SeekBias::Left, &());
         fragments_cursor
             .item()
             .ok_or_else(|| anyhow!("offset is out of range"))
@@ -1810,7 +1860,7 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point {
 impl<'a> CharIter<'a> {
     fn new(fragments: &'a SumTree<Fragment>, offset: usize) -> Self {
         let mut fragments_cursor = fragments.cursor::<usize, usize>();
-        fragments_cursor.seek(&offset, SeekBias::Right);
+        fragments_cursor.seek(&offset, SeekBias::Right, &());
         let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| {
             let offset_in_fragment = offset - fragments_cursor.start();
             fragment.text[offset_in_fragment..].chars()
@@ -1847,7 +1897,7 @@ impl<'a> Iterator for CharIter<'a> {
 impl<'a> FragmentIter<'a> {
     fn new(fragments: &'a SumTree<Fragment>) -> Self {
         let mut cursor = fragments.cursor::<usize, usize>();
-        cursor.seek(&0, SeekBias::Right);
+        cursor.seek(&0, SeekBias::Right, &());
         Self {
             cursor,
             started: false,
@@ -2140,8 +2190,10 @@ impl sum_tree::Item for Fragment {
     }
 }
 
-impl<'a> AddAssign<&'a FragmentSummary> for FragmentSummary {
-    fn add_assign(&mut self, other: &Self) {
+impl sum_tree::Summary for FragmentSummary {
+    type Context = ();
+
+    fn add_summary(&mut self, other: &Self, _: &()) {
         self.text_summary += &other.text_summary;
         debug_assert!(self.max_fragment_id <= other.max_fragment_id);
         self.max_fragment_id = other.max_fragment_id.clone();
@@ -2204,8 +2256,10 @@ impl sum_tree::Item for InsertionSplit {
     }
 }
 
-impl<'a> AddAssign<&'a InsertionSplitSummary> for InsertionSplitSummary {
-    fn add_assign(&mut self, other: &Self) {
+impl sum_tree::Summary for InsertionSplitSummary {
+    type Context = ();
+
+    fn add_summary(&mut self, other: &Self, _: &()) {
         self.extent += other.extent;
     }
 }
@@ -2262,7 +2316,7 @@ pub trait ToOffset {
 impl ToOffset for Point {
     fn to_offset(&self, buffer: &Buffer) -> Result<usize> {
         let mut fragments_cursor = buffer.fragments.cursor::<Point, TextSummary>();
-        fragments_cursor.seek(self, SeekBias::Left);
+        fragments_cursor.seek(self, SeekBias::Left, &());
         fragments_cursor
             .item()
             .ok_or_else(|| anyhow!("point is out of range"))
@@ -2306,7 +2360,7 @@ impl ToPoint for Anchor {
 impl ToPoint for usize {
     fn to_point(&self, buffer: &Buffer) -> Result<Point> {
         let mut fragments_cursor = buffer.fragments.cursor::<usize, TextSummary>();
-        fragments_cursor.seek(&self, SeekBias::Left);
+        fragments_cursor.seek(&self, SeekBias::Left, &());
         fragments_cursor
             .item()
             .ok_or_else(|| anyhow!("offset is out of range"))

zed/src/editor/buffer/selection.rs 🔗

@@ -75,6 +75,7 @@ impl Selection {
 
     pub fn buffer_rows_for_display_rows(
         &self,
+        include_end_if_at_line_start: bool,
         map: &DisplayMap,
         ctx: &AppContext,
     ) -> (Range<u32>, Range<u32>) {
@@ -84,7 +85,8 @@ impl Selection {
             .unwrap();
 
         let mut display_end = self.end.to_display_point(map, ctx).unwrap();
-        if display_end.row() != map.max_point(ctx).row()
+        if !include_end_if_at_line_start
+            && display_end.row() != map.max_point(ctx).row()
             && display_start.row() != display_end.row()
             && display_end.column() == 0
         {

zed/src/editor/buffer/text.rs 🔗

@@ -58,6 +58,14 @@ pub struct TextSummary {
     pub rightmost_point: Point,
 }
 
+impl sum_tree::Summary for TextSummary {
+    type Context = ();
+
+    fn add_summary(&mut self, other: &Self, _: &()) {
+        *self += other;
+    }
+}
+
 impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
     fn add_assign(&mut self, other: &'a Self) {
         let joined_line_len = self.lines.column + other.first_line_len;
@@ -85,8 +93,8 @@ impl std::ops::AddAssign<Self> for TextSummary {
 }
 
 impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary {
-    fn add_summary(&mut self, summary: &TextSummary) {
-        *self += summary;
+    fn add_summary(&mut self, other: &TextSummary) {
+        *self += other;
     }
 }
 
@@ -157,7 +165,7 @@ impl From<Arc<str>> for Text {
         }
 
         let mut tree = SumTree::new();
-        tree.extend(runs);
+        tree.extend(runs, &());
         Text {
             text,
             runs: tree,
@@ -231,13 +239,14 @@ impl Text {
 
     pub fn line_len(&self, row: u32) -> u32 {
         let mut cursor = self.runs.cursor::<usize, Point>();
-        cursor.seek(&self.range.start, SeekBias::Right);
+        cursor.seek(&self.range.start, SeekBias::Right, &());
         let absolute_row = cursor.start().row + row;
 
         let mut cursor = self.runs.cursor::<Point, usize>();
-        cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right);
+        cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right, &());
         let prefix_len = self.range.start.saturating_sub(*cursor.start());
-        let line_len = cursor.summary::<usize>(&Point::new(absolute_row + 1, 0), SeekBias::Left);
+        let line_len =
+            cursor.summary::<usize>(&Point::new(absolute_row + 1, 0), SeekBias::Left, &());
         let suffix_len = cursor.start().saturating_sub(self.range.end);
 
         line_len
@@ -262,14 +271,15 @@ impl Text {
             candidates.push(Point::new(0, self.line_len(0)));
             if lines.row > 1 {
                 let mut cursor = self.runs.cursor::<usize, Point>();
-                cursor.seek(&self.range.start, SeekBias::Right);
+                cursor.seek(&self.range.start, SeekBias::Right, &());
                 let absolute_start_row = cursor.start().row;
 
                 let mut cursor = self.runs.cursor::<Point, usize>();
-                cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right);
+                cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right, &());
                 let summary = cursor.summary::<TextSummary>(
                     &Point::new(absolute_start_row + lines.row, 0),
                     SeekBias::Left,
+                    &(),
                 );
 
                 candidates.push(Point::new(1, 0) + &summary.rightmost_point);
@@ -287,7 +297,7 @@ impl Text {
     pub fn offset_for_point(&self, point: Point) -> usize {
         let mut cursor = self.runs.cursor::<Point, TextSummary>();
         let abs_point = self.abs_point_for_offset(self.range.start) + &point;
-        cursor.seek(&abs_point, SeekBias::Right);
+        cursor.seek(&abs_point, SeekBias::Right, &());
         let overshoot = abs_point - &cursor.start().lines;
         let abs_offset = cursor.start().chars + overshoot.column as usize;
         abs_offset - self.range.start
@@ -307,14 +317,14 @@ impl Text {
 
     fn abs_point_for_offset(&self, offset: usize) -> Point {
         let mut cursor = self.runs.cursor::<usize, TextSummary>();
-        cursor.seek(&offset, SeekBias::Right);
+        cursor.seek(&offset, SeekBias::Right, &());
         let overshoot = (offset - cursor.start().chars) as u32;
         cursor.start().lines + &Point::new(0, overshoot)
     }
 
     fn abs_byte_offset_for_offset(&self, offset: usize) -> usize {
         let mut cursor = self.runs.cursor::<usize, TextSummary>();
-        cursor.seek(&offset, SeekBias::Right);
+        cursor.seek(&offset, SeekBias::Right, &());
         let overshoot = offset - cursor.start().chars;
         cursor.start().bytes + overshoot * cursor.item().map_or(0, |run| run.char_size()) as usize
     }

zed/src/editor/buffer_view.rs 🔗

@@ -138,6 +138,12 @@ pub fn init(app: &mut MutableAppContext) {
         ),
         Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
         Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
+        Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
+        Binding::new(
+            "cmd-shift-L",
+            "buffer:split_selection_into_lines",
+            Some("BufferView"),
+        ),
         Binding::new("pageup", "buffer:page_up", Some("BufferView")),
         Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
         Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
@@ -228,6 +234,11 @@ pub fn init(app: &mut MutableAppContext) {
     );
     app.add_action("buffer:select_to_end", BufferView::select_to_end);
     app.add_action("buffer:select_all", BufferView::select_all);
+    app.add_action("buffer:select_line", BufferView::select_line);
+    app.add_action(
+        "buffer:split_selection_into_lines",
+        BufferView::split_selection_into_lines,
+    );
     app.add_action("buffer:page_up", BufferView::page_up);
     app.add_action("buffer:page_down", BufferView::page_down);
     app.add_action("buffer:fold", BufferView::fold);
@@ -704,7 +715,8 @@ impl BufferView {
 
         let mut selections = self.selections(app).iter().peekable();
         while let Some(selection) = selections.next() {
-            let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
+            let (mut rows, _) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
             let goal_display_column = selection
                 .head()
                 .to_display_point(&self.display_map, app)
@@ -714,7 +726,7 @@ impl BufferView {
             // Accumulate contiguous regions of rows that we want to delete.
             while let Some(next_selection) = selections.peek() {
                 let (next_rows, _) =
-                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
                 if next_rows.start <= rows.end {
                     rows.end = next_rows.end;
                     selections.next().unwrap();
@@ -795,10 +807,11 @@ impl BufferView {
         let mut selections_iter = selections.iter_mut().peekable();
         while let Some(selection) = selections_iter.next() {
             // Avoid duplicating the same lines twice.
-            let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
+            let (mut rows, _) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
             while let Some(next_selection) = selections_iter.peek() {
                 let (next_rows, _) =
-                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
                 if next_rows.start <= rows.end - 1 {
                     rows.end = next_rows.end;
                     selections_iter.next().unwrap();
@@ -852,10 +865,10 @@ impl BufferView {
             // Accumulate contiguous regions of rows that we want to move.
             contiguous_selections.push(selection.range(buffer));
             let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(&self.display_map, app);
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
             while let Some(next_selection) = selections.peek() {
                 let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
@@ -942,10 +955,10 @@ impl BufferView {
             // Accumulate contiguous regions of rows that we want to move.
             contiguous_selections.push(selection.range(buffer));
             let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(&self.display_map, app);
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
             while let Some(next_selection) = selections.peek() {
                 let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
@@ -1654,6 +1667,63 @@ impl BufferView {
         self.update_selections(vec![selection], false, ctx);
     }
 
+    pub fn select_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+        let mut selections = self.selections(app).to_vec();
+        let max_point = buffer.max_point();
+        for selection in &mut selections {
+            let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app);
+            selection.start = buffer.anchor_before(Point::new(rows.start, 0)).unwrap();
+            selection.end = buffer
+                .anchor_before(cmp::min(max_point, Point::new(rows.end, 0)))
+                .unwrap();
+            selection.reversed = false;
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        use super::RangeExt;
+
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+
+        let mut to_unfold = Vec::new();
+        let mut new_selections = Vec::new();
+        for selection in self.selections(app) {
+            let range = selection.range(buffer).sorted();
+            if range.start.row != range.end.row {
+                new_selections.push(Selection {
+                    start: selection.start.clone(),
+                    end: selection.start.clone(),
+                    reversed: false,
+                    goal_column: None,
+                });
+            }
+            for row in range.start.row + 1..range.end.row {
+                let cursor = buffer
+                    .anchor_before(Point::new(row, buffer.line_len(row).unwrap()))
+                    .unwrap();
+                new_selections.push(Selection {
+                    start: cursor.clone(),
+                    end: cursor,
+                    reversed: false,
+                    goal_column: None,
+                });
+            }
+            new_selections.push(Selection {
+                start: selection.end.clone(),
+                end: selection.end.clone(),
+                reversed: false,
+                goal_column: None,
+            });
+            to_unfold.push(range);
+        }
+        self.unfold_ranges(to_unfold, ctx);
+        self.update_selections(new_selections, true, ctx);
+    }
+
     pub fn selections_in_range<'a>(
         &'a self,
         range: Range<DisplayPoint>,
@@ -1874,9 +1944,8 @@ impl BufferView {
             .selections(ctx.as_ref())
             .iter()
             .map(|s| s.range(buffer).sorted())
-            .collect::<Vec<_>>();
-        self.display_map.fold(ranges, ctx.as_ref()).unwrap();
-        ctx.notify();
+            .collect();
+        self.fold_ranges(ranges, ctx);
     }
 
     fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
@@ -3292,6 +3361,124 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_select_line() {
+        App::test((), |app| {
+            let settings = settings::channel(&app.font_cache()).unwrap().1;
+            let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx));
+            let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
+            view.update(app, |view, ctx| {
+                view.select_display_ranges(
+                    &[
+                        DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+                        DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                        DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                        DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
+                    ],
+                    ctx,
+                )
+                .unwrap();
+                view.select_line(&(), ctx);
+            });
+            assert_eq!(
+                view.read(app).selection_ranges(app.as_ref()),
+                vec![
+                    DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
+                    DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
+                ]
+            );
+
+            view.update(app, |view, ctx| view.select_line(&(), ctx));
+            assert_eq!(
+                view.read(app).selection_ranges(app.as_ref()),
+                vec![
+                    DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
+                    DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
+                ]
+            );
+
+            view.update(app, |view, ctx| view.select_line(&(), ctx));
+            assert_eq!(
+                view.read(app).selection_ranges(app.as_ref()),
+                vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
+            );
+        });
+    }
+
+    #[test]
+    fn test_split_selection_into_lines() {
+        App::test((), |app| {
+            let settings = settings::channel(&app.font_cache()).unwrap().1;
+            let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx));
+            let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
+            view.update(app, |view, ctx| {
+                view.fold_ranges(
+                    vec![
+                        Point::new(0, 2)..Point::new(1, 2),
+                        Point::new(2, 3)..Point::new(4, 1),
+                        Point::new(7, 0)..Point::new(8, 4),
+                    ],
+                    ctx,
+                );
+                view.select_display_ranges(
+                    &[
+                        DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+                        DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                        DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                        DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
+                    ],
+                    ctx,
+                )
+                .unwrap();
+            });
+            assert_eq!(
+                view.read(app).text(app.as_ref()),
+                "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
+            );
+
+            view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx));
+            assert_eq!(
+                view.read(app).text(app.as_ref()),
+                "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
+            );
+            assert_eq!(
+                view.read(app).selection_ranges(app.as_ref()),
+                [
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2)
+                ]
+            );
+
+            view.update(app, |view, ctx| {
+                view.select_display_ranges(
+                    &[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)],
+                    ctx,
+                )
+                .unwrap();
+                view.split_selection_into_lines(&(), ctx);
+            });
+            assert_eq!(
+                view.read(app).text(app.as_ref()),
+                "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i"
+            );
+            assert_eq!(
+                view.read(app).selection_ranges(app.as_ref()),
+                [
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+                    DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
+                    DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
+                    DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
+                    DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
+                    DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
+                    DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
+                ]
+            );
+        });
+    }
+
     impl BufferView {
         fn selection_ranges(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
             self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)

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

@@ -1,10 +1,10 @@
 use super::{
-    buffer, Anchor, AnchorRangeExt, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
+    buffer::{self, AnchorRangeExt},
+    Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
 };
 use crate::{
-    sum_tree::{self, Cursor, SumTree},
+    sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
     time,
-    util::find_insertion_index,
 };
 use anyhow::{anyhow, Result};
 use gpui::{AppContext, ModelHandle};
@@ -14,12 +14,11 @@ use std::{
     iter::Take,
     ops::Range,
 };
-use sum_tree::{Dimension, SeekBias};
 
 pub struct FoldMap {
     buffer: ModelHandle<Buffer>,
     transforms: Mutex<SumTree<Transform>>,
-    folds: Vec<Range<Anchor>>,
+    folds: SumTree<Fold>,
     last_sync: Mutex<time::Global>,
 }
 
@@ -29,14 +28,17 @@ impl FoldMap {
         let text_summary = buffer.text_summary();
         Self {
             buffer: buffer_handle,
-            folds: Vec::new(),
-            transforms: Mutex::new(SumTree::from_item(Transform {
-                summary: TransformSummary {
-                    buffer: text_summary.clone(),
-                    display: text_summary,
+            folds: Default::default(),
+            transforms: Mutex::new(SumTree::from_item(
+                Transform {
+                    summary: TransformSummary {
+                        buffer: text_summary.clone(),
+                        display: text_summary,
+                    },
+                    display_text: None,
                 },
-                display_text: None,
-            })),
+                &(),
+            )),
             last_sync: Mutex::new(buffer.version()),
         }
     }
@@ -76,17 +78,12 @@ impl FoldMap {
     pub fn folds_in_range<'a, T>(
         &'a self,
         range: Range<T>,
-        app: &'a AppContext,
+        ctx: &'a AppContext,
     ) -> Result<impl Iterator<Item = &'a Range<Anchor>>>
     where
         T: ToOffset,
     {
-        let buffer = self.buffer.read(app);
-        let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
-        Ok(self.folds.iter().filter(move |fold| {
-            range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less
-                && range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater
-        }))
+        Ok(self.intersecting_folds(range, ctx)?.map(|f| &f.0))
     }
 
     pub fn fold<T: ToOffset>(
@@ -97,19 +94,22 @@ impl FoldMap {
         let _ = self.sync(ctx);
 
         let mut edits = Vec::new();
+        let mut folds = Vec::new();
         let buffer = self.buffer.read(ctx);
         for range in ranges.into_iter() {
-            let start = range.start.to_offset(buffer)?;
-            let end = range.end.to_offset(buffer)?;
-            edits.push(Edit {
-                old_range: start..end,
-                new_range: start..end,
-            });
-
-            let fold = buffer.anchor_after(start)?..buffer.anchor_before(end)?;
-            let ix = find_insertion_index(&self.folds, |probe| probe.cmp(&fold, buffer))?;
-            self.folds.insert(ix, fold);
+            let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?;
+            if range.start != range.end {
+                let fold =
+                    Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?);
+                folds.push(fold);
+                edits.push(Edit {
+                    old_range: range.clone(),
+                    new_range: range.clone(),
+                });
+            }
         }
+
+        folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, buffer));
         edits.sort_unstable_by(|a, b| {
             a.old_range
                 .start
@@ -117,6 +117,16 @@ impl FoldMap {
                 .then_with(|| b.old_range.end.cmp(&a.old_range.end))
         });
 
+        self.folds = {
+            let mut new_tree = SumTree::new();
+            let mut cursor = self.folds.cursor::<_, ()>();
+            for fold in folds {
+                new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
+                new_tree.push(fold, buffer);
+            }
+            new_tree.push_tree(cursor.suffix(buffer), buffer);
+            new_tree
+        };
         self.apply_edits(edits, ctx);
         Ok(())
     }
@@ -131,36 +141,66 @@ impl FoldMap {
         let buffer = self.buffer.read(ctx);
 
         let mut edits = Vec::new();
+        let mut fold_ixs_to_delete = Vec::new();
         for range in ranges.into_iter() {
-            let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
-            let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
-
-            // Remove intersecting folds and add their ranges to edits that are passed to apply_edits
-            self.folds.retain(|fold| {
-                if fold.start.cmp(&end, buffer).unwrap() > Ordering::Equal
-                    || fold.end.cmp(&start, buffer).unwrap() < Ordering::Equal
-                {
-                    true
-                } else {
-                    let offset_range =
-                        fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap();
-                    edits.push(Edit {
-                        old_range: offset_range.clone(),
-                        new_range: offset_range,
-                    });
-                    false
-                }
-            });
+            // Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
+            let mut folds_cursor = self.intersecting_folds(range, ctx)?;
+            while let Some(fold) = folds_cursor.item() {
+                let offset_range =
+                    fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap();
+                edits.push(Edit {
+                    old_range: offset_range.clone(),
+                    new_range: offset_range,
+                });
+                fold_ixs_to_delete.push(*folds_cursor.start());
+                folds_cursor.next();
+            }
         }
 
+        fold_ixs_to_delete.sort_unstable();
+        fold_ixs_to_delete.dedup();
+        edits.sort_unstable_by(|a, b| {
+            a.old_range
+                .start
+                .cmp(&b.old_range.start)
+                .then_with(|| b.old_range.end.cmp(&a.old_range.end))
+        });
+
+        self.folds = {
+            let mut cursor = self.folds.cursor::<_, ()>();
+            let mut folds = SumTree::new();
+            for fold_ix in fold_ixs_to_delete {
+                folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer);
+                cursor.next();
+            }
+            folds.push_tree(cursor.suffix(buffer), buffer);
+            folds
+        };
         self.apply_edits(edits, ctx);
         Ok(())
     }
 
+    fn intersecting_folds<'a, T>(
+        &self,
+        range: Range<T>,
+        ctx: &'a AppContext,
+    ) -> Result<FilterCursor<impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>>
+    where
+        T: ToOffset,
+    {
+        let buffer = self.buffer.read(ctx);
+        let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
+        let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
+        Ok(self.folds.filter::<_, usize>(move |summary| {
+            start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
+                && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
+        }))
+    }
+
     pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
         let transforms = self.sync(ctx);
         let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
-        cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right);
+        cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
         while let Some(transform) = cursor.item() {
             if transform.display_text.is_some() {
                 return true;
@@ -177,7 +217,7 @@ impl FoldMap {
     pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<usize> {
         let transforms = self.sync(ctx);
         let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right);
+        cursor.seek(&point, SeekBias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
         (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
     }
@@ -193,7 +233,7 @@ impl FoldMap {
     pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
         let transforms = self.sync(ctx);
         let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&display_point, SeekBias::Right);
+        cursor.seek(&display_point, SeekBias::Right, &());
         let overshoot = display_point.0 - cursor.start().display.lines;
         cursor.start().buffer.lines + overshoot
     }
@@ -201,7 +241,7 @@ impl FoldMap {
     pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
         let transforms = self.sync(ctx);
         let mut cursor = transforms.cursor::<Point, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right);
+        cursor.seek(&point, SeekBias::Right, &());
         let overshoot = point - cursor.start().buffer.lines;
         DisplayPoint(cmp::min(
             cursor.start().display.lines + overshoot,
@@ -226,14 +266,17 @@ impl FoldMap {
         let mut new_transforms = SumTree::new();
         let mut transforms = self.transforms.lock();
         let mut cursor = transforms.cursor::<usize, usize>();
-        cursor.seek(&0, SeekBias::Right);
+        cursor.seek(&0, SeekBias::Right, &());
 
         while let Some(mut edit) = edits.next() {
-            new_transforms.push_tree(cursor.slice(&edit.old_range.start, SeekBias::Left));
+            new_transforms.push_tree(
+                cursor.slice(&edit.old_range.start, SeekBias::Left, &()),
+                &(),
+            );
             edit.new_range.start -= edit.old_range.start - cursor.start();
             edit.old_range.start = *cursor.start();
 
-            cursor.seek(&edit.old_range.end, SeekBias::Right);
+            cursor.seek(&edit.old_range.end, SeekBias::Right, &());
             cursor.next();
 
             let mut delta = edit.delta();
@@ -250,7 +293,7 @@ impl FoldMap {
 
                     if next_edit.old_range.end >= edit.old_range.end {
                         edit.old_range.end = next_edit.old_range.end;
-                        cursor.seek(&edit.old_range.end, SeekBias::Right);
+                        cursor.seek(&edit.old_range.end, SeekBias::Right, &());
                         cursor.next();
                     }
                 } else {
@@ -262,14 +305,10 @@ impl FoldMap {
                 ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
 
             let anchor = buffer.anchor_before(edit.new_range.start).unwrap();
-            let folds_start =
-                find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))
-                    .unwrap();
-            let mut folds = self.folds[folds_start..]
-                .iter()
-                .map(|fold| {
-                    fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
-                })
+            let mut folds_cursor = self.folds.cursor::<_, ()>();
+            folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer);
+            let mut folds = folds_cursor
+                .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap())
                 .peekable();
 
             while folds
@@ -293,29 +332,35 @@ impl FoldMap {
 
                 if fold.start > sum.buffer.chars {
                     let text_summary = buffer.text_summary_for_range(sum.buffer.chars..fold.start);
-                    new_transforms.push(Transform {
-                        summary: TransformSummary {
-                            display: text_summary.clone(),
-                            buffer: text_summary,
+                    new_transforms.push(
+                        Transform {
+                            summary: TransformSummary {
+                                display: text_summary.clone(),
+                                buffer: text_summary,
+                            },
+                            display_text: None,
                         },
-                        display_text: None,
-                    });
+                        &(),
+                    );
                 }
 
                 if fold.end > fold.start {
-                    new_transforms.push(Transform {
-                        summary: TransformSummary {
-                            display: TextSummary {
-                                chars: 1,
-                                bytes: '…'.len_utf8(),
-                                lines: Point::new(0, 1),
-                                first_line_len: 1,
-                                rightmost_point: Point::new(0, 1),
+                    new_transforms.push(
+                        Transform {
+                            summary: TransformSummary {
+                                display: TextSummary {
+                                    chars: 1,
+                                    bytes: '…'.len_utf8(),
+                                    lines: Point::new(0, 1),
+                                    first_line_len: 1,
+                                    rightmost_point: Point::new(0, 1),
+                                },
+                                buffer: buffer.text_summary_for_range(fold.start..fold.end),
                             },
-                            buffer: buffer.text_summary_for_range(fold.start..fold.end),
+                            display_text: Some('…'),
                         },
-                        display_text: Some('…'),
-                    });
+                        &(),
+                    );
                 }
             }
 
@@ -323,26 +368,32 @@ impl FoldMap {
             if sum.buffer.chars < edit.new_range.end {
                 let text_summary =
                     buffer.text_summary_for_range(sum.buffer.chars..edit.new_range.end);
-                new_transforms.push(Transform {
-                    summary: TransformSummary {
-                        display: text_summary.clone(),
-                        buffer: text_summary,
+                new_transforms.push(
+                    Transform {
+                        summary: TransformSummary {
+                            display: text_summary.clone(),
+                            buffer: text_summary,
+                        },
+                        display_text: None,
                     },
-                    display_text: None,
-                });
+                    &(),
+                );
             }
         }
 
-        new_transforms.push_tree(cursor.suffix());
+        new_transforms.push_tree(cursor.suffix(&()), &());
         if new_transforms.is_empty() {
             let text_summary = buffer.text_summary();
-            new_transforms.push(Transform {
-                summary: TransformSummary {
-                    display: text_summary.clone(),
-                    buffer: text_summary,
+            new_transforms.push(
+                Transform {
+                    summary: TransformSummary {
+                        display: text_summary.clone(),
+                        buffer: text_summary,
+                    },
+                    display_text: None,
                 },
-                display_text: None,
-            });
+                &(),
+            );
         }
 
         drop(cursor);
@@ -363,7 +414,7 @@ impl FoldMapSnapshot {
 
         let display_point = Point::new(start_row, 0);
         let mut cursor = self.transforms.cursor();
-        cursor.seek(&DisplayPoint(display_point), SeekBias::Left);
+        cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &());
 
         Ok(BufferRows {
             display_point,
@@ -374,7 +425,7 @@ impl FoldMapSnapshot {
     pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result<Chars<'a>> {
         let offset = self.to_display_offset(point, ctx)?;
         let mut cursor = self.transforms.cursor();
-        cursor.seek(&offset, SeekBias::Right);
+        cursor.seek(&offset, SeekBias::Right, &());
         Ok(Chars {
             cursor,
             offset: offset.0,
@@ -385,7 +436,7 @@ impl FoldMapSnapshot {
 
     fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<DisplayOffset> {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right);
+        cursor.seek(&point, SeekBias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
         let mut offset = cursor.start().display.chars;
         if !overshoot.is_zero() {
@@ -421,16 +472,106 @@ impl sum_tree::Item for Transform {
     }
 }
 
-impl<'a> std::ops::AddAssign<&'a Self> for TransformSummary {
-    fn add_assign(&mut self, other: &'a Self) {
+impl sum_tree::Summary for TransformSummary {
+    type Context = ();
+
+    fn add_summary(&mut self, other: &Self, _: &()) {
         self.buffer += &other.buffer;
         self.display += &other.display;
     }
 }
 
-impl<'a> Dimension<'a, TransformSummary> for TransformSummary {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
     fn add_summary(&mut self, summary: &'a TransformSummary) {
-        *self += summary;
+        sum_tree::Summary::add_summary(self, summary, &());
+    }
+}
+
+#[derive(Clone, Debug)]
+struct Fold(Range<Anchor>);
+
+impl Default for Fold {
+    fn default() -> Self {
+        Self(Anchor::Start..Anchor::End)
+    }
+}
+
+impl sum_tree::Item for Fold {
+    type Summary = FoldSummary;
+
+    fn summary(&self) -> Self::Summary {
+        FoldSummary {
+            start: self.0.start.clone(),
+            end: self.0.end.clone(),
+            min_start: self.0.start.clone(),
+            max_end: self.0.end.clone(),
+            count: 1,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+struct FoldSummary {
+    start: Anchor,
+    end: Anchor,
+    min_start: Anchor,
+    max_end: Anchor,
+    count: usize,
+}
+
+impl Default for FoldSummary {
+    fn default() -> Self {
+        Self {
+            start: Anchor::Start,
+            end: Anchor::End,
+            min_start: Anchor::End,
+            max_end: Anchor::Start,
+            count: 0,
+        }
+    }
+}
+
+impl sum_tree::Summary for FoldSummary {
+    type Context = Buffer;
+
+    fn add_summary(&mut self, other: &Self, buffer: &Buffer) {
+        if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
+            self.min_start = other.min_start.clone();
+        }
+        if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
+            self.max_end = other.max_end.clone();
+        }
+
+        #[cfg(debug_assertions)]
+        {
+            let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
+            assert!(start_comparison <= Ordering::Equal);
+            if start_comparison == Ordering::Equal {
+                assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
+            }
+        }
+        self.start = other.start.clone();
+        self.end = other.end.clone();
+        self.count += other.count;
+    }
+}
+
+impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
+    fn add_summary(&mut self, summary: &'a FoldSummary) {
+        self.0.start = summary.start.clone();
+        self.0.end = summary.end.clone();
+    }
+}
+
+impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
+    fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering {
+        self.0.cmp(&other.0, buffer).unwrap()
+    }
+}
+
+impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
+    fn add_summary(&mut self, summary: &'a FoldSummary) {
+        *self += summary.count;
     }
 }
 
@@ -498,7 +639,7 @@ impl<'a> Iterator for Chars<'a> {
     }
 }
 
-impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
     fn add_summary(&mut self, summary: &'a TransformSummary) {
         self.0 += &summary.display.lines;
     }
@@ -507,19 +648,19 @@ impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 pub struct DisplayOffset(usize);
 
-impl<'a> Dimension<'a, TransformSummary> for DisplayOffset {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
     fn add_summary(&mut self, summary: &'a TransformSummary) {
         self.0 += &summary.display.chars;
     }
 }
 
-impl<'a> Dimension<'a, TransformSummary> for Point {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
     fn add_summary(&mut self, summary: &'a TransformSummary) {
         *self += &summary.buffer.lines;
     }
 }
 
-impl<'a> Dimension<'a, TransformSummary> for usize {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
     fn add_summary(&mut self, summary: &'a TransformSummary) {
         *self += &summary.buffer.chars;
     }
@@ -571,7 +712,7 @@ mod tests {
             });
             assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
 
-            map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref())
+            map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref())
                 .unwrap();
             assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
         });
@@ -719,7 +860,7 @@ mod tests {
         };
 
         for seed in seed_range {
-            println!("{:?}", seed);
+            dbg!(seed);
             let mut rng = StdRng::seed_from_u64(seed);
 
             App::test((), |app| {
@@ -732,26 +873,38 @@ mod tests {
 
                 for _ in 0..operations {
                     log::info!("text: {:?}", buffer.read(app).text());
-                    if rng.gen() {
-                        let buffer = buffer.read(app);
-
-                        let fold_count = rng.gen_range(1..=5);
-                        let mut fold_ranges: Vec<Range<usize>> = Vec::new();
-                        for _ in 0..fold_count {
-                            let end = rng.gen_range(0..buffer.len() + 1);
-                            let start = rng.gen_range(0..end + 1);
-                            fold_ranges.push(start..end);
+                    match rng.gen_range(0..=100) {
+                        0..=34 => {
+                            let buffer = buffer.read(app);
+                            let mut to_fold = Vec::new();
+                            for _ in 0..rng.gen_range(1..=5) {
+                                let end = rng.gen_range(0..=buffer.len());
+                                let start = rng.gen_range(0..=end);
+                                to_fold.push(start..end);
+                            }
+                            log::info!("folding {:?}", to_fold);
+                            map.fold(to_fold, app.as_ref()).unwrap();
+                        }
+                        35..=59 if !map.folds.is_empty() => {
+                            let buffer = buffer.read(app);
+                            let mut to_unfold = Vec::new();
+                            for _ in 0..rng.gen_range(1..=3) {
+                                let end = rng.gen_range(0..=buffer.len());
+                                let start = rng.gen_range(0..=end);
+                                to_unfold.push(start..end);
+                            }
+                            log::info!("unfolding {:?}", to_unfold);
+                            map.unfold(to_unfold, app.as_ref()).unwrap();
+                        }
+                        _ => {
+                            let edits = buffer.update(app, |buffer, ctx| {
+                                let start_version = buffer.version.clone();
+                                let edit_count = rng.gen_range(1..=5);
+                                buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
+                                buffer.edits_since(start_version).collect::<Vec<_>>()
+                            });
+                            log::info!("editing {:?}", edits);
                         }
-                        log::info!("folding {:?}", fold_ranges);
-                        map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
-                    } else {
-                        let edits = buffer.update(app, |buffer, ctx| {
-                            let start_version = buffer.version.clone();
-                            let edit_count = rng.gen_range(1..=5);
-                            buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
-                            buffer.edits_since(start_version).collect::<Vec<_>>()
-                        });
-                        log::info!("editing {:?}", edits);
                     }
                     map.check_invariants(app.as_ref());
 
@@ -844,6 +997,31 @@ mod tests {
                         );
                         assert!(map.is_line_folded(display_point.row(), app.as_ref()));
                     }
+
+                    for _ in 0..5 {
+                        let end = rng.gen_range(0..=buffer.len());
+                        let start = rng.gen_range(0..=end);
+                        let expected_folds = map
+                            .folds
+                            .items()
+                            .into_iter()
+                            .filter(|fold| {
+                                let start = buffer.anchor_before(start).unwrap();
+                                let end = buffer.anchor_after(end).unwrap();
+                                start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less
+                                    && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater
+                            })
+                            .map(|fold| fold.0)
+                            .collect::<Vec<_>>();
+
+                        assert_eq!(
+                            map.folds_in_range(start..end, app.as_ref())
+                                .unwrap()
+                                .cloned()
+                                .collect::<Vec<_>>(),
+                            expected_folds
+                        );
+                    }
                 }
             });
         }
@@ -894,13 +1072,13 @@ mod tests {
 
         fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
             let buffer = self.buffer.read(app);
-            let mut folds = self.folds.clone();
+            let mut folds = self.folds.items();
             // Ensure sorting doesn't change how folds get merged and displayed.
-            folds.sort_by(|a, b| a.cmp(b, buffer).unwrap());
+            folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
             let mut fold_ranges = folds
                 .iter()
                 .map(|fold| {
-                    fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
+                    fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap()
                 })
                 .peekable();
 

zed/src/editor/display_map/mod.rs 🔗

@@ -1,6 +1,6 @@
 mod fold_map;
 
-use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
+use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
 use anyhow::Result;
 pub use fold_map::BufferRows;
 use fold_map::{FoldMap, FoldMapSnapshot};

zed/src/operation_queue.rs 🔗

@@ -1,11 +1,8 @@
 use crate::{
-    sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree},
+    sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary},
     time,
 };
-use std::{
-    fmt::Debug,
-    ops::{Add, AddAssign},
-};
+use std::{fmt::Debug, ops::Add};
 
 pub trait Operation: Clone + Debug + Eq {
     fn timestamp(&self) -> time::Lamport;
@@ -35,7 +32,8 @@ impl<T: Operation> OperationQueue<T> {
     pub fn insert(&mut self, mut ops: Vec<T>) {
         ops.sort_by_key(|op| op.timestamp());
         ops.dedup_by_key(|op| op.timestamp());
-        self.0.edit(ops.into_iter().map(Edit::Insert).collect());
+        self.0
+            .edit(ops.into_iter().map(Edit::Insert).collect(), &());
     }
 
     pub fn drain(&mut self) -> Self {
@@ -68,8 +66,10 @@ impl<T: Operation> KeyedItem for T {
     }
 }
 
-impl<'a> AddAssign<&'a Self> for OperationSummary {
-    fn add_assign(&mut self, other: &Self) {
+impl Summary for OperationSummary {
+    type Context = ();
+
+    fn add_summary(&mut self, other: &Self, _: &()) {
         assert!(self.key < other.key);
         self.key = other.key;
         self.len += other.len;

zed/src/sum_tree/cursor.rs 🔗

@@ -199,9 +199,6 @@ where
     }
 
     pub fn next(&mut self) {
-        if !self.did_seek {
-            self.descend_to_first_item(self.tree, |_| true)
-        }
         self.next_internal(|_| true)
     }
 
@@ -209,67 +206,88 @@ where
     where
         F: Fn(&T::Summary) -> bool,
     {
-        assert!(self.did_seek, "Must seek before calling this method");
+        let mut descend = false;
+
+        if self.stack.is_empty() && !self.at_end {
+            self.stack.push(StackEntry {
+                tree: self.tree,
+                index: 0,
+                seek_dimension: S::default(),
+                sum_dimension: U::default(),
+            });
+            descend = true;
+            self.did_seek = true;
+        }
 
-        if self.stack.is_empty() {
-            if !self.at_end {
-                self.descend_to_first_item(self.tree, filter_node);
-            }
-        } else {
-            while self.stack.len() > 0 {
-                let new_subtree = {
-                    let entry = self.stack.last_mut().unwrap();
-                    match entry.tree.0.as_ref() {
-                        Node::Internal {
-                            child_trees,
-                            child_summaries,
-                            ..
-                        } => {
-                            while entry.index < child_summaries.len() {
-                                entry
-                                    .seek_dimension
-                                    .add_summary(&child_summaries[entry.index]);
-                                entry
-                                    .sum_dimension
-                                    .add_summary(&child_summaries[entry.index]);
-
-                                entry.index += 1;
-                                if let Some(next_summary) = child_summaries.get(entry.index) {
-                                    if filter_node(next_summary) {
-                                        break;
-                                    } else {
-                                        self.seek_dimension.add_summary(next_summary);
-                                        self.sum_dimension.add_summary(next_summary);
-                                    }
-                                }
-                            }
+        while self.stack.len() > 0 {
+            let new_subtree = {
+                let entry = self.stack.last_mut().unwrap();
+                match entry.tree.0.as_ref() {
+                    Node::Internal {
+                        child_trees,
+                        child_summaries,
+                        ..
+                    } => {
+                        if !descend {
+                            let summary = &child_summaries[entry.index];
+                            entry.seek_dimension.add_summary(summary);
+                            entry.sum_dimension.add_summary(summary);
+                            entry.index += 1;
+                        }
 
-                            child_trees.get(entry.index)
+                        while entry.index < child_summaries.len() {
+                            let next_summary = &child_summaries[entry.index];
+                            if filter_node(next_summary) {
+                                break;
+                            } else {
+                                self.seek_dimension.add_summary(next_summary);
+                                self.sum_dimension.add_summary(next_summary);
+                            }
+                            entry.index += 1;
                         }
-                        Node::Leaf { item_summaries, .. } => loop {
+
+                        child_trees.get(entry.index)
+                    }
+                    Node::Leaf { item_summaries, .. } => {
+                        if !descend {
                             let item_summary = &item_summaries[entry.index];
                             self.seek_dimension.add_summary(item_summary);
                             entry.seek_dimension.add_summary(item_summary);
                             self.sum_dimension.add_summary(item_summary);
                             entry.sum_dimension.add_summary(item_summary);
                             entry.index += 1;
+                        }
+
+                        loop {
                             if let Some(next_item_summary) = item_summaries.get(entry.index) {
                                 if filter_node(next_item_summary) {
                                     return;
+                                } else {
+                                    self.seek_dimension.add_summary(next_item_summary);
+                                    entry.seek_dimension.add_summary(next_item_summary);
+                                    self.sum_dimension.add_summary(next_item_summary);
+                                    entry.sum_dimension.add_summary(next_item_summary);
+                                    entry.index += 1;
                                 }
                             } else {
                                 break None;
                             }
-                        },
+                        }
                     }
-                };
-
-                if let Some(subtree) = new_subtree {
-                    self.descend_to_first_item(subtree, filter_node);
-                    break;
-                } else {
-                    self.stack.pop();
                 }
+            };
+
+            if let Some(subtree) = new_subtree {
+                descend = true;
+                self.stack.push(StackEntry {
+                    tree: subtree,
+                    index: 0,
+                    seek_dimension: self.seek_dimension.clone(),
+                    sum_dimension: self.sum_dimension.clone(),
+                });
+            } else {
+                descend = false;
+                self.stack.pop();
             }
         }
 
@@ -277,67 +295,6 @@ where
         debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf());
     }
 
-    pub fn descend_to_first_item<F>(&mut self, mut subtree: &'a SumTree<T>, filter_node: F)
-    where
-        F: Fn(&T::Summary) -> bool,
-    {
-        self.did_seek = true;
-        loop {
-            subtree = match *subtree.0 {
-                Node::Internal {
-                    ref child_trees,
-                    ref child_summaries,
-                    ..
-                } => {
-                    let mut new_index = None;
-                    for (index, summary) in child_summaries.iter().enumerate() {
-                        if filter_node(summary) {
-                            new_index = Some(index);
-                            break;
-                        }
-                        self.seek_dimension.add_summary(summary);
-                        self.sum_dimension.add_summary(summary);
-                    }
-
-                    if let Some(new_index) = new_index {
-                        self.stack.push(StackEntry {
-                            tree: subtree,
-                            index: new_index,
-                            seek_dimension: self.seek_dimension.clone(),
-                            sum_dimension: self.sum_dimension.clone(),
-                        });
-                        &child_trees[new_index]
-                    } else {
-                        break;
-                    }
-                }
-                Node::Leaf {
-                    ref item_summaries, ..
-                } => {
-                    let mut new_index = None;
-                    for (index, item_summary) in item_summaries.iter().enumerate() {
-                        if filter_node(item_summary) {
-                            new_index = Some(index);
-                            break;
-                        }
-                        self.seek_dimension.add_summary(item_summary);
-                        self.sum_dimension.add_summary(item_summary);
-                    }
-
-                    if let Some(new_index) = new_index {
-                        self.stack.push(StackEntry {
-                            tree: subtree,
-                            index: new_index,
-                            seek_dimension: self.seek_dimension.clone(),
-                            sum_dimension: self.sum_dimension.clone(),
-                        });
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
     fn descend_to_last_item(&mut self, mut subtree: &'a SumTree<T>) {
         self.did_seek = true;
         loop {
@@ -382,22 +339,36 @@ where
 impl<'a, T, S, U> Cursor<'a, T, S, U>
 where
     T: Item,
-    S: Dimension<'a, T::Summary> + Ord,
+    S: SeekDimension<'a, T::Summary>,
     U: Dimension<'a, T::Summary>,
 {
-    pub fn seek(&mut self, pos: &S, bias: SeekBias) -> bool {
+    pub fn seek(
+        &mut self,
+        pos: &S,
+        bias: SeekBias,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> bool {
         self.reset();
-        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None)
+        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx)
     }
 
-    #[allow(unused)]
-    pub fn seek_forward(&mut self, pos: &S, bias: SeekBias) -> bool {
-        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None)
+    pub fn seek_forward(
+        &mut self,
+        pos: &S,
+        bias: SeekBias,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> bool {
+        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx)
     }
 
-    pub fn slice(&mut self, end: &S, bias: SeekBias) -> SumTree<T> {
+    pub fn slice(
+        &mut self,
+        end: &S,
+        bias: SeekBias,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> SumTree<T> {
         let mut slice = SeekAggregate::Slice(SumTree::new());
-        self.seek_internal::<()>(end, bias, &mut slice);
+        self.seek_internal::<()>(end, bias, &mut slice, ctx);
         if let SeekAggregate::Slice(slice) = slice {
             slice
         } else {
@@ -405,10 +376,10 @@ where
         }
     }
 
-    pub fn suffix(&mut self) -> SumTree<T> {
+    pub fn suffix(&mut self, ctx: &<T::Summary as Summary>::Context) -> SumTree<T> {
         let extent = self.tree.extent::<S>();
         let mut slice = SeekAggregate::Slice(SumTree::new());
-        self.seek_internal::<()>(&extent, SeekBias::Right, &mut slice);
+        self.seek_internal::<()>(&extent, SeekBias::Right, &mut slice, ctx);
         if let SeekAggregate::Slice(slice) = slice {
             slice
         } else {
@@ -416,12 +387,17 @@ where
         }
     }
 
-    pub fn summary<D>(&mut self, end: &S, bias: SeekBias) -> D
+    pub fn summary<D>(
+        &mut self,
+        end: &S,
+        bias: SeekBias,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> D
     where
         D: Dimension<'a, T::Summary>,
     {
         let mut summary = SeekAggregate::Summary(D::default());
-        self.seek_internal(end, bias, &mut summary);
+        self.seek_internal(end, bias, &mut summary, ctx);
         if let SeekAggregate::Summary(summary) = summary {
             summary
         } else {
@@ -434,11 +410,12 @@ where
         target: &S,
         bias: SeekBias,
         aggregate: &mut SeekAggregate<T, D>,
+        ctx: &<T::Summary as Summary>::Context,
     ) -> bool
     where
         D: Dimension<'a, T::Summary>,
     {
-        debug_assert!(target >= &self.seek_dimension);
+        debug_assert!(target.cmp(&self.seek_dimension, ctx) >= Ordering::Equal);
         let mut containing_subtree = None;
 
         if self.did_seek {
@@ -458,7 +435,7 @@ where
                                 let mut child_end = self.seek_dimension.clone();
                                 child_end.add_summary(&child_summary);
 
-                                let comparison = target.cmp(&child_end);
+                                let comparison = target.cmp(&child_end, ctx);
                                 if comparison == Ordering::Greater
                                     || (comparison == Ordering::Equal && bias == SeekBias::Right)
                                 {
@@ -467,7 +444,7 @@ where
                                     match aggregate {
                                         SeekAggregate::None => {}
                                         SeekAggregate::Slice(slice) => {
-                                            slice.push_tree(child_tree.clone());
+                                            slice.push_tree(child_tree.clone(), ctx);
                                         }
                                         SeekAggregate::Summary(summary) => {
                                             summary.add_summary(child_summary);
@@ -500,7 +477,7 @@ where
                                 let mut item_end = self.seek_dimension.clone();
                                 item_end.add_summary(item_summary);
 
-                                let comparison = target.cmp(&item_end);
+                                let comparison = target.cmp(&item_end, ctx);
                                 if comparison == Ordering::Greater
                                     || (comparison == Ordering::Equal && bias == SeekBias::Right)
                                 {
@@ -511,7 +488,10 @@ where
                                         SeekAggregate::Slice(_) => {
                                             slice_items.push(item.clone());
                                             slice_item_summaries.push(item_summary.clone());
-                                            *slice_items_summary.as_mut().unwrap() += item_summary;
+                                            slice_items_summary
+                                                .as_mut()
+                                                .unwrap()
+                                                .add_summary(item_summary, ctx);
                                         }
                                         SeekAggregate::Summary(summary) => {
                                             summary.add_summary(item_summary);
@@ -520,11 +500,14 @@ where
                                     entry.index += 1;
                                 } else {
                                     if let SeekAggregate::Slice(slice) = aggregate {
-                                        slice.push_tree(SumTree(Arc::new(Node::Leaf {
-                                            summary: slice_items_summary.unwrap(),
-                                            items: slice_items,
-                                            item_summaries: slice_item_summaries,
-                                        })));
+                                        slice.push_tree(
+                                            SumTree(Arc::new(Node::Leaf {
+                                                summary: slice_items_summary.unwrap(),
+                                                items: slice_items,
+                                                item_summaries: slice_item_summaries,
+                                            })),
+                                            ctx,
+                                        );
                                     }
                                     break 'outer;
                                 }
@@ -532,11 +515,14 @@ where
 
                             if let SeekAggregate::Slice(slice) = aggregate {
                                 if !slice_items.is_empty() {
-                                    slice.push_tree(SumTree(Arc::new(Node::Leaf {
-                                        summary: slice_items_summary.unwrap(),
-                                        items: slice_items,
-                                        item_summaries: slice_item_summaries,
-                                    })));
+                                    slice.push_tree(
+                                        SumTree(Arc::new(Node::Leaf {
+                                            summary: slice_items_summary.unwrap(),
+                                            items: slice_items,
+                                            item_summaries: slice_item_summaries,
+                                        })),
+                                        ctx,
+                                    );
                                 }
                             }
                         }
@@ -565,7 +551,7 @@ where
                             let mut child_end = self.seek_dimension.clone();
                             child_end.add_summary(child_summary);
 
-                            let comparison = target.cmp(&child_end);
+                            let comparison = target.cmp(&child_end, ctx);
                             if comparison == Ordering::Greater
                                 || (comparison == Ordering::Equal && bias == SeekBias::Right)
                             {
@@ -574,7 +560,7 @@ where
                                 match aggregate {
                                     SeekAggregate::None => {}
                                     SeekAggregate::Slice(slice) => {
-                                        slice.push_tree(child_trees[index].clone());
+                                        slice.push_tree(child_trees[index].clone(), ctx);
                                     }
                                     SeekAggregate::Summary(summary) => {
                                         summary.add_summary(child_summary);
@@ -611,7 +597,7 @@ where
                             let mut child_end = self.seek_dimension.clone();
                             child_end.add_summary(item_summary);
 
-                            let comparison = target.cmp(&child_end);
+                            let comparison = target.cmp(&child_end, ctx);
                             if comparison == Ordering::Greater
                                 || (comparison == Ordering::Equal && bias == SeekBias::Right)
                             {
@@ -621,7 +607,10 @@ where
                                     SeekAggregate::None => {}
                                     SeekAggregate::Slice(_) => {
                                         slice_items.push(item.clone());
-                                        *slice_items_summary.as_mut().unwrap() += item_summary;
+                                        slice_items_summary
+                                            .as_mut()
+                                            .unwrap()
+                                            .add_summary(item_summary, ctx);
                                         slice_item_summaries.push(item_summary.clone());
                                     }
                                     SeekAggregate::Summary(summary) => {
@@ -641,11 +630,14 @@ where
 
                         if let SeekAggregate::Slice(slice) = aggregate {
                             if !slice_items.is_empty() {
-                                slice.push_tree(SumTree(Arc::new(Node::Leaf {
-                                    summary: slice_items_summary.unwrap(),
-                                    items: slice_items,
-                                    item_summaries: slice_item_summaries,
-                                })));
+                                slice.push_tree(
+                                    SumTree(Arc::new(Node::Leaf {
+                                        summary: slice_items_summary.unwrap(),
+                                        items: slice_items,
+                                        item_summaries: slice_item_summaries,
+                                    })),
+                                    ctx,
+                                );
                             }
                         }
                     }
@@ -666,9 +658,9 @@ where
             if let Some(summary) = self.item_summary() {
                 end.add_summary(summary);
             }
-            *target == end
+            target.cmp(&end, ctx) == Ordering::Equal
         } else {
-            *target == self.seek_dimension
+            target.cmp(&self.seek_dimension, ctx) == Ordering::Equal
         }
     }
 }
@@ -683,7 +675,7 @@ where
 
     fn next(&mut self) -> Option<Self::Item> {
         if !self.did_seek {
-            self.descend_to_first_item(self.tree, |_| true);
+            self.next();
         }
 
         if let Some(item) = self.item() {
@@ -708,13 +700,7 @@ where
 {
     pub fn new(tree: &'a SumTree<T>, filter_node: F) -> Self {
         let mut cursor = tree.cursor::<(), U>();
-        if filter_node(&tree.summary()) {
-            cursor.descend_to_first_item(tree, &filter_node);
-        } else {
-            cursor.did_seek = true;
-            cursor.at_end = true;
-        }
-
+        cursor.next_internal(&filter_node);
         Self {
             cursor,
             filter_node,

zed/src/sum_tree/mod.rs 🔗

@@ -3,7 +3,7 @@ mod cursor;
 use arrayvec::ArrayVec;
 pub use cursor::Cursor;
 pub use cursor::FilterCursor;
-use std::{fmt, iter::FromIterator, ops::AddAssign, sync::Arc};
+use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc};
 
 #[cfg(test)]
 const TREE_BASE: usize = 2;
@@ -11,7 +11,7 @@ const TREE_BASE: usize = 2;
 const TREE_BASE: usize = 6;
 
 pub trait Item: Clone + fmt::Debug {
-    type Summary: for<'a> AddAssign<&'a Self::Summary> + Default + Clone + fmt::Debug;
+    type Summary: Summary;
 
     fn summary(&self) -> Self::Summary;
 }
@@ -22,14 +22,30 @@ pub trait KeyedItem: Item {
     fn key(&self) -> Self::Key;
 }
 
-pub trait Dimension<'a, Summary: Default>: Clone + fmt::Debug + Default {
-    fn add_summary(&mut self, summary: &'a Summary);
+pub trait Summary: Default + Clone + fmt::Debug {
+    type Context;
+
+    fn add_summary(&mut self, summary: &Self, ctx: &Self::Context);
+}
+
+pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default {
+    fn add_summary(&mut self, _summary: &'a S);
 }
 
-impl<'a, T: Default> Dimension<'a, T> for () {
+impl<'a, T: Summary> Dimension<'a, T> for () {
     fn add_summary(&mut self, _: &'a T) {}
 }
 
+pub trait SeekDimension<'a, T: Summary>: Dimension<'a, T> {
+    fn cmp(&self, other: &Self, ctx: &T::Context) -> Ordering;
+}
+
+impl<'a, S: Summary, T: Dimension<'a, S> + Ord> SeekDimension<'a, S> for T {
+    fn cmp(&self, other: &Self, _ctx: &S::Context) -> Ordering {
+        Ord::cmp(self, other)
+    }
+}
+
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub enum SeekBias {
     Left,
@@ -48,16 +64,15 @@ impl<T: Item> SumTree<T> {
         }))
     }
 
-    pub fn from_item(item: T) -> Self {
+    pub fn from_item(item: T, ctx: &<T::Summary as Summary>::Context) -> Self {
         let mut tree = Self::new();
-        tree.push(item);
+        tree.push(item, ctx);
         tree
     }
 
     #[allow(unused)]
     pub fn items(&self) -> Vec<T> {
         let mut cursor = self.cursor::<(), ()>();
-        cursor.descend_to_first_item(self, |_| true);
         cursor.cloned().collect()
     }
 
@@ -90,7 +105,7 @@ impl<T: Item> SumTree<T> {
         let mut extent = D::default();
         match self.0.as_ref() {
             Node::Internal { summary, .. } | Node::Leaf { summary, .. } => {
-                extent.add_summary(summary)
+                extent.add_summary(summary);
             }
         }
         extent
@@ -110,7 +125,7 @@ impl<T: Item> SumTree<T> {
         }
     }
 
-    pub fn extend<I>(&mut self, iter: I)
+    pub fn extend<I>(&mut self, iter: I, ctx: &<T::Summary as Summary>::Context)
     where
         I: IntoIterator<Item = T>,
     {
@@ -118,7 +133,7 @@ impl<T: Item> SumTree<T> {
 
         for item in iter {
             if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
-                self.push_tree(SumTree(Arc::new(leaf.take().unwrap())));
+                self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
             }
 
             if leaf.is_none() {
@@ -136,7 +151,7 @@ impl<T: Item> SumTree<T> {
             }) = leaf.as_mut()
             {
                 let item_summary = item.summary();
-                *summary += &item_summary;
+                summary.add_summary(&item_summary, ctx);
                 items.push(item);
                 item_summaries.push(item_summary);
             } else {
@@ -145,35 +160,43 @@ impl<T: Item> SumTree<T> {
         }
 
         if leaf.is_some() {
-            self.push_tree(SumTree(Arc::new(leaf.take().unwrap())));
+            self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
         }
     }
 
-    pub fn push(&mut self, item: T) {
+    pub fn push(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
         let summary = item.summary();
-        self.push_tree(SumTree::from_child_trees(vec![SumTree(Arc::new(
-            Node::Leaf {
-                summary: summary.clone(),
-                items: ArrayVec::from_iter(Some(item)),
-                item_summaries: ArrayVec::from_iter(Some(summary)),
-            },
-        ))]))
-    }
-
-    pub fn push_tree(&mut self, other: Self) {
+        self.push_tree(
+            SumTree::from_child_trees(
+                vec![SumTree(Arc::new(Node::Leaf {
+                    summary: summary.clone(),
+                    items: ArrayVec::from_iter(Some(item)),
+                    item_summaries: ArrayVec::from_iter(Some(summary)),
+                }))],
+                ctx,
+            ),
+            ctx,
+        )
+    }
+
+    pub fn push_tree(&mut self, other: Self, ctx: &<T::Summary as Summary>::Context) {
         let other_node = other.0.clone();
         if !other_node.is_leaf() || other_node.items().len() > 0 {
             if self.0.height() < other_node.height() {
                 for tree in other_node.child_trees() {
-                    self.push_tree(tree.clone());
+                    self.push_tree(tree.clone(), ctx);
                 }
-            } else if let Some(split_tree) = self.push_tree_recursive(other) {
-                *self = Self::from_child_trees(vec![self.clone(), split_tree]);
+            } else if let Some(split_tree) = self.push_tree_recursive(other, ctx) {
+                *self = Self::from_child_trees(vec![self.clone(), split_tree], ctx);
             }
         }
     }
 
-    fn push_tree_recursive(&mut self, other: SumTree<T>) -> Option<SumTree<T>> {
+    fn push_tree_recursive(
+        &mut self,
+        other: SumTree<T>,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> Option<SumTree<T>> {
         match Arc::make_mut(&mut self.0) {
             Node::Internal {
                 height,
@@ -183,7 +206,7 @@ impl<T: Item> SumTree<T> {
                 ..
             } => {
                 let other_node = other.0.clone();
-                *summary += other_node.summary();
+                summary.add_summary(other_node.summary(), ctx);
 
                 let height_delta = *height - other_node.height();
                 let mut summaries_to_append = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new();
@@ -195,7 +218,10 @@ impl<T: Item> SumTree<T> {
                     summaries_to_append.push(other_node.summary().clone());
                     trees_to_append.push(other)
                 } else {
-                    let tree_to_append = child_trees.last_mut().unwrap().push_tree_recursive(other);
+                    let tree_to_append = child_trees
+                        .last_mut()
+                        .unwrap()
+                        .push_tree_recursive(other, ctx);
                     *child_summaries.last_mut().unwrap() =
                         child_trees.last().unwrap().0.summary().clone();
 
@@ -225,13 +251,13 @@ impl<T: Item> SumTree<T> {
                         left_trees = all_trees.by_ref().take(midpoint).collect();
                         right_trees = all_trees.collect();
                     }
-                    *summary = sum(left_summaries.iter());
+                    *summary = sum(left_summaries.iter(), ctx);
                     *child_summaries = left_summaries;
                     *child_trees = left_trees;
 
                     Some(SumTree(Arc::new(Node::Internal {
                         height: *height,
-                        summary: sum(right_summaries.iter()),
+                        summary: sum(right_summaries.iter(), ctx),
                         child_summaries: right_summaries,
                         child_trees: right_trees,
                     })))
@@ -270,14 +296,14 @@ impl<T: Item> SumTree<T> {
                     }
                     *items = left_items;
                     *item_summaries = left_summaries;
-                    *summary = sum(item_summaries.iter());
+                    *summary = sum(item_summaries.iter(), ctx);
                     Some(SumTree(Arc::new(Node::Leaf {
                         items: right_items,
-                        summary: sum(right_summaries.iter()),
+                        summary: sum(right_summaries.iter(), ctx),
                         item_summaries: right_summaries,
                     })))
                 } else {
-                    *summary += other_node.summary();
+                    summary.add_summary(other_node.summary(), ctx);
                     items.extend(other_node.items().iter().cloned());
                     item_summaries.extend(other_node.child_summaries().iter().cloned());
                     None
@@ -286,13 +312,16 @@ impl<T: Item> SumTree<T> {
         }
     }
 
-    fn from_child_trees(child_trees: Vec<SumTree<T>>) -> Self {
+    fn from_child_trees(
+        child_trees: Vec<SumTree<T>>,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> Self {
         let height = child_trees[0].0.height() + 1;
         let mut child_summaries = ArrayVec::new();
         for child in &child_trees {
             child_summaries.push(child.0.summary().clone());
         }
-        let summary = sum(child_summaries.iter());
+        let summary = sum(child_summaries.iter(), ctx);
         SumTree(Arc::new(Node::Internal {
             height,
             summary,
@@ -322,17 +351,21 @@ impl<T: Item> SumTree<T> {
 
 impl<T: KeyedItem> SumTree<T> {
     #[allow(unused)]
-    pub fn insert(&mut self, item: T) {
+    pub fn insert(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
         *self = {
             let mut cursor = self.cursor::<T::Key, ()>();
-            let mut new_tree = cursor.slice(&item.key(), SeekBias::Left);
-            new_tree.push(item);
-            new_tree.push_tree(cursor.suffix());
+            let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx);
+            new_tree.push(item, ctx);
+            new_tree.push_tree(cursor.suffix(ctx), ctx);
             new_tree
         };
     }
 
-    pub fn edit(&mut self, mut edits: Vec<Edit<T>>) -> Vec<T> {
+    pub fn edit(
+        &mut self,
+        mut edits: Vec<Edit<T>>,
+        ctx: &<T::Summary as Summary>::Context,
+    ) -> Vec<T> {
         if edits.is_empty() {
             return Vec::new();
         }
@@ -345,7 +378,7 @@ impl<T: KeyedItem> SumTree<T> {
             let mut new_tree = SumTree::new();
             let mut buffered_items = Vec::new();
 
-            cursor.seek(&T::Key::default(), SeekBias::Left);
+            cursor.seek(&T::Key::default(), SeekBias::Left, ctx);
             for edit in edits {
                 let new_key = edit.key();
                 let mut old_item = cursor.item();
@@ -354,9 +387,9 @@ impl<T: KeyedItem> SumTree<T> {
                     .as_ref()
                     .map_or(false, |old_item| old_item.key() < new_key)
                 {
-                    new_tree.extend(buffered_items.drain(..));
-                    let slice = cursor.slice(&new_key, SeekBias::Left);
-                    new_tree.push_tree(slice);
+                    new_tree.extend(buffered_items.drain(..), ctx);
+                    let slice = cursor.slice(&new_key, SeekBias::Left, ctx);
+                    new_tree.push_tree(slice, ctx);
                     old_item = cursor.item();
                 }
 
@@ -375,17 +408,17 @@ impl<T: KeyedItem> SumTree<T> {
                 }
             }
 
-            new_tree.extend(buffered_items);
-            new_tree.push_tree(cursor.suffix());
+            new_tree.extend(buffered_items, ctx);
+            new_tree.push_tree(cursor.suffix(ctx), ctx);
             new_tree
         };
 
         removed
     }
 
-    pub fn get(&self, key: &T::Key) -> Option<&T> {
+    pub fn get(&self, key: &T::Key, ctx: &<T::Summary as Summary>::Context) -> Option<&T> {
         let mut cursor = self.cursor::<T::Key, ()>();
-        if cursor.seek(key, SeekBias::Left) {
+        if cursor.seek(key, SeekBias::Left, ctx) {
             cursor.item()
         } else {
             None
@@ -482,14 +515,14 @@ impl<T: KeyedItem> Edit<T> {
     }
 }
 
-fn sum<'a, T, I>(iter: I) -> T
+fn sum<'a, T, I>(iter: I, ctx: &T::Context) -> T
 where
-    T: 'a + Default + AddAssign<&'a T>,
+    T: 'a + Summary,
     I: Iterator<Item = &'a T>,
 {
     let mut sum = T::default();
     for value in iter {
-        sum += value;
+        sum.add_summary(value, ctx);
     }
     sum
 }
@@ -503,12 +536,12 @@ mod tests {
     #[test]
     fn test_extend_and_push_tree() {
         let mut tree1 = SumTree::new();
-        tree1.extend(0..20);
+        tree1.extend(0..20, &());
 
         let mut tree2 = SumTree::new();
-        tree2.extend(50..100);
+        tree2.extend(50..100, &());
 
-        tree1.push_tree(tree2);
+        tree1.push_tree(tree2, &());
         assert_eq!(tree1.items(), (0..20).chain(50..100).collect::<Vec<u8>>());
     }
 
@@ -517,11 +550,12 @@ mod tests {
         for seed in 0..100 {
             use rand::{distributions, prelude::*};
 
+            dbg!(seed);
             let rng = &mut StdRng::seed_from_u64(seed);
 
             let mut tree = SumTree::<u8>::new();
             let count = rng.gen_range(0..10);
-            tree.extend(rng.sample_iter(distributions::Standard).take(count));
+            tree.extend(rng.sample_iter(distributions::Standard).take(count), &());
 
             for _ in 0..5 {
                 let splice_end = rng.gen_range(0..tree.extent::<Count>().0 + 1);
@@ -538,10 +572,10 @@ mod tests {
 
                 tree = {
                     let mut cursor = tree.cursor::<Count, ()>();
-                    let mut new_tree = cursor.slice(&Count(splice_start), SeekBias::Right);
-                    new_tree.extend(new_items);
-                    cursor.seek(&Count(splice_end), SeekBias::Right);
-                    new_tree.push_tree(cursor.slice(&tree_end, SeekBias::Right));
+                    let mut new_tree = cursor.slice(&Count(splice_start), SeekBias::Right, &());
+                    new_tree.extend(new_items, &());
+                    cursor.seek(&Count(splice_end), SeekBias::Right, &());
+                    new_tree.push_tree(cursor.slice(&tree_end, SeekBias::Right, &()), &());
                     new_tree
                 };
 
@@ -564,7 +598,7 @@ mod tests {
                 let mut pos = rng.gen_range(0..tree.extent::<Count>().0 + 1);
                 let mut before_start = false;
                 let mut cursor = tree.cursor::<Count, Count>();
-                cursor.seek(&Count(pos), SeekBias::Right);
+                cursor.seek(&Count(pos), SeekBias::Right, &());
 
                 for i in 0..10 {
                     assert_eq!(cursor.start().0, pos);
@@ -612,11 +646,11 @@ mod tests {
                 };
 
                 let mut cursor = tree.cursor::<Count, ()>();
-                cursor.seek(&Count(start), start_bias);
-                let slice = cursor.slice(&Count(end), end_bias);
+                cursor.seek(&Count(start), start_bias, &());
+                let slice = cursor.slice(&Count(end), end_bias, &());
 
-                cursor.seek(&Count(start), start_bias);
-                let summary = cursor.summary::<Sum>(&Count(end), end_bias);
+                cursor.seek(&Count(start), start_bias, &());
+                let summary = cursor.summary::<Sum>(&Count(end), end_bias, &());
 
                 assert_eq!(summary, slice.summary().sum);
             }
@@ -629,7 +663,7 @@ mod tests {
         let tree = SumTree::<u8>::new();
         let mut cursor = tree.cursor::<Count, Sum>();
         assert_eq!(
-            cursor.slice(&Count(0), SeekBias::Right).items(),
+            cursor.slice(&Count(0), SeekBias::Right, &()).items(),
             Vec::<u8>::new()
         );
         assert_eq!(cursor.item(), None);
@@ -638,10 +672,10 @@ mod tests {
 
         // Single-element tree
         let mut tree = SumTree::<u8>::new();
-        tree.extend(vec![1]);
+        tree.extend(vec![1], &());
         let mut cursor = tree.cursor::<Count, Sum>();
         assert_eq!(
-            cursor.slice(&Count(0), SeekBias::Right).items(),
+            cursor.slice(&Count(0), SeekBias::Right, &()).items(),
             Vec::<u8>::new()
         );
         assert_eq!(cursor.item(), Some(&1));
@@ -659,15 +693,15 @@ mod tests {
         assert_eq!(cursor.start(), &Sum(0));
 
         let mut cursor = tree.cursor::<Count, Sum>();
-        assert_eq!(cursor.slice(&Count(1), SeekBias::Right).items(), [1]);
+        assert_eq!(cursor.slice(&Count(1), SeekBias::Right, &()).items(), [1]);
         assert_eq!(cursor.item(), None);
         assert_eq!(cursor.prev_item(), Some(&1));
         assert_eq!(cursor.start(), &Sum(1));
 
-        cursor.seek(&Count(0), SeekBias::Right);
+        cursor.seek(&Count(0), SeekBias::Right, &());
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right)
+                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
                 .items(),
             [1]
         );
@@ -677,10 +711,13 @@ mod tests {
 
         // Multiple-element tree
         let mut tree = SumTree::new();
-        tree.extend(vec![1, 2, 3, 4, 5, 6]);
+        tree.extend(vec![1, 2, 3, 4, 5, 6], &());
         let mut cursor = tree.cursor::<Count, Sum>();
 
-        assert_eq!(cursor.slice(&Count(2), SeekBias::Right).items(), [1, 2]);
+        assert_eq!(
+            cursor.slice(&Count(2), SeekBias::Right, &()).items(),
+            [1, 2]
+        );
         assert_eq!(cursor.item(), Some(&3));
         assert_eq!(cursor.prev_item(), Some(&2));
         assert_eq!(cursor.start(), &Sum(3));
@@ -749,7 +786,7 @@ mod tests {
         let mut cursor = tree.cursor::<Count, Sum>();
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right)
+                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
                 .items(),
             tree.items()
         );
@@ -757,10 +794,10 @@ mod tests {
         assert_eq!(cursor.prev_item(), Some(&6));
         assert_eq!(cursor.start(), &Sum(21));
 
-        cursor.seek(&Count(3), SeekBias::Right);
+        cursor.seek(&Count(3), SeekBias::Right, &());
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right)
+                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
                 .items(),
             [4, 5, 6]
         );
@@ -769,37 +806,46 @@ mod tests {
         assert_eq!(cursor.start(), &Sum(21));
 
         // Seeking can bias left or right
-        cursor.seek(&Count(1), SeekBias::Left);
+        cursor.seek(&Count(1), SeekBias::Left, &());
         assert_eq!(cursor.item(), Some(&1));
-        cursor.seek(&Count(1), SeekBias::Right);
+        cursor.seek(&Count(1), SeekBias::Right, &());
         assert_eq!(cursor.item(), Some(&2));
 
         // Slicing without resetting starts from where the cursor is parked at.
-        cursor.seek(&Count(1), SeekBias::Right);
-        assert_eq!(cursor.slice(&Count(3), SeekBias::Right).items(), vec![2, 3]);
-        assert_eq!(cursor.slice(&Count(6), SeekBias::Left).items(), vec![4, 5]);
-        assert_eq!(cursor.slice(&Count(6), SeekBias::Right).items(), vec![6]);
+        cursor.seek(&Count(1), SeekBias::Right, &());
+        assert_eq!(
+            cursor.slice(&Count(3), SeekBias::Right, &()).items(),
+            vec![2, 3]
+        );
+        assert_eq!(
+            cursor.slice(&Count(6), SeekBias::Left, &()).items(),
+            vec![4, 5]
+        );
+        assert_eq!(
+            cursor.slice(&Count(6), SeekBias::Right, &()).items(),
+            vec![6]
+        );
     }
 
     #[test]
     fn test_edit() {
         let mut tree = SumTree::<u8>::new();
 
-        let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)]);
+        let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)], &());
         assert_eq!(tree.items(), vec![0, 1, 2]);
         assert_eq!(removed, Vec::<u8>::new());
-        assert_eq!(tree.get(&0), Some(&0));
-        assert_eq!(tree.get(&1), Some(&1));
-        assert_eq!(tree.get(&2), Some(&2));
-        assert_eq!(tree.get(&4), None);
+        assert_eq!(tree.get(&0, &()), Some(&0));
+        assert_eq!(tree.get(&1, &()), Some(&1));
+        assert_eq!(tree.get(&2, &()), Some(&2));
+        assert_eq!(tree.get(&4, &()), None);
 
-        let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)]);
+        let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)], &());
         assert_eq!(tree.items(), vec![1, 2, 4]);
         assert_eq!(removed, vec![0, 2]);
-        assert_eq!(tree.get(&0), None);
-        assert_eq!(tree.get(&1), Some(&1));
-        assert_eq!(tree.get(&2), Some(&2));
-        assert_eq!(tree.get(&4), Some(&4));
+        assert_eq!(tree.get(&0, &()), None);
+        assert_eq!(tree.get(&1, &()), Some(&1));
+        assert_eq!(tree.get(&2, &()), Some(&2));
+        assert_eq!(tree.get(&4, &()), Some(&4));
     }
 
     #[derive(Clone, Default, Debug)]
@@ -837,14 +883,10 @@ mod tests {
         }
     }
 
-    impl<'a> Dimension<'a, IntegersSummary> for u8 {
-        fn add_summary(&mut self, summary: &IntegersSummary) {
-            *self = summary.max;
-        }
-    }
+    impl Summary for IntegersSummary {
+        type Context = ();
 
-    impl<'a> AddAssign<&'a Self> for IntegersSummary {
-        fn add_assign(&mut self, other: &Self) {
+        fn add_summary(&mut self, other: &Self, _: &()) {
             self.count.0 += &other.count.0;
             self.sum.0 += &other.sum.0;
             self.contains_even |= other.contains_even;
@@ -852,6 +894,12 @@ mod tests {
         }
     }
 
+    impl<'a> Dimension<'a, IntegersSummary> for u8 {
+        fn add_summary(&mut self, summary: &IntegersSummary) {
+            *self = summary.max;
+        }
+    }
+
     impl<'a> Dimension<'a, IntegersSummary> for Count {
         fn add_summary(&mut self, summary: &IntegersSummary) {
             self.0 += summary.count.0;

zed/src/util.rs 🔗

@@ -30,37 +30,6 @@ where
     }
 }
 
-pub fn find_insertion_index<'a, F, T, E>(slice: &'a [T], mut f: F) -> Result<usize, E>
-where
-    F: FnMut(&'a T) -> Result<Ordering, E>,
-{
-    use Ordering::*;
-
-    let s = slice;
-    let mut size = s.len();
-    if size == 0 {
-        return Ok(0);
-    }
-    let mut base = 0usize;
-    while size > 1 {
-        let half = size / 2;
-        let mid = base + half;
-        // mid is always in [0, size), that means mid is >= 0 and < size.
-        // mid >= 0: by definition
-        // mid < size: mid = size / 2 + size / 4 + size / 8 ...
-        let cmp = f(unsafe { s.get_unchecked(mid) })?;
-        base = if cmp == Greater { base } else { mid };
-        size -= half;
-    }
-    // base is always in [0, size) because base <= mid.
-    let cmp = f(unsafe { s.get_unchecked(base) })?;
-    if cmp == Equal {
-        Ok(base)
-    } else {
-        Ok(base + (cmp == Less) as usize)
-    }
-}
-
 pub struct RandomCharIter<T: Rng>(T);
 
 impl<T: Rng> RandomCharIter<T> {
@@ -85,14 +54,6 @@ impl<T: Rng> Iterator for RandomCharIter<T> {
 mod tests {
     use super::*;
 
-    #[test]
-    fn test_find_insertion_index() {
-        assert_eq!(
-            find_insertion_index(&[0, 4, 8], |probe| Ok::<Ordering, ()>(probe.cmp(&2))),
-            Ok(1)
-        );
-    }
-
     #[test]
     fn test_extend_sorted() {
         let mut vec = vec![];

zed/src/worktree.rs 🔗

@@ -24,7 +24,7 @@ use std::{
     fmt, fs,
     future::Future,
     io::{self, Read, Write},
-    ops::{AddAssign, Deref},
+    ops::Deref,
     os::unix::{ffi::OsStrExt, fs::MetadataExt},
     path::{Path, PathBuf},
     sync::{Arc, Weak},
@@ -249,9 +249,10 @@ impl Snapshot {
 
     #[cfg(test)]
     pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
-        let mut cursor = self.entries.cursor::<(), ()>();
-        cursor.next();
-        cursor.map(|entry| entry.path())
+        self.entries
+            .cursor::<(), ()>()
+            .skip(1)
+            .map(|entry| entry.path())
     }
 
     pub fn visible_files(&self, start: usize) -> FileIter {
@@ -274,7 +275,7 @@ impl Snapshot {
 
     fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
         let mut cursor = self.entries.cursor::<_, ()>();
-        if cursor.seek(&PathSearch::Exact(path.as_ref()), SeekBias::Left) {
+        if cursor.seek(&PathSearch::Exact(path.as_ref()), SeekBias::Left, &()) {
             cursor.item()
         } else {
             None
@@ -296,7 +297,7 @@ impl Snapshot {
             self.ignores
                 .insert(ignore_dir_path.into(), (Arc::new(ignore), self.scan_id));
         }
-        self.entries.insert(entry);
+        self.entries.insert(entry, &());
     }
 
     fn populate_dir(
@@ -309,7 +310,7 @@ impl Snapshot {
 
         let mut parent_entry = self
             .entries
-            .get(&PathKey(parent_path.clone()))
+            .get(&PathKey(parent_path.clone()), &())
             .unwrap()
             .clone();
         if let Some(ignore) = ignore {
@@ -325,15 +326,15 @@ impl Snapshot {
         for entry in entries {
             edits.push(Edit::Insert(entry));
         }
-        self.entries.edit(edits);
+        self.entries.edit(edits, &());
     }
 
     fn remove_path(&mut self, path: &Path) {
         let new_entries = {
             let mut cursor = self.entries.cursor::<_, ()>();
-            let mut new_entries = cursor.slice(&PathSearch::Exact(path), SeekBias::Left);
-            cursor.seek_forward(&PathSearch::Successor(path), SeekBias::Left);
-            new_entries.push_tree(cursor.suffix());
+            let mut new_entries = cursor.slice(&PathSearch::Exact(path), SeekBias::Left, &());
+            cursor.seek_forward(&PathSearch::Successor(path), SeekBias::Left, &());
+            new_entries.push_tree(cursor.suffix(&()), &());
             new_entries
         };
         self.entries = new_entries;
@@ -543,8 +544,10 @@ impl Default for EntrySummary {
     }
 }
 
-impl<'a> AddAssign<&'a EntrySummary> for EntrySummary {
-    fn add_assign(&mut self, rhs: &'a EntrySummary) {
+impl sum_tree::Summary for EntrySummary {
+    type Context = ();
+
+    fn add_summary(&mut self, rhs: &Self, _: &()) {
         self.max_path = rhs.max_path.clone();
         self.file_count += rhs.file_count;
         self.visible_file_count += rhs.visible_file_count;
@@ -1073,7 +1076,7 @@ impl BackgroundScanner {
                 edits.push(Edit::Insert(entry));
             }
         }
-        self.snapshot.lock().entries.edit(edits);
+        self.snapshot.lock().entries.edit(edits, &());
     }
 
     fn fs_entry_for_path(&self, path: Arc<Path>, abs_path: &Path) -> Result<Option<Entry>> {
@@ -1176,13 +1179,13 @@ pub enum FileIter<'a> {
 impl<'a> FileIter<'a> {
     fn all(snapshot: &'a Snapshot, start: usize) -> Self {
         let mut cursor = snapshot.entries.cursor();
-        cursor.seek(&FileCount(start), SeekBias::Right);
+        cursor.seek(&FileCount(start), SeekBias::Right, &());
         Self::All(cursor)
     }
 
     fn visible(snapshot: &'a Snapshot, start: usize) -> Self {
         let mut cursor = snapshot.entries.cursor();
-        cursor.seek(&VisibleFileCount(start), SeekBias::Right);
+        cursor.seek(&VisibleFileCount(start), SeekBias::Right, &());
         Self::Visible(cursor)
     }
 
@@ -1190,11 +1193,11 @@ impl<'a> FileIter<'a> {
         match self {
             Self::All(cursor) => {
                 let ix = *cursor.start();
-                cursor.seek_forward(&FileCount(ix.0 + 1), SeekBias::Right);
+                cursor.seek_forward(&FileCount(ix.0 + 1), SeekBias::Right, &());
             }
             Self::Visible(cursor) => {
                 let ix = *cursor.start();
-                cursor.seek_forward(&VisibleFileCount(ix.0 + 1), SeekBias::Right);
+                cursor.seek_forward(&VisibleFileCount(ix.0 + 1), SeekBias::Right, &());
             }
         }
     }
@@ -1228,7 +1231,7 @@ struct ChildEntriesIter<'a> {
 impl<'a> ChildEntriesIter<'a> {
     fn new(parent_path: &'a Path, snapshot: &'a Snapshot) -> Self {
         let mut cursor = snapshot.entries.cursor();
-        cursor.seek(&PathSearch::Exact(parent_path), SeekBias::Right);
+        cursor.seek(&PathSearch::Exact(parent_path), SeekBias::Right, &());
         Self {
             parent_path,
             cursor,
@@ -1243,7 +1246,7 @@ impl<'a> Iterator for ChildEntriesIter<'a> {
         if let Some(item) = self.cursor.item() {
             if item.path().starts_with(self.parent_path) {
                 self.cursor
-                    .seek_forward(&PathSearch::Successor(item.path()), SeekBias::Left);
+                    .seek_forward(&PathSearch::Successor(item.path()), SeekBias::Left, &());
                 Some(item)
             } else {
                 None