Merge pull request #81 from zed-industries/versioned-anchors

Max Brunsfeld created

Represent edit positions and anchors as versioned offsets

Change summary

zed/src/editor.rs                      |   12 
zed/src/editor/buffer.rs               | 1222 +++++++++------------------
zed/src/editor/buffer/anchor.rs        |   62 
zed/src/editor/buffer/rope.rs          |   65 
zed/src/editor/display_map/fold_map.rs |  135 +-
zed/src/operation_queue.rs             |    2 
zed/src/sum_tree.rs                    |  187 ++-
zed/src/sum_tree/cursor.rs             |  457 ++++------
zed/src/time.rs                        |   76 +
zed/src/util.rs                        |   23 
zed/src/worktree.rs                    |   31 
11 files changed, 919 insertions(+), 1,353 deletions(-)

Detailed changes

zed/src/editor.rs 🔗

@@ -5,7 +5,7 @@ pub mod movement;
 
 use crate::{
     settings::{Settings, StyleId},
-    util::post_inc,
+    util::{post_inc, Bias},
     workspace,
     worktree::FileHandle,
 };
@@ -723,9 +723,7 @@ impl Editor {
         let mut new_selections = Vec::new();
         self.buffer.update(cx, |buffer, cx| {
             let edit_ranges = old_selections.iter().map(|(_, range)| range.clone());
-            if let Err(error) = buffer.edit(edit_ranges, text.as_str(), Some(cx)) {
-                log::error!("error inserting text: {}", error);
-            };
+            buffer.edit(edit_ranges, text.as_str(), Some(cx));
             let text_len = text.len() as isize;
             let mut delta = 0_isize;
             new_selections = old_selections
@@ -4139,12 +4137,6 @@ mod tests {
     }
 }
 
-#[derive(Copy, Clone)]
-pub enum Bias {
-    Left,
-    Right,
-}
-
 trait RangeExt<T> {
     fn sorted(&self) -> Range<T>;
     fn to_inclusive(&self) -> RangeInclusive<T>;

zed/src/editor/buffer.rs 🔗

@@ -13,12 +13,12 @@ use similar::{ChangeTag, TextDiff};
 use tree_sitter::{InputEdit, Parser, QueryCursor};
 
 use crate::{
-    editor::Bias,
     language::{Language, Tree},
     operation_queue::{self, OperationQueue},
     settings::{StyleId, ThemeMap},
-    sum_tree::{self, FilterCursor, SeekBias, SumTree},
+    sum_tree::{self, FilterCursor, SumTree},
     time::{self, ReplicaId},
+    util::Bias,
     worktree::FileHandle,
 };
 use anyhow::{anyhow, Result};
@@ -28,8 +28,7 @@ use std::{
     cell::RefCell,
     cmp,
     hash::BuildHasher,
-    iter::{self, Iterator},
-    mem,
+    iter::Iterator,
     ops::{Deref, DerefMut, Range},
     str,
     sync::Arc,
@@ -109,7 +108,6 @@ pub struct Buffer {
     fragments: SumTree<Fragment>,
     visible_text: Rope,
     deleted_text: Rope,
-    insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
     pub version: time::Global,
     saved_version: time::Global,
     saved_mtime: SystemTime,
@@ -171,7 +169,7 @@ impl History {
     }
 
     fn push(&mut self, op: EditOperation) {
-        self.ops.insert(op.id, op);
+        self.ops.insert(op.timestamp.local(), op);
     }
 
     fn start_transaction(
@@ -328,61 +326,62 @@ struct Diff {
     changes: Vec<(ChangeTag, usize)>,
 }
 
-#[derive(Clone, Eq, PartialEq, Debug)]
-pub struct Insertion {
-    id: time::Local,
-    parent_id: time::Local,
-    offset_in_parent: usize,
-    lamport_timestamp: time::Lamport,
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+struct InsertionTimestamp {
+    replica_id: ReplicaId,
+    local: time::Seq,
+    lamport: time::Seq,
+}
+
+impl InsertionTimestamp {
+    fn local(&self) -> time::Local {
+        time::Local {
+            replica_id: self.replica_id,
+            value: self.local,
+        }
+    }
+
+    fn lamport(&self) -> time::Lamport {
+        time::Lamport {
+            replica_id: self.replica_id,
+            value: self.lamport,
+        }
+    }
 }
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 struct Fragment {
-    id: FragmentId,
-    insertion: Arc<Insertion>,
-    range_in_insertion: Range<usize>,
+    timestamp: InsertionTimestamp,
+    len: usize,
+    visible: bool,
     deletions: HashSet<time::Local>,
     max_undos: time::Global,
-    visible: bool,
 }
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub struct FragmentSummary {
     text: FragmentTextSummary,
-    max_fragment_id: FragmentId,
     max_version: time::Global,
+    min_insertion_version: time::Global,
+    max_insertion_version: time::Global,
 }
 
-#[derive(Default, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Default, Clone, Debug, PartialEq, Eq)]
 struct FragmentTextSummary {
     visible: usize,
     deleted: usize,
 }
 
 impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
-    fn add_summary(&mut self, summary: &'a FragmentSummary) {
+    fn add_summary(&mut self, summary: &'a FragmentSummary, _: &Option<time::Global>) {
         self.visible += summary.text.visible;
         self.deleted += summary.text.deleted;
     }
 }
 
-#[derive(Eq, PartialEq, Clone, Debug)]
-struct InsertionSplit {
-    extent: usize,
-    fragment_id: FragmentId,
-}
-
-#[derive(Eq, PartialEq, Clone, Debug)]
-struct InsertionSplitSummary {
-    extent: usize,
-}
-
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Operation {
-    Edit {
-        edit: EditOperation,
-        lamport_timestamp: time::Lamport,
-    },
+    Edit(EditOperation),
     Undo {
         undo: UndoOperation,
         lamport_timestamp: time::Lamport,
@@ -396,12 +395,9 @@ pub enum Operation {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct EditOperation {
-    id: time::Local,
-    start_id: time::Local,
-    start_offset: usize,
-    end_id: time::Local,
-    end_offset: usize,
-    version_in_range: time::Global,
+    timestamp: InsertionTimestamp,
+    version: time::Global,
+    ranges: Vec<Range<usize>>,
     new_text: Option<String>,
 }
 
@@ -474,53 +470,19 @@ impl Buffer {
             saved_mtime = UNIX_EPOCH;
         }
 
-        let mut visible_text = Rope::new();
-        let mut insertion_splits = HashMap::default();
         let mut fragments = SumTree::new();
 
-        let base_text = Rope::from(history.base_text.as_ref());
-        let base_insertion = Arc::new(Insertion {
-            id: time::Local::default(),
-            parent_id: time::Local::default(),
-            offset_in_parent: 0,
-            lamport_timestamp: time::Lamport::default(),
-        });
-
-        insertion_splits.insert(
-            base_insertion.id,
-            SumTree::from_item(
-                InsertionSplit {
-                    fragment_id: FragmentId::min_value().clone(),
-                    extent: 0,
-                },
-                &(),
-            ),
-        );
-        fragments.push(
-            Fragment::new(
-                FragmentId::min_value().clone(),
-                base_insertion.clone(),
-                0..0,
-            ),
-            &(),
-        );
-
-        if base_text.len() > 0 {
-            let base_fragment_id =
-                FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value());
-            let range_in_insertion = 0..base_text.len();
-
-            visible_text = base_text.clone();
-            insertion_splits.get_mut(&base_insertion.id).unwrap().push(
-                InsertionSplit {
-                    fragment_id: base_fragment_id.clone(),
-                    extent: range_in_insertion.end,
-                },
-                &(),
-            );
+        let visible_text = Rope::from(history.base_text.as_ref());
+        if visible_text.len() > 0 {
             fragments.push(
-                Fragment::new(base_fragment_id, base_insertion, range_in_insertion.clone()),
-                &(),
+                Fragment {
+                    timestamp: Default::default(),
+                    len: visible_text.len(),
+                    visible: true,
+                    deletions: Default::default(),
+                    max_undos: Default::default(),
+                },
+                &None,
             );
         }
 
@@ -528,7 +490,6 @@ impl Buffer {
             visible_text,
             deleted_text: Rope::new(),
             fragments,
-            insertion_splits,
             version: time::Global::new(),
             saved_version: time::Global::new(),
             last_edit: time::Local::default(),
@@ -781,12 +742,11 @@ impl Buffer {
                 match tag {
                     ChangeTag::Equal => offset += len,
                     ChangeTag::Delete => {
-                        operations.extend_from_slice(&self.edit(Some(range), "", Some(cx)).unwrap())
+                        operations.push(self.edit(Some(range), "", Some(cx)).unwrap())
                     }
                     ChangeTag::Insert => {
-                        operations.extend_from_slice(
-                            &self
-                                .edit(Some(offset..offset), &diff.new_text[range], Some(cx))
+                        operations.push(
+                            self.edit(Some(offset..offset), &diff.new_text[range], Some(cx))
                                 .unwrap(),
                         );
                         offset += len;
@@ -825,7 +785,7 @@ impl Buffer {
     }
 
     pub fn len(&self) -> usize {
-        self.fragments.extent::<usize>()
+        self.fragments.extent::<usize>(&None)
     }
 
     pub fn line_len(&self, row: u32) -> u32 {
@@ -871,9 +831,10 @@ impl Buffer {
 
     pub fn edits_since<'a>(&'a self, since: time::Global) -> impl 'a + Iterator<Item = Edit> {
         let since_2 = since.clone();
-        let cursor = self
-            .fragments
-            .filter(move |summary| summary.max_version.changed_since(&since_2));
+        let cursor = self.fragments.filter(
+            move |summary| summary.max_version.changed_since(&since_2),
+            &None,
+        );
 
         Edits {
             deleted_text: &self.deleted_text,
@@ -951,56 +912,60 @@ impl Buffer {
 
     pub fn edit<I, S, T>(
         &mut self,
-        old_ranges: I,
+        ranges_iter: I,
         new_text: T,
         cx: Option<&mut ModelContext<Self>>,
-    ) -> Result<Vec<Operation>>
+    ) -> Option<Operation>
     where
         I: IntoIterator<Item = Range<S>>,
         S: ToOffset,
         T: Into<String>,
     {
-        self.start_transaction_at(None, Instant::now())?;
-
         let new_text = new_text.into();
-        let old_ranges = old_ranges
-            .into_iter()
-            .map(|range| range.start.to_offset(self)..range.end.to_offset(self))
-            .collect::<Vec<Range<usize>>>();
-
         let new_text = if new_text.len() > 0 {
             Some(new_text)
         } else {
             None
         };
-
         let has_new_text = new_text.is_some();
-        let ops = self.splice_fragments(
-            old_ranges
-                .into_iter()
-                .filter(|old_range| has_new_text || old_range.end > old_range.start),
-            new_text.into(),
-        );
 
-        for op in &ops {
-            if let Operation::Edit { edit, .. } = op {
-                self.history.push(edit.clone());
-                self.history.push_undo(edit.id);
+        // Skip invalid ranges and coalesce contiguous ones.
+        let mut ranges: Vec<Range<usize>> = Vec::new();
+        for range in ranges_iter {
+            let range = range.start.to_offset(self)..range.end.to_offset(self);
+            if has_new_text || !range.is_empty() {
+                if let Some(prev_range) = ranges.last_mut() {
+                    if prev_range.end >= range.start {
+                        prev_range.end = cmp::max(prev_range.end, range.end);
+                    } else {
+                        ranges.push(range);
+                    }
+                } else {
+                    ranges.push(range);
+                }
             }
         }
 
-        if let Some(op) = ops.last() {
-            if let Operation::Edit { edit, .. } = op {
-                self.last_edit = edit.id;
-                self.version.observe(edit.id);
-            } else {
-                unreachable!()
-            }
-        }
+        if ranges.is_empty() {
+            None
+        } else {
+            self.start_transaction_at(None, Instant::now()).unwrap();
+            let timestamp = InsertionTimestamp {
+                replica_id: self.replica_id,
+                local: self.local_clock.tick().value,
+                lamport: self.lamport_clock.tick().value,
+            };
+            let edit = self.apply_local_edit(&ranges, new_text, timestamp);
+
+            self.history.push(edit.clone());
+            self.history.push_undo(edit.timestamp.local());
+            self.last_edit = edit.timestamp.local();
+            self.version.observe(edit.timestamp.local());
 
-        self.end_transaction_at(None, Instant::now(), cx)?;
+            self.end_transaction_at(None, Instant::now(), cx).unwrap();
 
-        Ok(ops)
+            Some(Operation::Edit(edit))
+        }
     }
 
     fn did_edit(&self, was_dirty: bool, cx: &mut ModelContext<Self>) {
@@ -1120,23 +1085,15 @@ impl Buffer {
 
     fn apply_op(&mut self, op: Operation) -> Result<()> {
         match op {
-            Operation::Edit {
-                edit,
-                lamport_timestamp,
-                ..
-            } => {
-                if !self.version.observed(edit.id) {
-                    self.apply_edit(
-                        edit.start_id,
-                        edit.start_offset,
-                        edit.end_id,
-                        edit.end_offset,
+            Operation::Edit(edit) => {
+                if !self.version.observed(edit.timestamp.local()) {
+                    self.apply_remote_edit(
+                        &edit.version,
+                        &edit.ranges,
                         edit.new_text.as_deref(),
-                        &edit.version_in_range,
-                        edit.id,
-                        lamport_timestamp,
-                    )?;
-                    self.version.observe(edit.id);
+                        edit.timestamp,
+                    );
+                    self.version.observe(edit.timestamp.local());
                     self.history.push(edit);
                 }
             }
@@ -1167,154 +1124,149 @@ impl Buffer {
         Ok(())
     }
 
-    fn apply_edit(
+    fn apply_remote_edit(
         &mut self,
-        start_id: time::Local,
-        start_offset: usize,
-        end_id: time::Local,
-        end_offset: usize,
-        mut new_text: Option<&str>,
-        version_in_range: &time::Global,
-        local_timestamp: time::Local,
-        lamport_timestamp: time::Lamport,
-    ) -> Result<()> {
-        let start_fragment_id = self.resolve_fragment_id(start_id, start_offset)?;
-        let end_fragment_id = self.resolve_fragment_id(end_id, end_offset)?;
-
-        let mut old_visible_text = Rope::new();
-        let mut old_deleted_text = Rope::new();
-        let mut old_fragments = SumTree::new();
-        mem::swap(&mut old_visible_text, &mut self.visible_text);
-        mem::swap(&mut old_deleted_text, &mut self.deleted_text);
-        mem::swap(&mut old_fragments, &mut self.fragments);
-
-        let mut fragments_cursor = old_fragments.cursor::<FragmentIdRef, FragmentTextSummary>();
+        version: &time::Global,
+        ranges: &[Range<usize>],
+        new_text: Option<&str>,
+        timestamp: InsertionTimestamp,
+    ) {
+        if ranges.is_empty() {
+            return;
+        }
 
-        let mut new_fragments =
-            fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
+        let cx = Some(version.clone());
         let mut new_ropes =
-            RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0));
+            RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
+        let mut old_fragments = self.fragments.cursor::<VersionedOffset, VersionedOffset>();
+        let mut new_fragments =
+            old_fragments.slice(&VersionedOffset::Offset(ranges[0].start), Bias::Left, &cx);
         new_ropes.push_tree(new_fragments.summary().text);
 
-        let start_fragment = fragments_cursor.item().unwrap();
-        if start_offset == start_fragment.range_in_insertion.end {
-            let fragment = fragments_cursor.item().unwrap().clone();
-            new_ropes.push_fragment(&fragment, fragment.visible);
-            new_fragments.push(fragment, &());
-            fragments_cursor.next();
-        }
+        let mut fragment_start = old_fragments.start().offset();
+        for range in ranges {
+            let fragment_end = old_fragments.end(&cx).offset();
+
+            // If the current fragment ends before this range, then jump ahead to the first fragment
+            // that extends past the start of this range, reusing any intervening fragments.
+            if fragment_end < range.start {
+                // If the current fragment has been partially consumed, then consume the rest of it
+                // and advance to the next fragment before slicing.
+                if fragment_start > old_fragments.start().offset() {
+                    if fragment_end > fragment_start {
+                        let mut suffix = old_fragments.item().unwrap().clone();
+                        suffix.len = fragment_end - fragment_start;
+                        new_ropes.push_fragment(&suffix, suffix.visible);
+                        new_fragments.push(suffix, &None);
+                    }
+                    old_fragments.next(&cx);
+                }
 
-        while let Some(fragment) = fragments_cursor.item() {
-            if new_text.is_none() && fragment.id > end_fragment_id {
-                break;
+                let slice =
+                    old_fragments.slice(&VersionedOffset::Offset(range.start), Bias::Left, &cx);
+                new_ropes.push_tree(slice.summary().text);
+                new_fragments.push_tree(slice, &None);
+                fragment_start = old_fragments.start().offset();
             }
 
-            let mut fragment = fragment.clone();
+            // If we are at the end of a non-concurrent fragment, advance to the next one.
+            let fragment_end = old_fragments.end(&cx).offset();
+            if fragment_end == range.start && fragment_end > fragment_start {
+                let mut fragment = old_fragments.item().unwrap().clone();
+                fragment.len = fragment_end - fragment_start;
+                new_ropes.push_fragment(&fragment, fragment.visible);
+                new_fragments.push(fragment, &None);
+                old_fragments.next(&cx);
+                fragment_start = old_fragments.start().offset();
+            }
 
-            if fragment.id == start_fragment_id || fragment.id == end_fragment_id {
-                let split_start = if start_fragment_id == fragment.id {
-                    start_offset
-                } else {
-                    fragment.range_in_insertion.start
-                };
-                let split_end = if end_fragment_id == fragment.id {
-                    end_offset
-                } else {
-                    fragment.range_in_insertion.end
-                };
-                let (before_range, within_range, after_range) = self.split_fragment(
-                    fragments_cursor.prev_item().as_ref().unwrap(),
-                    &fragment,
-                    split_start..split_end,
-                );
-                let insertion = if let Some(new_text) = new_text {
-                    let prev_fragment = fragments_cursor.prev_item();
-                    Some(self.build_fragment_to_insert(
-                        before_range.as_ref().or(prev_fragment).unwrap(),
-                        within_range.as_ref().or(after_range.as_ref()),
-                        new_text,
-                        local_timestamp,
-                        lamport_timestamp,
-                    ))
+            // Skip over insertions that are concurrent to this edit, but have a lower lamport
+            // timestamp.
+            while let Some(fragment) = old_fragments.item() {
+                if fragment_start == range.start
+                    && fragment.timestamp.lamport() > timestamp.lamport()
+                {
+                    new_ropes.push_fragment(fragment, fragment.visible);
+                    new_fragments.push(fragment.clone(), &None);
+                    old_fragments.next(&cx);
+                    debug_assert_eq!(fragment_start, range.start);
                 } else {
-                    None
-                };
-                if let Some(fragment) = before_range {
-                    new_ropes.push_fragment(&fragment, fragment.visible);
-                    new_fragments.push(fragment, &());
-                }
-                if let Some(fragment) = insertion {
-                    new_ropes.push_str(new_text.take().unwrap());
-                    new_fragments.push(fragment, &());
+                    break;
                 }
-                if let Some(mut fragment) = within_range {
-                    let fragment_was_visible = fragment.visible;
-                    if fragment.was_visible(&version_in_range, &self.undo_map) {
-                        fragment.deletions.insert(local_timestamp);
-                        if fragment.visible {
-                            fragment.visible = false;
-                        }
-                    }
+            }
+            debug_assert!(fragment_start <= range.start);
+
+            // Preserve any portion of the current fragment that precedes this range.
+            if fragment_start < range.start {
+                let mut prefix = old_fragments.item().unwrap().clone();
+                prefix.len = range.start - fragment_start;
+                fragment_start = range.start;
+                new_ropes.push_fragment(&prefix, prefix.visible);
+                new_fragments.push(prefix, &None);
+            }
 
-                    new_ropes.push_fragment(&fragment, fragment_was_visible);
-                    new_fragments.push(fragment, &());
+            // Insert the new text before any existing fragments within the range.
+            if let Some(new_text) = new_text {
+                new_ropes.push_str(new_text);
+                new_fragments.push(
+                    Fragment {
+                        timestamp,
+                        len: new_text.len(),
+                        deletions: Default::default(),
+                        max_undos: Default::default(),
+                        visible: true,
+                    },
+                    &None,
+                );
+            }
+
+            // Advance through every fragment that intersects this range, marking the intersecting
+            // portions as deleted.
+            while fragment_start < range.end {
+                let fragment = old_fragments.item().unwrap();
+                let fragment_end = old_fragments.end(&cx).offset();
+                let mut intersection = fragment.clone();
+                let intersection_end = cmp::min(range.end, fragment_end);
+                if fragment.was_visible(version, &self.undo_map) {
+                    intersection.len = intersection_end - fragment_start;
+                    intersection.deletions.insert(timestamp.local());
+                    intersection.visible = false;
                 }
-                if let Some(fragment) = after_range {
-                    new_ropes.push_fragment(&fragment, fragment.visible);
-                    new_fragments.push(fragment, &());
+                if intersection.len > 0 {
+                    new_ropes.push_fragment(&intersection, fragment.visible);
+                    new_fragments.push(intersection, &None);
+                    fragment_start = intersection_end;
                 }
-            } else {
-                if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
-                    let new_text = new_text.take().unwrap();
-                    let fragment = self.build_fragment_to_insert(
-                        fragments_cursor.prev_item().as_ref().unwrap(),
-                        Some(&fragment),
-                        new_text,
-                        local_timestamp,
-                        lamport_timestamp,
-                    );
-                    new_ropes.push_str(new_text);
-                    new_fragments.push(fragment, &());
-                }
-
-                let fragment_was_visible = fragment.visible;
-                if fragment.id < end_fragment_id
-                    && fragment.was_visible(&version_in_range, &self.undo_map)
-                {
-                    fragment.deletions.insert(local_timestamp);
-                    if fragment.visible {
-                        fragment.visible = false;
-                    }
+                if fragment_end <= range.end {
+                    old_fragments.next(&cx);
                 }
-
-                new_ropes.push_fragment(&fragment, fragment_was_visible);
-                new_fragments.push(fragment, &());
             }
-
-            fragments_cursor.next();
         }
 
-        if let Some(new_text) = new_text {
-            let fragment = self.build_fragment_to_insert(
-                fragments_cursor.prev_item().as_ref().unwrap(),
-                None,
-                new_text,
-                local_timestamp,
-                lamport_timestamp,
-            );
-            new_ropes.push_str(new_text);
-            new_fragments.push(fragment, &());
+        // If the current fragment has been partially consumed, then consume the rest of it
+        // and advance to the next fragment before slicing.
+        if fragment_start > old_fragments.start().offset() {
+            let fragment_end = old_fragments.end(&cx).offset();
+            if fragment_end > fragment_start {
+                let mut suffix = old_fragments.item().unwrap().clone();
+                suffix.len = fragment_end - fragment_start;
+                new_ropes.push_fragment(&suffix, suffix.visible);
+                new_fragments.push(suffix, &None);
+            }
+            old_fragments.next(&cx);
         }
 
+        let suffix = old_fragments.suffix(&cx);
+        new_ropes.push_tree(suffix.summary().text);
+        new_fragments.push_tree(suffix, &None);
         let (visible_text, deleted_text) = new_ropes.finish();
-        new_fragments.push_tree(fragments_cursor.suffix(&()), &());
+        drop(old_fragments);
 
         self.fragments = new_fragments;
         self.visible_text = visible_text;
         self.deleted_text = deleted_text;
-        self.local_clock.observe(local_timestamp);
-        self.lamport_clock.observe(lamport_timestamp);
-        Ok(())
+        self.local_clock.observe(timestamp.local());
+        self.lamport_clock.observe(timestamp.lamport());
     }
 
     pub fn undo(&mut self, mut cx: Option<&mut ModelContext<Self>>) -> Vec<Operation> {
@@ -1387,85 +1339,60 @@ impl Buffer {
     }
 
     fn apply_undo(&mut self, undo: UndoOperation) -> Result<()> {
-        let mut new_fragments;
-        let mut old_visible_text = Rope::new();
-        let mut old_deleted_text = Rope::new();
-        mem::swap(&mut old_visible_text, &mut self.visible_text);
-        mem::swap(&mut old_deleted_text, &mut self.deleted_text);
-        let mut new_ropes =
-            RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0));
-
         self.undo_map.insert(undo);
         let edit = &self.history.ops[&undo.edit_id];
-        let start_fragment_id = self.resolve_fragment_id(edit.start_id, edit.start_offset)?;
-        let end_fragment_id = self.resolve_fragment_id(edit.end_id, edit.end_offset)?;
+        let version = Some(edit.version.clone());
 
-        let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, ()>();
+        let mut old_fragments = self.fragments.cursor::<VersionedOffset, VersionedOffset>();
+        old_fragments.seek(&VersionedOffset::Offset(0), Bias::Left, &version);
 
-        if edit.start_id == edit.end_id && edit.start_offset == edit.end_offset {
-            let splits = &self.insertion_splits[&undo.edit_id];
-            let mut insertion_splits = splits.cursor::<(), ()>().map(|s| &s.fragment_id).peekable();
+        let mut new_fragments = SumTree::new();
+        let mut new_ropes =
+            RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
 
-            let first_split_id = insertion_splits.next().unwrap();
-            new_fragments =
-                fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &());
-            new_ropes.push_tree(new_fragments.summary().text);
+        for range in &edit.ranges {
+            let mut end_offset = old_fragments.end(&version).offset();
 
-            loop {
-                let mut fragment = fragments_cursor.item().unwrap().clone();
-                let was_visible = fragment.visible;
-                fragment.visible = fragment.is_visible(&self.undo_map);
-                fragment.max_undos.observe(undo.id);
-
-                new_ropes.push_fragment(&fragment, was_visible);
-                new_fragments.push(fragment.clone(), &());
-
-                fragments_cursor.next();
-                if let Some(split_id) = insertion_splits.next() {
-                    let slice =
-                        fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &());
-                    new_ropes.push_tree(slice.summary().text);
-                    new_fragments.push_tree(slice, &());
-                } else {
-                    break;
-                }
+            if end_offset < range.start {
+                let preceding_fragments = old_fragments.slice(
+                    &VersionedOffset::Offset(range.start),
+                    Bias::Left,
+                    &version,
+                );
+                new_ropes.push_tree(preceding_fragments.summary().text);
+                new_fragments.push_tree(preceding_fragments, &None);
             }
-        } else {
-            new_fragments = fragments_cursor.slice(
-                &FragmentIdRef::new(&start_fragment_id),
-                SeekBias::Left,
-                &(),
-            );
-            new_ropes.push_tree(new_fragments.summary().text);
 
-            while let Some(fragment) = fragments_cursor.item() {
-                if fragment.id > end_fragment_id {
-                    break;
-                } else {
+            while end_offset <= range.end {
+                if let Some(fragment) = old_fragments.item() {
                     let mut fragment = fragment.clone();
                     let fragment_was_visible = fragment.visible;
-                    if edit.version_in_range.observed(fragment.insertion.id)
-                        || fragment.insertion.id == undo.edit_id
+                    if fragment.was_visible(&edit.version, &self.undo_map)
+                        || fragment.timestamp.local() == edit.timestamp.local()
                     {
                         fragment.visible = fragment.is_visible(&self.undo_map);
                         fragment.max_undos.observe(undo.id);
                     }
-
                     new_ropes.push_fragment(&fragment, fragment_was_visible);
-                    new_fragments.push(fragment, &());
-                    fragments_cursor.next();
+                    new_fragments.push(fragment, &None);
+
+                    old_fragments.next(&version);
+                    end_offset = old_fragments.end(&version).offset();
+                } else {
+                    break;
                 }
             }
         }
 
-        new_fragments.push_tree(fragments_cursor.suffix(&()), &());
-        let (visible_text, deleted_text) = new_ropes.finish();
-        drop(fragments_cursor);
+        let suffix = old_fragments.suffix(&version);
+        new_ropes.push_tree(suffix.summary().text);
+        new_fragments.push_tree(suffix, &None);
 
+        drop(old_fragments);
+        let (visible_text, deleted_text) = new_ropes.finish();
         self.fragments = new_fragments;
         self.visible_text = visible_text;
         self.deleted_text = deleted_text;
-
         Ok(())
     }
 
@@ -1489,25 +1416,17 @@ impl Buffer {
             false
         } else {
             match op {
-                Operation::Edit { edit, .. } => {
-                    self.version.observed(edit.start_id)
-                        && self.version.observed(edit.end_id)
-                        && edit.version_in_range <= self.version
-                }
+                Operation::Edit(edit) => self.version >= edit.version,
                 Operation::Undo { undo, .. } => self.version.observed(undo.edit_id),
                 Operation::UpdateSelections { selections, .. } => {
                     if let Some(selections) = selections {
                         selections.iter().all(|selection| {
-                            let contains_start = match selection.start {
-                                Anchor::Middle { insertion_id, .. } => {
-                                    self.version.observed(insertion_id)
-                                }
+                            let contains_start = match &selection.start {
+                                Anchor::Middle { version, .. } => self.version >= *version,
                                 _ => true,
                             };
-                            let contains_end = match selection.end {
-                                Anchor::Middle { insertion_id, .. } => {
-                                    self.version.observed(insertion_id)
-                                }
+                            let contains_end = match &selection.end {
+                                Anchor::Middle { version, .. } => self.version >= *version,
                                 _ => true,
                             };
                             contains_start && contains_end
@@ -1520,557 +1439,208 @@ impl Buffer {
         }
     }
 
-    fn resolve_fragment_id(&self, edit_id: time::Local, offset: usize) -> Result<FragmentId> {
-        let split_tree = self
-            .insertion_splits
-            .get(&edit_id)
-            .ok_or_else(|| anyhow!("invalid operation"))?;
-        let mut cursor = split_tree.cursor::<usize, ()>();
-        cursor.seek(&offset, SeekBias::Left, &());
-        Ok(cursor
-            .item()
-            .ok_or_else(|| anyhow!("invalid operation"))?
-            .fragment_id
-            .clone())
-    }
-
-    fn splice_fragments<I>(&mut self, mut old_ranges: I, new_text: Option<String>) -> Vec<Operation>
-    where
-        I: Iterator<Item = Range<usize>>,
-    {
-        let mut cur_range = old_ranges.next();
-        if cur_range.is_none() {
-            return Vec::new();
-        }
-
-        let mut ops = Vec::with_capacity(old_ranges.size_hint().0);
-
-        let mut old_fragments = SumTree::new();
-        let mut old_visible_text = Rope::new();
-        let mut old_deleted_text = Rope::new();
-        mem::swap(&mut old_visible_text, &mut self.visible_text);
-        mem::swap(&mut old_deleted_text, &mut self.deleted_text);
-        mem::swap(&mut old_fragments, &mut self.fragments);
-
-        let mut fragments_cursor = old_fragments.cursor::<usize, usize>();
-        let mut new_fragments =
-            fragments_cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &());
+    fn apply_local_edit(
+        &mut self,
+        ranges: &[Range<usize>],
+        new_text: Option<String>,
+        timestamp: InsertionTimestamp,
+    ) -> EditOperation {
+        let mut edit = EditOperation {
+            timestamp,
+            version: self.version(),
+            ranges: Vec::with_capacity(ranges.len()),
+            new_text: None,
+        };
 
         let mut new_ropes =
-            RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0));
+            RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
+        let mut old_fragments = self.fragments.cursor::<usize, FragmentTextSummary>();
+        let mut new_fragments = old_fragments.slice(&ranges[0].start, Bias::Right, &None);
         new_ropes.push_tree(new_fragments.summary().text);
 
-        let mut start_id = None;
-        let mut start_offset = None;
-        let mut end_id = None;
-        let mut end_offset = None;
-        let mut version_in_range = time::Global::new();
-
-        let mut local_timestamp = self.local_clock.tick();
-        let mut lamport_timestamp = self.lamport_clock.tick();
-
-        while cur_range.is_some() && fragments_cursor.item().is_some() {
-            let mut fragment = fragments_cursor.item().unwrap().clone();
-            let fragment_summary = fragments_cursor.item_summary().unwrap();
-            let mut fragment_start = *fragments_cursor.start();
-            let mut fragment_end = fragment_start + fragment.visible_len();
-            let fragment_was_visible = fragment.visible;
-
-            let old_split_tree = self
-                .insertion_splits
-                .remove(&fragment.insertion.id)
-                .unwrap();
-            let mut splits_cursor = old_split_tree.cursor::<usize, ()>();
-            let mut new_split_tree =
-                splits_cursor.slice(&fragment.range_in_insertion.start, 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
-            // inserted text.
-            while cur_range.as_ref().map_or(false, |r| r.start < fragment_end) {
-                let range = cur_range.clone().unwrap();
-                if range.start > fragment_start {
-                    let mut prefix = fragment.clone();
-                    prefix.range_in_insertion.end =
-                        prefix.range_in_insertion.start + (range.start - fragment_start);
-                    prefix.id =
-                        FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
-                    fragment.range_in_insertion.start = prefix.range_in_insertion.end;
-
-                    new_ropes.push_fragment(&prefix, prefix.visible);
-                    new_fragments.push(prefix.clone(), &());
-                    new_split_tree.push(
-                        InsertionSplit {
-                            extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start,
-                            fragment_id: prefix.id,
-                        },
-                        &(),
-                    );
-                    fragment_start = range.start;
-                }
-
-                if range.end == fragment_start {
-                    end_id = Some(new_fragments.last().unwrap().insertion.id);
-                    end_offset = Some(new_fragments.last().unwrap().range_in_insertion.end);
-                } else if range.end == fragment_end {
-                    end_id = Some(fragment.insertion.id);
-                    end_offset = Some(fragment.range_in_insertion.end);
-                }
-
-                if range.start == fragment_start {
-                    start_id = Some(new_fragments.last().unwrap().insertion.id);
-                    start_offset = Some(new_fragments.last().unwrap().range_in_insertion.end);
-
-                    if let Some(new_text) = new_text.clone() {
-                        let new_fragment = self.build_fragment_to_insert(
-                            &new_fragments.last().unwrap(),
-                            Some(&fragment),
-                            &new_text,
-                            local_timestamp,
-                            lamport_timestamp,
-                        );
-
-                        new_ropes.push_str(&new_text);
-                        new_fragments.push(new_fragment, &());
-                    }
-                }
-
-                if range.end < fragment_end {
-                    if range.end > fragment_start {
-                        let mut prefix = fragment.clone();
-                        prefix.range_in_insertion.end =
-                            prefix.range_in_insertion.start + (range.end - fragment_start);
-                        prefix.id =
-                            FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
-                        version_in_range.observe_all(&fragment_summary.max_version);
-                        if prefix.visible {
-                            prefix.deletions.insert(local_timestamp);
-                            prefix.visible = false;
-                        }
-                        fragment.range_in_insertion.start = prefix.range_in_insertion.end;
-                        new_ropes.push_fragment(&prefix, fragment_was_visible);
-                        new_fragments.push(prefix.clone(), &());
-                        new_split_tree.push(
-                            InsertionSplit {
-                                extent: prefix.range_in_insertion.end
-                                    - prefix.range_in_insertion.start,
-                                fragment_id: prefix.id,
-                            },
-                            &(),
-                        );
-                        fragment_start = range.end;
-                        end_id = Some(fragment.insertion.id);
-                        end_offset = Some(fragment.range_in_insertion.start);
-                    }
-                } else {
-                    version_in_range.observe_all(&fragment_summary.max_version);
-                    if fragment.visible {
-                        fragment.deletions.insert(local_timestamp);
-                        fragment.visible = false;
+        let mut fragment_start = old_fragments.start().visible;
+        for range in ranges {
+            let fragment_end = old_fragments.end(&None).visible;
+
+            // If the current fragment ends before this range, then jump ahead to the first fragment
+            // that extends past the start of this range, reusing any intervening fragments.
+            if fragment_end < range.start {
+                // If the current fragment has been partially consumed, then consume the rest of it
+                // and advance to the next fragment before slicing.
+                if fragment_start > old_fragments.start().visible {
+                    if fragment_end > fragment_start {
+                        let mut suffix = old_fragments.item().unwrap().clone();
+                        suffix.len = fragment_end - fragment_start;
+                        new_ropes.push_fragment(&suffix, suffix.visible);
+                        new_fragments.push(suffix, &None);
                     }
+                    old_fragments.next(&None);
                 }
 
-                // If the splice ends inside this fragment, we can advance to the next splice and
-                // check if it also intersects the current fragment. Otherwise we break out of the
-                // loop and find the first fragment that the splice does not contain fully.
-                if range.end <= fragment_end {
-                    ops.push(Operation::Edit {
-                        edit: EditOperation {
-                            id: local_timestamp,
-                            start_id: start_id.unwrap(),
-                            start_offset: start_offset.unwrap(),
-                            end_id: end_id.unwrap(),
-                            end_offset: end_offset.unwrap(),
-                            version_in_range,
-                            new_text: new_text.clone(),
-                        },
-                        lamport_timestamp,
-                    });
-
-                    start_id = None;
-                    start_offset = None;
-                    end_id = None;
-                    end_offset = None;
-                    version_in_range = time::Global::new();
-                    cur_range = old_ranges.next();
-                    if cur_range.is_some() {
-                        local_timestamp = self.local_clock.tick();
-                        lamport_timestamp = self.lamport_clock.tick();
-                    }
-                } else {
-                    break;
-                }
+                let slice = old_fragments.slice(&range.start, Bias::Right, &None);
+                new_ropes.push_tree(slice.summary().text);
+                new_fragments.push_tree(slice, &None);
+                fragment_start = old_fragments.start().visible;
             }
-            new_split_tree.push(
-                InsertionSplit {
-                    extent: fragment.range_in_insertion.end - fragment.range_in_insertion.start,
-                    fragment_id: fragment.id.clone(),
-                },
-                &(),
-            );
-            splits_cursor.next();
-            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_ropes.push_fragment(&fragment, fragment_was_visible);
-            new_fragments.push(fragment, &());
-
-            // Scan forward until we find a fragment that is not fully contained by the current splice.
-            fragments_cursor.next();
-            if let Some(range) = cur_range.clone() {
-                while let Some(fragment) = fragments_cursor.item() {
-                    let fragment_summary = fragments_cursor.item_summary().unwrap();
-                    let fragment_was_visible = fragment.visible;
-                    fragment_start = *fragments_cursor.start();
-                    fragment_end = fragment_start + fragment.visible_len();
-                    if range.start < fragment_start && range.end >= fragment_end {
-                        let mut new_fragment = fragment.clone();
-                        version_in_range.observe_all(&fragment_summary.max_version);
-                        if new_fragment.visible {
-                            new_fragment.deletions.insert(local_timestamp);
-                            new_fragment.visible = false;
-                        }
+            let full_range_start = range.start + old_fragments.start().deleted;
 
-                        new_ropes.push_fragment(&new_fragment, fragment_was_visible);
-                        new_fragments.push(new_fragment, &());
-                        fragments_cursor.next();
-
-                        if range.end == fragment_end {
-                            end_id = Some(fragment.insertion.id);
-                            end_offset = Some(fragment.range_in_insertion.end);
-                            ops.push(Operation::Edit {
-                                edit: EditOperation {
-                                    id: local_timestamp,
-                                    start_id: start_id.unwrap(),
-                                    start_offset: start_offset.unwrap(),
-                                    end_id: end_id.unwrap(),
-                                    end_offset: end_offset.unwrap(),
-                                    version_in_range,
-                                    new_text: new_text.clone(),
-                                },
-                                lamport_timestamp,
-                            });
+            // Preserve any portion of the current fragment that precedes this range.
+            if fragment_start < range.start {
+                let mut prefix = old_fragments.item().unwrap().clone();
+                prefix.len = range.start - fragment_start;
+                new_ropes.push_fragment(&prefix, prefix.visible);
+                new_fragments.push(prefix, &None);
+                fragment_start = range.start;
+            }
 
-                            start_id = None;
-                            start_offset = None;
-                            end_id = None;
-                            end_offset = None;
-                            version_in_range = time::Global::new();
+            // Insert the new text before any existing fragments within the range.
+            if let Some(new_text) = new_text.as_deref() {
+                new_ropes.push_str(new_text);
+                new_fragments.push(
+                    Fragment {
+                        timestamp,
+                        len: new_text.len(),
+                        deletions: Default::default(),
+                        max_undos: Default::default(),
+                        visible: true,
+                    },
+                    &None,
+                );
+            }
 
-                            cur_range = old_ranges.next();
-                            if cur_range.is_some() {
-                                local_timestamp = self.local_clock.tick();
-                                lamport_timestamp = self.lamport_clock.tick();
-                            }
-                            break;
-                        }
-                    } else {
-                        break;
-                    }
+            // Advance through every fragment that intersects this range, marking the intersecting
+            // portions as deleted.
+            while fragment_start < range.end {
+                let fragment = old_fragments.item().unwrap();
+                let fragment_end = old_fragments.end(&None).visible;
+                let mut intersection = fragment.clone();
+                let intersection_end = cmp::min(range.end, fragment_end);
+                if fragment.visible {
+                    intersection.len = intersection_end - fragment_start;
+                    intersection.deletions.insert(timestamp.local());
+                    intersection.visible = false;
                 }
-
-                // If the splice we are currently evaluating starts after the end of the fragment
-                // that the cursor is parked at, we should seek to the next splice's start range
-                // and push all the fragments in between into the new tree.
-                if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) {
-                    let slice = fragments_cursor.slice(
-                        &cur_range.as_ref().unwrap().start,
-                        SeekBias::Right,
-                        &(),
-                    );
-                    new_ropes.push_tree(slice.summary().text);
-                    new_fragments.push_tree(slice, &());
+                if intersection.len > 0 {
+                    new_ropes.push_fragment(&intersection, fragment.visible);
+                    new_fragments.push(intersection, &None);
+                    fragment_start = intersection_end;
+                }
+                if fragment_end <= range.end {
+                    old_fragments.next(&None);
                 }
             }
-        }
 
-        // Handle range that is at the end of the buffer if it exists. There should never be
-        // multiple because ranges must be disjoint.
-        if cur_range.is_some() {
-            debug_assert_eq!(old_ranges.next(), None);
-            let last_fragment = new_fragments.last().unwrap();
-            ops.push(Operation::Edit {
-                edit: EditOperation {
-                    id: local_timestamp,
-                    start_id: last_fragment.insertion.id,
-                    start_offset: last_fragment.range_in_insertion.end,
-                    end_id: last_fragment.insertion.id,
-                    end_offset: last_fragment.range_in_insertion.end,
-                    version_in_range: time::Global::new(),
-                    // TODO: avoid cloning the String.
-                    new_text: new_text.clone(),
-                },
-                lamport_timestamp,
-            });
-
-            if let Some(new_text) = new_text {
-                let new_fragment = self.build_fragment_to_insert(
-                    &last_fragment,
-                    None,
-                    &new_text,
-                    local_timestamp,
-                    lamport_timestamp,
-                );
+            let full_range_end = range.end + old_fragments.start().deleted;
+            edit.ranges.push(full_range_start..full_range_end);
+        }
 
-                new_ropes.push_str(&new_text);
-                new_fragments.push(new_fragment, &());
+        // If the current fragment has been partially consumed, then consume the rest of it
+        // and advance to the next fragment before slicing.
+        if fragment_start > old_fragments.start().visible {
+            let fragment_end = old_fragments.end(&None).visible;
+            if fragment_end > fragment_start {
+                let mut suffix = old_fragments.item().unwrap().clone();
+                suffix.len = fragment_end - fragment_start;
+                new_ropes.push_fragment(&suffix, suffix.visible);
+                new_fragments.push(suffix, &None);
             }
+            old_fragments.next(&None);
         }
 
-        new_fragments.push_tree(fragments_cursor.suffix(&()), &());
+        let suffix = old_fragments.suffix(&None);
+        new_ropes.push_tree(suffix.summary().text);
+        new_fragments.push_tree(suffix, &None);
         let (visible_text, deleted_text) = new_ropes.finish();
+        drop(old_fragments);
 
         self.fragments = new_fragments;
         self.visible_text = visible_text;
         self.deleted_text = deleted_text;
-        ops
-    }
-
-    fn split_fragment(
-        &mut self,
-        prev_fragment: &Fragment,
-        fragment: &Fragment,
-        range: Range<usize>,
-    ) -> (Option<Fragment>, Option<Fragment>, Option<Fragment>) {
-        debug_assert!(range.start >= fragment.range_in_insertion.start);
-        debug_assert!(range.start <= fragment.range_in_insertion.end);
-        debug_assert!(range.end <= fragment.range_in_insertion.end);
-        debug_assert!(range.end >= fragment.range_in_insertion.start);
-
-        if range.end == fragment.range_in_insertion.start {
-            (None, None, Some(fragment.clone()))
-        } else if range.start == fragment.range_in_insertion.end {
-            (Some(fragment.clone()), None, None)
-        } else if range.start == fragment.range_in_insertion.start
-            && range.end == fragment.range_in_insertion.end
-        {
-            (None, Some(fragment.clone()), None)
-        } else {
-            let mut prefix = fragment.clone();
-
-            let after_range = if range.end < fragment.range_in_insertion.end {
-                let mut suffix = prefix.clone();
-                suffix.range_in_insertion.start = range.end;
-                prefix.range_in_insertion.end = range.end;
-                prefix.id = FragmentId::between(&prev_fragment.id, &suffix.id);
-                Some(suffix)
-            } else {
-                None
-            };
-
-            let within_range = if range.start != range.end {
-                let mut suffix = prefix.clone();
-                suffix.range_in_insertion.start = range.start;
-                prefix.range_in_insertion.end = range.start;
-                prefix.id = FragmentId::between(&prev_fragment.id, &suffix.id);
-                Some(suffix)
-            } else {
-                None
-            };
-
-            let before_range = if range.start > fragment.range_in_insertion.start {
-                Some(prefix)
-            } else {
-                None
-            };
-
-            let old_split_tree = self
-                .insertion_splits
-                .remove(&fragment.insertion.id)
-                .unwrap();
-            let mut cursor = old_split_tree.cursor::<usize, ()>();
-            let mut new_split_tree =
-                cursor.slice(&fragment.range_in_insertion.start, SeekBias::Right, &());
-
-            if let Some(ref fragment) = before_range {
-                new_split_tree.push(
-                    InsertionSplit {
-                        extent: range.start - fragment.range_in_insertion.start,
-                        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(),
-                    },
-                    &(),
-                );
-            }
-
-            if let Some(ref fragment) = after_range {
-                new_split_tree.push(
-                    InsertionSplit {
-                        extent: fragment.range_in_insertion.end - range.end,
-                        fragment_id: fragment.id.clone(),
-                    },
-                    &(),
-                );
-            }
-
-            cursor.next();
-            new_split_tree.push_tree(
-                cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right, &()),
-                &(),
-            );
-
-            self.insertion_splits
-                .insert(fragment.insertion.id, new_split_tree);
-
-            (before_range, within_range, after_range)
-        }
-    }
-
-    fn build_fragment_to_insert(
-        &mut self,
-        prev_fragment: &Fragment,
-        next_fragment: Option<&Fragment>,
-        text: &str,
-        insertion_id: time::Local,
-        lamport_timestamp: time::Lamport,
-    ) -> Fragment {
-        let new_fragment_id = FragmentId::between(
-            &prev_fragment.id,
-            next_fragment
-                .map(|f| &f.id)
-                .unwrap_or(&FragmentId::max_value()),
-        );
-
-        let range_in_insertion = 0..text.len();
-        let mut split_tree = SumTree::new();
-        split_tree.push(
-            InsertionSplit {
-                extent: range_in_insertion.len(),
-                fragment_id: new_fragment_id.clone(),
-            },
-            &(),
-        );
-        self.insertion_splits.insert(insertion_id, split_tree);
-
-        Fragment::new(
-            new_fragment_id,
-            Arc::new(Insertion {
-                id: insertion_id,
-                parent_id: prev_fragment.insertion.id,
-                offset_in_parent: prev_fragment.range_in_insertion.end,
-                lamport_timestamp,
-            }),
-            range_in_insertion,
-        )
+        edit.new_text = new_text;
+        edit
     }
 
     pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
-        self.anchor_at(position, AnchorBias::Left)
+        self.anchor_at(position, Bias::Left)
     }
 
     pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
-        self.anchor_at(position, AnchorBias::Right)
+        self.anchor_at(position, Bias::Right)
     }
 
-    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: AnchorBias) -> Anchor {
+    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
         let offset = position.to_offset(self);
         let max_offset = self.len();
         assert!(offset <= max_offset, "offset is out of range");
 
-        let seek_bias;
-        match bias {
-            AnchorBias::Left => {
-                if offset == 0 {
-                    return Anchor::Start;
-                } else {
-                    seek_bias = SeekBias::Left;
-                }
-            }
-            AnchorBias::Right => {
-                if offset == max_offset {
-                    return Anchor::End;
-                } else {
-                    seek_bias = SeekBias::Right;
-                }
+        if offset == 0 && bias == Bias::Left {
+            Anchor::Start
+        } else if offset == max_offset && bias == Bias::Right {
+            Anchor::End
+        } else {
+            let mut cursor = self.fragments.cursor::<usize, FragmentTextSummary>();
+            cursor.seek(&offset, bias, &None);
+            Anchor::Middle {
+                offset: offset + cursor.start().deleted,
+                bias,
+                version: self.version(),
             }
-        };
-
-        let mut cursor = self.fragments.cursor::<usize, usize>();
-        cursor.seek(&offset, seek_bias, &());
-        let fragment = cursor.item().unwrap();
-        let offset_in_fragment = offset - cursor.start();
-        let offset_in_insertion = fragment.range_in_insertion.start + offset_in_fragment;
-        let anchor = Anchor::Middle {
-            insertion_id: fragment.insertion.id,
-            offset: offset_in_insertion,
-            bias,
-        };
-        anchor
+        }
     }
 
-    fn fragment_id_for_anchor(&self, anchor: &Anchor) -> Result<&FragmentId> {
+    fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary {
         match anchor {
-            Anchor::Start => Ok(FragmentId::max_value()),
-            Anchor::End => Ok(FragmentId::min_value()),
+            Anchor::Start => TextSummary::default(),
+            Anchor::End => self.text_summary(),
             Anchor::Middle {
-                insertion_id,
                 offset,
                 bias,
-                ..
+                version,
             } => {
-                let seek_bias = match bias {
-                    AnchorBias::Left => SeekBias::Left,
-                    AnchorBias::Right => SeekBias::Right,
+                let mut cursor = self
+                    .fragments
+                    .cursor::<VersionedOffset, (VersionedOffset, usize)>();
+                cursor.seek(
+                    &VersionedOffset::Offset(*offset),
+                    *bias,
+                    &Some(version.clone()),
+                );
+                let fragment = cursor.item().unwrap();
+                let overshoot = if fragment.visible {
+                    offset - cursor.start().0.offset()
+                } else {
+                    0
                 };
 
-                let splits = self
-                    .insertion_splits
-                    .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
-                    .item()
-                    .ok_or_else(|| anyhow!("split offset is out of range"))
-                    .map(|split| &split.fragment_id)
+                self.text_summary_for_range(0..cursor.start().1 + overshoot)
             }
         }
     }
 
-    fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary {
+    fn full_offset_for_anchor(&self, anchor: &Anchor) -> usize {
         match anchor {
-            Anchor::Start => TextSummary::default(),
-            Anchor::End => self.text_summary(),
+            Anchor::Start => 0,
+            Anchor::End => {
+                let summary = self.fragments.summary();
+                summary.text.visible + summary.text.deleted
+            }
             Anchor::Middle {
-                insertion_id,
                 offset,
                 bias,
+                version,
             } => {
-                let seek_bias = match bias {
-                    AnchorBias::Left => SeekBias::Left,
-                    AnchorBias::Right => SeekBias::Right,
-                };
-
-                let splits = self
-                    .insertion_splits
-                    .get(&insertion_id)
-                    .expect("split does not exist for insertion id");
-                let mut splits_cursor = splits.cursor::<usize, ()>();
-                splits_cursor.seek(offset, seek_bias, &());
-                let split = splits_cursor.item().expect("split offset is out of range");
-
-                let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, usize>();
-                fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &());
-                let fragment = fragments_cursor.item().expect("fragment id does not exist");
-
-                let mut ix = *fragments_cursor.start();
-                if fragment.visible {
-                    ix += offset - fragment.range_in_insertion.start;
-                }
-                self.text_summary_for_range(0..ix)
+                let mut cursor = self
+                    .fragments
+                    .cursor::<VersionedOffset, (VersionedOffset, FragmentTextSummary)>();
+                cursor.seek(
+                    &VersionedOffset::Offset(*offset),
+                    *bias,
+                    &Some(version.clone()),
+                );
+                let overshoot = offset - cursor.start().0.offset();
+                let summary = cursor.start().1;
+                summary.visible + summary.deleted + overshoot
             }
         }
     }

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

@@ -1,47 +1,19 @@
 use super::Buffer;
-use crate::time;
+use crate::{time, util::Bias};
 use anyhow::Result;
-use std::cmp::Ordering;
-use std::ops::Range;
+use std::{cmp::Ordering, ops::Range};
 
 #[derive(Clone, Eq, PartialEq, Debug, Hash)]
 pub enum Anchor {
     Start,
     End,
     Middle {
-        insertion_id: time::Local,
         offset: usize,
-        bias: AnchorBias,
+        bias: Bias,
+        version: time::Global,
     },
 }
 
-#[derive(Clone, Eq, PartialEq, Debug, Hash)]
-pub enum AnchorBias {
-    Left,
-    Right,
-}
-
-impl PartialOrd for AnchorBias {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for AnchorBias {
-    fn cmp(&self, other: &Self) -> Ordering {
-        use AnchorBias::*;
-
-        if self == other {
-            return Ordering::Equal;
-        }
-
-        match (self, other) {
-            (Left, _) => Ordering::Less,
-            (Right, _) => Ordering::Greater,
-        }
-    }
-}
-
 impl Anchor {
     pub fn cmp(&self, other: &Anchor, buffer: &Buffer) -> Result<Ordering> {
         if self == other {
@@ -55,18 +27,24 @@ impl Anchor {
                 Anchor::Middle {
                     offset: self_offset,
                     bias: self_bias,
-                    ..
+                    version: self_version,
                 },
                 Anchor::Middle {
                     offset: other_offset,
                     bias: other_bias,
-                    ..
+                    version: other_version,
                 },
-            ) => buffer
-                .fragment_id_for_anchor(self)?
-                .cmp(buffer.fragment_id_for_anchor(other)?)
-                .then_with(|| self_offset.cmp(other_offset))
-                .then_with(|| self_bias.cmp(other_bias)),
+            ) => {
+                let offset_comparison = if self_version == other_version {
+                    self_offset.cmp(other_offset)
+                } else {
+                    buffer
+                        .full_offset_for_anchor(self)
+                        .cmp(&buffer.full_offset_for_anchor(other))
+                };
+
+                offset_comparison.then_with(|| self_bias.cmp(&other_bias))
+            }
         })
     }
 
@@ -74,8 +52,7 @@ impl Anchor {
         match self {
             Anchor::Start
             | Anchor::Middle {
-                bias: AnchorBias::Left,
-                ..
+                bias: Bias::Left, ..
             } => self.clone(),
             _ => buffer.anchor_before(self),
         }
@@ -85,8 +62,7 @@ impl Anchor {
         match self {
             Anchor::End
             | Anchor::Middle {
-                bias: AnchorBias::Right,
-                ..
+                bias: Bias::Right, ..
             } => self.clone(),
             _ => buffer.anchor_after(self),
         }

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

@@ -1,7 +1,7 @@
 use super::Point;
 use crate::{
-    editor::Bias,
-    sum_tree::{self, SeekBias, SumTree},
+    sum_tree::{self, SumTree},
+    util::Bias,
 };
 use arrayvec::ArrayString;
 use smallvec::SmallVec;
@@ -25,13 +25,13 @@ impl Rope {
 
     pub fn append(&mut self, rope: Rope) {
         let mut chunks = rope.chunks.cursor::<(), ()>();
-        chunks.next();
+        chunks.next(&());
         if let Some(chunk) = chunks.item() {
             if self.chunks.last().map_or(false, |c| c.0.len() < CHUNK_BASE)
                 || chunk.0.len() < CHUNK_BASE
             {
                 self.push(&chunk.0);
-                chunks.next();
+                chunks.next(&());
             }
         }
 
@@ -99,11 +99,11 @@ impl Rope {
     }
 
     pub fn len(&self) -> usize {
-        self.chunks.extent()
+        self.chunks.extent(&())
     }
 
     pub fn max_point(&self) -> Point {
-        self.chunks.extent()
+        self.chunks.extent(&())
     }
 
     pub fn cursor(&self, offset: usize) -> Cursor {
@@ -129,7 +129,7 @@ impl Rope {
     pub fn to_point(&self, offset: usize) -> Point {
         assert!(offset <= self.summary().bytes);
         let mut cursor = self.chunks.cursor::<usize, TextSummary>();
-        cursor.seek(&offset, SeekBias::Left, &());
+        cursor.seek(&offset, Bias::Left, &());
         let overshoot = offset - cursor.start().bytes;
         cursor.start().lines
             + cursor
@@ -140,14 +140,14 @@ impl Rope {
     pub fn to_offset(&self, point: Point) -> usize {
         assert!(point <= self.summary().lines);
         let mut cursor = self.chunks.cursor::<Point, TextSummary>();
-        cursor.seek(&point, SeekBias::Left, &());
+        cursor.seek(&point, Bias::Left, &());
         let overshoot = point - cursor.start().lines;
         cursor.start().bytes + cursor.item().map_or(0, |chunk| chunk.to_offset(overshoot))
     }
 
     pub fn clip_offset(&self, mut offset: usize, bias: Bias) -> usize {
         let mut cursor = self.chunks.cursor::<usize, usize>();
-        cursor.seek(&offset, SeekBias::Left, &());
+        cursor.seek(&offset, Bias::Left, &());
         if let Some(chunk) = cursor.item() {
             let mut ix = offset - cursor.start();
             while !chunk.0.is_char_boundary(ix) {
@@ -170,7 +170,7 @@ impl Rope {
 
     pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
         let mut cursor = self.chunks.cursor::<Point, Point>();
-        cursor.seek(&point, SeekBias::Right, &());
+        cursor.seek(&point, Bias::Right, &());
         if let Some(chunk) = cursor.item() {
             let overshoot = point - cursor.start();
             *cursor.start() + chunk.clip_point(overshoot, bias)
@@ -197,7 +197,7 @@ pub struct Cursor<'a> {
 impl<'a> Cursor<'a> {
     pub fn new(rope: &'a Rope, offset: usize) -> Self {
         let mut chunks = rope.chunks.cursor();
-        chunks.seek(&offset, SeekBias::Right, &());
+        chunks.seek(&offset, Bias::Right, &());
         Self {
             rope,
             chunks,
@@ -208,24 +208,29 @@ impl<'a> Cursor<'a> {
     pub fn seek_forward(&mut self, end_offset: usize) {
         debug_assert!(end_offset >= self.offset);
 
-        self.chunks.seek_forward(&end_offset, SeekBias::Right, &());
+        self.chunks.seek_forward(&end_offset, Bias::Right, &());
         self.offset = end_offset;
     }
 
     pub fn slice(&mut self, end_offset: usize) -> Rope {
-        debug_assert!(end_offset >= self.offset);
+        debug_assert!(
+            end_offset >= self.offset,
+            "cannot slice backwards from {} to {}",
+            self.offset,
+            end_offset
+        );
 
         let mut slice = Rope::new();
         if let Some(start_chunk) = self.chunks.item() {
             let start_ix = self.offset - self.chunks.start();
-            let end_ix = cmp::min(end_offset, self.chunks.end()) - self.chunks.start();
+            let end_ix = cmp::min(end_offset, self.chunks.end(&())) - self.chunks.start();
             slice.push(&start_chunk.0[start_ix..end_ix]);
         }
 
-        if end_offset > self.chunks.end() {
-            self.chunks.next();
+        if end_offset > self.chunks.end(&()) {
+            self.chunks.next(&());
             slice.append(Rope {
-                chunks: self.chunks.slice(&end_offset, SeekBias::Right, &()),
+                chunks: self.chunks.slice(&end_offset, Bias::Right, &()),
             });
             if let Some(end_chunk) = self.chunks.item() {
                 let end_ix = end_offset - self.chunks.start();
@@ -243,13 +248,13 @@ impl<'a> Cursor<'a> {
         let mut summary = TextSummary::default();
         if let Some(start_chunk) = self.chunks.item() {
             let start_ix = self.offset - self.chunks.start();
-            let end_ix = cmp::min(end_offset, self.chunks.end()) - self.chunks.start();
+            let end_ix = cmp::min(end_offset, self.chunks.end(&())) - self.chunks.start();
             summary = TextSummary::from(&start_chunk.0[start_ix..end_ix]);
         }
 
-        if end_offset > self.chunks.end() {
-            self.chunks.next();
-            summary += &self.chunks.summary(&end_offset, SeekBias::Right, &());
+        if end_offset > self.chunks.end(&()) {
+            self.chunks.next(&());
+            summary += &self.chunks.summary(&end_offset, Bias::Right, &());
             if let Some(end_chunk) = self.chunks.item() {
                 let end_ix = end_offset - self.chunks.start();
                 summary += TextSummary::from(&end_chunk.0[..end_ix]);
@@ -260,7 +265,7 @@ impl<'a> Cursor<'a> {
     }
 
     pub fn suffix(mut self) -> Rope {
-        self.slice(self.rope.chunks.extent())
+        self.slice(self.rope.chunks.extent(&()))
     }
 
     pub fn offset(&self) -> usize {
@@ -276,7 +281,7 @@ pub struct Chunks<'a> {
 impl<'a> Chunks<'a> {
     pub fn new(rope: &'a Rope, range: Range<usize>) -> Self {
         let mut chunks = rope.chunks.cursor();
-        chunks.seek(&range.start, SeekBias::Right, &());
+        chunks.seek(&range.start, Bias::Right, &());
         Self { chunks, range }
     }
 
@@ -285,10 +290,10 @@ impl<'a> Chunks<'a> {
     }
 
     pub fn seek(&mut self, offset: usize) {
-        if offset >= self.chunks.end() {
-            self.chunks.seek_forward(&offset, SeekBias::Right, &());
+        if offset >= self.chunks.end(&()) {
+            self.chunks.seek_forward(&offset, Bias::Right, &());
         } else {
-            self.chunks.seek(&offset, SeekBias::Right, &());
+            self.chunks.seek(&offset, Bias::Right, &());
         }
         self.range.start = offset;
     }
@@ -312,7 +317,7 @@ impl<'a> Iterator for Chunks<'a> {
     fn next(&mut self) -> Option<Self::Item> {
         let result = self.peek();
         if result.is_some() {
-            self.chunks.next();
+            self.chunks.next(&());
         }
         result
     }
@@ -478,19 +483,19 @@ impl std::ops::AddAssign<Self> for TextSummary {
 }
 
 impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary {
-    fn add_summary(&mut self, summary: &'a TextSummary) {
+    fn add_summary(&mut self, summary: &'a TextSummary, _: &()) {
         *self += summary;
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, TextSummary> for usize {
-    fn add_summary(&mut self, summary: &'a TextSummary) {
+    fn add_summary(&mut self, summary: &'a TextSummary, _: &()) {
         *self += summary.bytes;
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, TextSummary> for Point {
-    fn add_summary(&mut self, summary: &'a TextSummary) {
+    fn add_summary(&mut self, summary: &'a TextSummary, _: &()) {
         *self += &summary.lines;
     }
 }

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

@@ -1,17 +1,19 @@
 use super::{
     buffer::{AnchorRangeExt, TextSummary},
-    Anchor, Bias, Buffer, DisplayPoint, Edit, Point, ToOffset,
+    Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset,
 };
 use crate::{
     editor::buffer,
     settings::StyleId,
-    sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
+    sum_tree::{self, Cursor, FilterCursor, SumTree},
     time,
+    util::Bias,
 };
 use gpui::{AppContext, ModelHandle};
 use parking_lot::{Mutex, MutexGuard};
 use std::{
     cmp::{self, Ordering},
+    iter,
     ops::Range,
 };
 
@@ -80,7 +82,13 @@ impl FoldMap {
     where
         T: ToOffset,
     {
-        self.intersecting_folds(range, cx).map(|f| &f.0)
+        let buffer = self.buffer.read(cx);
+        let mut folds = self.intersecting_folds(range, cx);
+        iter::from_fn(move || {
+            let item = folds.item().map(|f| &f.0);
+            folds.next(buffer);
+            item
+        })
     }
 
     pub fn fold<T: ToOffset>(
@@ -118,7 +126,7 @@ impl FoldMap {
             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_tree(cursor.slice(&fold, Bias::Right, buffer), buffer);
                 new_tree.push(fold, buffer);
             }
             new_tree.push_tree(cursor.suffix(buffer), buffer);
@@ -149,7 +157,7 @@ impl FoldMap {
                     ..Default::default()
                 });
                 fold_ixs_to_delete.push(*folds_cursor.start());
-                folds_cursor.next();
+                folds_cursor.next(buffer);
             }
         }
 
@@ -166,8 +174,8 @@ impl FoldMap {
             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.slice(&fold_ix, Bias::Right, buffer), buffer);
+                cursor.next(buffer);
             }
             folds.push_tree(cursor.suffix(buffer), buffer);
             folds
@@ -186,10 +194,13 @@ impl FoldMap {
         let buffer = self.buffer.read(cx);
         let start = buffer.anchor_before(range.start.to_offset(buffer));
         let end = buffer.anchor_after(range.end.to_offset(buffer));
-        self.folds.filter::<_, usize>(move |summary| {
-            start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
-                && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
-        })
+        self.folds.filter::<_, usize>(
+            move |summary| {
+                start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
+                    && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
+            },
+            buffer,
+        )
     }
 
     pub fn intersects_fold<T>(&self, offset: T, cx: &AppContext) -> bool
@@ -200,20 +211,20 @@ impl FoldMap {
         let offset = offset.to_offset(buffer);
         let transforms = self.sync(cx);
         let mut cursor = transforms.cursor::<usize, usize>();
-        cursor.seek(&offset, SeekBias::Right, &());
+        cursor.seek(&offset, Bias::Right, &());
         cursor.item().map_or(false, |t| t.display_text.is_some())
     }
 
     pub fn is_line_folded(&self, display_row: u32, cx: &AppContext) -> bool {
         let transforms = self.sync(cx);
         let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
-        cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
+        cursor.seek(&DisplayPoint::new(display_row, 0), Bias::Right, &());
         while let Some(transform) = cursor.item() {
             if transform.display_text.is_some() {
                 return true;
             }
-            if cursor.end().row() == display_row {
-                cursor.next()
+            if cursor.end(&()).row() == display_row {
+                cursor.next(&())
             } else {
                 break;
             }
@@ -232,7 +243,7 @@ impl FoldMap {
     pub fn to_buffer_point(&self, display_point: DisplayPoint, cx: &AppContext) -> Point {
         let transforms = self.sync(cx);
         let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&display_point, SeekBias::Right, &());
+        cursor.seek(&display_point, Bias::Right, &());
         let overshoot = display_point.0 - cursor.start().display.lines;
         cursor.start().buffer.lines + overshoot
     }
@@ -240,11 +251,11 @@ impl FoldMap {
     pub fn to_display_point(&self, point: Point, cx: &AppContext) -> DisplayPoint {
         let transforms = self.sync(cx);
         let mut cursor = transforms.cursor::<Point, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right, &());
+        cursor.seek(&point, Bias::Right, &());
         let overshoot = point - cursor.start().buffer.lines;
         DisplayPoint(cmp::min(
             cursor.start().display.lines + overshoot,
-            cursor.end().display.lines,
+            cursor.end(&()).display.lines,
         ))
     }
 
@@ -265,18 +276,15 @@ 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, Bias::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, Bias::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.next();
+            cursor.seek(&edit.old_range.end, Bias::Right, &());
+            cursor.next(&());
 
             let mut delta = edit.delta();
             loop {
@@ -292,8 +300,8 @@ 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.next();
+                        cursor.seek(&edit.old_range.end, Bias::Right, &());
+                        cursor.next(&());
                     }
                 } else {
                     break;
@@ -305,10 +313,15 @@ impl FoldMap {
 
             let anchor = buffer.anchor_before(edit.new_range.start);
             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)..f.0.end.to_offset(buffer))
-                .peekable();
+            folds_cursor.seek(&Fold(anchor..Anchor::End), Bias::Left, buffer);
+            let mut folds = iter::from_fn(move || {
+                let item = folds_cursor
+                    .item()
+                    .map(|f| f.0.start.to_offset(buffer)..f.0.end.to_offset(buffer));
+                folds_cursor.next(buffer);
+                item
+            })
+            .peekable();
 
             while folds
                 .peek()
@@ -417,7 +430,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), Bias::Left, &());
 
         BufferRows {
             display_point,
@@ -431,7 +444,7 @@ impl FoldMapSnapshot {
 
     pub fn chunks_at(&self, offset: DisplayOffset) -> Chunks {
         let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
-        transform_cursor.seek(&offset, SeekBias::Right, &());
+        transform_cursor.seek(&offset, Bias::Right, &());
         let overshoot = offset.0 - transform_cursor.start().display.bytes;
         let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
         Chunks {
@@ -444,11 +457,11 @@ impl FoldMapSnapshot {
     pub fn highlighted_chunks(&mut self, range: Range<DisplayOffset>) -> HighlightedChunks {
         let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
 
-        transform_cursor.seek(&range.end, SeekBias::Right, &());
+        transform_cursor.seek(&range.end, Bias::Right, &());
         let overshoot = range.end.0 - transform_cursor.start().display.bytes;
         let buffer_end = transform_cursor.start().buffer.bytes + overshoot;
 
-        transform_cursor.seek(&range.start, SeekBias::Right, &());
+        transform_cursor.seek(&range.start, Bias::Right, &());
         let overshoot = range.start.0 - transform_cursor.start().display.bytes;
         let buffer_start = transform_cursor.start().buffer.bytes + overshoot;
 
@@ -469,7 +482,7 @@ impl FoldMapSnapshot {
 
     pub fn to_display_offset(&self, point: DisplayPoint) -> DisplayOffset {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right, &());
+        cursor.seek(&point, Bias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
         let mut offset = cursor.start().display.bytes;
         if !overshoot.is_zero() {
@@ -485,7 +498,7 @@ impl FoldMapSnapshot {
 
     pub fn to_buffer_offset(&self, point: DisplayPoint) -> usize {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right, &());
+        cursor.seek(&point, Bias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
         self.buffer
             .to_offset(cursor.start().buffer.lines + overshoot)
@@ -494,14 +507,14 @@ impl FoldMapSnapshot {
     #[cfg(test)]
     pub fn clip_offset(&self, offset: DisplayOffset, bias: Bias) -> DisplayOffset {
         let mut cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
-        cursor.seek(&offset, SeekBias::Right, &());
+        cursor.seek(&offset, Bias::Right, &());
         if let Some(transform) = cursor.item() {
             let transform_start = cursor.start().display.bytes;
             if transform.display_text.is_some() {
                 if offset.0 == transform_start || matches!(bias, Bias::Left) {
                     DisplayOffset(transform_start)
                 } else {
-                    DisplayOffset(cursor.end().display.bytes)
+                    DisplayOffset(cursor.end(&()).display.bytes)
                 }
             } else {
                 let overshoot = offset.0 - transform_start;
@@ -519,14 +532,14 @@ impl FoldMapSnapshot {
 
     pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right, &());
+        cursor.seek(&point, Bias::Right, &());
         if let Some(transform) = cursor.item() {
             let transform_start = cursor.start().display.lines;
             if transform.display_text.is_some() {
                 if point.0 == transform_start || matches!(bias, Bias::Left) {
                     DisplayPoint(transform_start)
                 } else {
-                    DisplayPoint(cursor.end().display.lines)
+                    DisplayPoint(cursor.end(&()).display.lines)
                 }
             } else {
                 let overshoot = point.0 - transform_start;
@@ -574,7 +587,7 @@ impl sum_tree::Summary for TransformSummary {
 }
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
-    fn add_summary(&mut self, summary: &'a TransformSummary) {
+    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
         sum_tree::Summary::add_summary(self, summary, &());
     }
 }
@@ -649,7 +662,7 @@ impl sum_tree::Summary for FoldSummary {
 }
 
 impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
-    fn add_summary(&mut self, summary: &'a FoldSummary) {
+    fn add_summary(&mut self, summary: &'a FoldSummary, _: &Buffer) {
         self.0.start = summary.start.clone();
         self.0.end = summary.end.clone();
     }
@@ -662,7 +675,7 @@ impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
 }
 
 impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
-    fn add_summary(&mut self, summary: &'a FoldSummary) {
+    fn add_summary(&mut self, summary: &'a FoldSummary, _: &Buffer) {
         *self += summary.count;
     }
 }
@@ -676,8 +689,8 @@ impl<'a> Iterator for BufferRows<'a> {
     type Item = u32;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while self.display_point > self.cursor.end().display.lines {
-            self.cursor.next();
+        while self.display_point > self.cursor.end(&()).display.lines {
+            self.cursor.next(&());
             if self.cursor.item().is_none() {
                 // TODO: Return a bool from next?
                 break;
@@ -717,10 +730,10 @@ impl<'a> Iterator for Chunks<'a> {
             self.buffer_offset += transform.summary.buffer.bytes;
             self.buffer_chunks.seek(self.buffer_offset);
 
-            while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
+            while self.buffer_offset >= self.transform_cursor.end(&()).buffer.bytes
                 && self.transform_cursor.item().is_some()
             {
-                self.transform_cursor.next();
+                self.transform_cursor.next(&());
             }
 
             return Some(display_text);
@@ -732,10 +745,10 @@ impl<'a> Iterator for Chunks<'a> {
             chunk = &chunk[offset_in_chunk..];
 
             // Truncate the chunk so that it ends at the next fold.
-            let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
+            let region_end = self.transform_cursor.end(&()).buffer.bytes - self.buffer_offset;
             if chunk.len() >= region_end {
                 chunk = &chunk[0..region_end];
-                self.transform_cursor.next();
+                self.transform_cursor.next(&());
             } else {
                 self.buffer_chunks.next();
             }
@@ -772,10 +785,10 @@ impl<'a> Iterator for HighlightedChunks<'a> {
             self.buffer_offset += transform.summary.buffer.bytes;
             self.buffer_chunks.seek(self.buffer_offset);
 
-            while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
+            while self.buffer_offset >= self.transform_cursor.end(&()).buffer.bytes
                 && self.transform_cursor.item().is_some()
             {
-                self.transform_cursor.next();
+                self.transform_cursor.next(&());
             }
 
             return Some((display_text, StyleId::default()));
@@ -796,10 +809,10 @@ impl<'a> Iterator for HighlightedChunks<'a> {
             chunk = &chunk[offset_in_chunk..];
 
             // Truncate the chunk so that it ends at the next fold.
-            let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
+            let region_end = self.transform_cursor.end(&()).buffer.bytes - self.buffer_offset;
             if chunk.len() >= region_end {
                 chunk = &chunk[0..region_end];
-                self.transform_cursor.next();
+                self.transform_cursor.next(&());
             } else {
                 self.buffer_chunk.take();
             }
@@ -813,7 +826,7 @@ impl<'a> Iterator for HighlightedChunks<'a> {
 }
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary) {
+    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
         self.0 += &summary.display.lines;
     }
 }
@@ -822,19 +835,19 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
 pub struct DisplayOffset(usize);
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
-    fn add_summary(&mut self, summary: &'a TransformSummary) {
+    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
         self.0 += &summary.display.bytes;
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
-    fn add_summary(&mut self, summary: &'a TransformSummary) {
+    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
         *self += &summary.buffer.lines;
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
-    fn add_summary(&mut self, summary: &'a TransformSummary) {
+    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
         *self += &summary.buffer.bytes;
     }
 }
@@ -1031,7 +1044,7 @@ mod tests {
                     0..=34 => {
                         let buffer = buffer.read(cx);
                         let mut to_fold = Vec::new();
-                        for _ in 0..rng.gen_range(1..=5) {
+                        for _ in 0..rng.gen_range(1..=2) {
                             let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
                             let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
                             to_fold.push(start..end);
@@ -1172,7 +1185,7 @@ mod tests {
                     let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
                     let expected_folds = map
                         .folds
-                        .items()
+                        .items(buffer)
                         .into_iter()
                         .filter(|fold| {
                             let start = buffer.anchor_before(start);
@@ -1227,7 +1240,7 @@ mod tests {
 
         fn merged_fold_ranges(&self, cx: &AppContext) -> Vec<Range<usize>> {
             let buffer = self.buffer.read(cx);
-            let mut folds = self.folds.items();
+            let mut folds = self.folds.items(buffer);
             // Ensure sorting doesn't change how folds get merged and displayed.
             folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
             let mut fold_ranges = folds

zed/src/operation_queue.rs 🔗

@@ -89,7 +89,7 @@ impl<'a> Add<&'a Self> for OperationSummary {
 }
 
 impl<'a> Dimension<'a, OperationSummary> for OperationKey {
-    fn add_summary(&mut self, summary: &OperationSummary) {
+    fn add_summary(&mut self, summary: &OperationSummary, _: &()) {
         assert!(*self <= summary.key);
         *self = summary.key;
     }

zed/src/sum_tree.rs 🔗

@@ -1,5 +1,6 @@
 mod cursor;
 
+use crate::util::Bias;
 use arrayvec::ArrayVec;
 pub use cursor::Cursor;
 pub use cursor::FilterCursor;
@@ -29,11 +30,23 @@ pub trait Summary: Default + Clone + fmt::Debug {
 }
 
 pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default {
-    fn add_summary(&mut self, _summary: &'a S);
+    fn add_summary(&mut self, _summary: &'a S, _: &S::Context);
 }
 
 impl<'a, T: Summary> Dimension<'a, T> for () {
-    fn add_summary(&mut self, _: &'a T) {}
+    fn add_summary(&mut self, _: &'a T, _: &T::Context) {}
+}
+
+impl<'a, S, D1, D2> Dimension<'a, S> for (D1, D2)
+where
+    S: Summary,
+    D1: Dimension<'a, S>,
+    D2: Dimension<'a, S>,
+{
+    fn add_summary(&mut self, summary: &'a S, cx: &S::Context) {
+        self.0.add_summary(summary, cx);
+        self.1.add_summary(summary, cx);
+    }
 }
 
 pub trait SeekDimension<'a, T: Summary>: Dimension<'a, T> {
@@ -46,12 +59,6 @@ impl<'a, S: Summary, T: Dimension<'a, S> + Ord> SeekDimension<'a, S> for T {
     }
 }
 
-#[derive(Copy, Clone, Eq, PartialEq)]
-pub enum SeekBias {
-    Left,
-    Right,
-}
-
 #[derive(Debug, Clone)]
 pub struct SumTree<T: Item>(Arc<Node<T>>);
 
@@ -71,9 +78,15 @@ impl<T: Item> SumTree<T> {
     }
 
     #[allow(unused)]
-    pub fn items(&self) -> Vec<T> {
+    pub fn items(&self, cx: &<T::Summary as Summary>::Context) -> Vec<T> {
+        let mut items = Vec::new();
         let mut cursor = self.cursor::<(), ()>();
-        cursor.cloned().collect()
+        cursor.next(cx);
+        while let Some(item) = cursor.item() {
+            items.push(item.clone());
+            cursor.next(cx);
+        }
+        items
     }
 
     pub fn cursor<'a, S, U>(&'a self) -> Cursor<T, S, U>
@@ -84,12 +97,16 @@ impl<T: Item> SumTree<T> {
         Cursor::new(self)
     }
 
-    pub fn filter<'a, F, U>(&'a self, filter_node: F) -> FilterCursor<F, T, U>
+    pub fn filter<'a, F, U>(
+        &'a self,
+        filter_node: F,
+        cx: &<T::Summary as Summary>::Context,
+    ) -> FilterCursor<F, T, U>
     where
         F: Fn(&T::Summary) -> bool,
         U: Dimension<'a, T::Summary>,
     {
-        FilterCursor::new(self, filter_node)
+        FilterCursor::new(self, filter_node, cx)
     }
 
     #[allow(dead_code)]
@@ -141,11 +158,14 @@ impl<T: Item> SumTree<T> {
         }
     }
 
-    pub fn extent<'a, D: Dimension<'a, T::Summary>>(&'a self) -> D {
+    pub fn extent<'a, D: Dimension<'a, T::Summary>>(
+        &'a self,
+        cx: &<T::Summary as Summary>::Context,
+    ) -> D {
         let mut extent = D::default();
         match self.0.as_ref() {
             Node::Internal { summary, .. } | Node::Leaf { summary, .. } => {
-                extent.add_summary(summary);
+                extent.add_summary(summary, cx);
             }
         }
         extent
@@ -392,7 +412,7 @@ impl<T: KeyedItem> SumTree<T> {
     pub fn insert(&mut self, item: T, cx: &<T::Summary as Summary>::Context) {
         *self = {
             let mut cursor = self.cursor::<T::Key, ()>();
-            let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, cx);
+            let mut new_tree = cursor.slice(&item.key(), Bias::Left, cx);
             new_tree.push(item, cx);
             new_tree.push_tree(cursor.suffix(cx), cx);
             new_tree
@@ -416,7 +436,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, cx);
+            cursor.seek(&T::Key::default(), Bias::Left, cx);
             for edit in edits {
                 let new_key = edit.key();
                 let mut old_item = cursor.item();
@@ -426,7 +446,7 @@ impl<T: KeyedItem> SumTree<T> {
                     .map_or(false, |old_item| old_item.key() < new_key)
                 {
                     new_tree.extend(buffered_items.drain(..), cx);
-                    let slice = cursor.slice(&new_key, SeekBias::Left, cx);
+                    let slice = cursor.slice(&new_key, Bias::Left, cx);
                     new_tree.push_tree(slice, cx);
                     old_item = cursor.item();
                 }
@@ -434,7 +454,7 @@ impl<T: KeyedItem> SumTree<T> {
                 if let Some(old_item) = old_item {
                     if old_item.key() == new_key {
                         removed.push(old_item.clone());
-                        cursor.next();
+                        cursor.next(cx);
                     }
                 }
 
@@ -456,7 +476,7 @@ impl<T: KeyedItem> SumTree<T> {
 
     pub fn get(&self, key: &T::Key, cx: &<T::Summary as Summary>::Context) -> Option<&T> {
         let mut cursor = self.cursor::<T::Key, ()>();
-        if cursor.seek(key, SeekBias::Left, cx) {
+        if cursor.seek(key, Bias::Left, cx) {
             cursor.item()
         } else {
             None
@@ -580,7 +600,10 @@ mod tests {
         tree2.extend(50..100, &());
 
         tree1.push_tree(tree2, &());
-        assert_eq!(tree1.items(), (0..20).chain(50..100).collect::<Vec<u8>>());
+        assert_eq!(
+            tree1.items(&()),
+            (0..20).chain(50..100).collect::<Vec<u8>>()
+        );
     }
 
     #[test]
@@ -596,32 +619,33 @@ mod tests {
             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);
+                let splice_end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
                 let splice_start = rng.gen_range(0..splice_end + 1);
                 let count = rng.gen_range(0..3);
-                let tree_end = tree.extent::<Count>();
+                let tree_end = tree.extent::<Count>(&());
                 let new_items = rng
                     .sample_iter(distributions::Standard)
                     .take(count)
                     .collect::<Vec<u8>>();
 
-                let mut reference_items = tree.items();
+                let mut reference_items = tree.items(&());
                 reference_items.splice(splice_start..splice_end, new_items.clone());
 
                 tree = {
                     let mut cursor = tree.cursor::<Count, ()>();
-                    let mut new_tree = cursor.slice(&Count(splice_start), SeekBias::Right, &());
+                    let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right, &());
                     new_tree.extend(new_items, &());
-                    cursor.seek(&Count(splice_end), SeekBias::Right, &());
-                    new_tree.push_tree(cursor.slice(&tree_end, SeekBias::Right, &()), &());
+                    cursor.seek(&Count(splice_end), Bias::Right, &());
+                    new_tree.push_tree(cursor.slice(&tree_end, Bias::Right, &()), &());
                     new_tree
                 };
 
-                assert_eq!(tree.items(), reference_items);
+                assert_eq!(tree.items(&()), reference_items);
 
-                let mut filter_cursor = tree.filter::<_, Count>(|summary| summary.contains_even);
+                let mut filter_cursor =
+                    tree.filter::<_, Count>(|summary| summary.contains_even, &());
                 let mut reference_filter = tree
-                    .items()
+                    .items(&())
                     .into_iter()
                     .enumerate()
                     .filter(|(_, item)| (item & 1) == 0);
@@ -629,14 +653,14 @@ mod tests {
                     let (reference_index, reference_item) = reference_filter.next().unwrap();
                     assert_eq!(actual_item, &reference_item);
                     assert_eq!(filter_cursor.start().0, reference_index);
-                    filter_cursor.next();
+                    filter_cursor.next(&());
                 }
                 assert!(reference_filter.next().is_none());
 
-                let mut pos = rng.gen_range(0..tree.extent::<Count>().0 + 1);
+                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), Bias::Right, &());
 
                 for i in 0..10 {
                     assert_eq!(cursor.start().0, pos);
@@ -654,13 +678,13 @@ mod tests {
                     }
 
                     if i < 5 {
-                        cursor.next();
+                        cursor.next(&());
                         if pos < reference_items.len() {
                             pos += 1;
                             before_start = false;
                         }
                     } else {
-                        cursor.prev();
+                        cursor.prev(&());
                         if pos == 0 {
                             before_start = true;
                         }
@@ -670,18 +694,10 @@ mod tests {
             }
 
             for _ in 0..10 {
-                let end = rng.gen_range(0..tree.extent::<Count>().0 + 1);
+                let end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
                 let start = rng.gen_range(0..end + 1);
-                let start_bias = if rng.gen() {
-                    SeekBias::Left
-                } else {
-                    SeekBias::Right
-                };
-                let end_bias = if rng.gen() {
-                    SeekBias::Left
-                } else {
-                    SeekBias::Right
-                };
+                let start_bias = if rng.gen() { Bias::Left } else { Bias::Right };
+                let end_bias = if rng.gen() { Bias::Left } else { Bias::Right };
 
                 let mut cursor = tree.cursor::<Count, ()>();
                 cursor.seek(&Count(start), start_bias, &());
@@ -701,7 +717,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), Bias::Right, &()).items(&()),
             Vec::<u8>::new()
         );
         assert_eq!(cursor.item(), None);
@@ -713,34 +729,34 @@ mod tests {
         tree.extend(vec![1], &());
         let mut cursor = tree.cursor::<Count, Sum>();
         assert_eq!(
-            cursor.slice(&Count(0), SeekBias::Right, &()).items(),
+            cursor.slice(&Count(0), Bias::Right, &()).items(&()),
             Vec::<u8>::new()
         );
         assert_eq!(cursor.item(), Some(&1));
         assert_eq!(cursor.prev_item(), None);
         assert_eq!(cursor.start(), &Sum(0));
 
-        cursor.next();
+        cursor.next(&());
         assert_eq!(cursor.item(), None);
         assert_eq!(cursor.prev_item(), Some(&1));
         assert_eq!(cursor.start(), &Sum(1));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&1));
         assert_eq!(cursor.prev_item(), None);
         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), Bias::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), Bias::Right, &());
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
-                .items(),
+                .slice(&tree.extent::<Count>(&()), Bias::Right, &())
+                .items(&()),
             [1]
         );
         assert_eq!(cursor.item(), None);
@@ -752,71 +768,68 @@ mod tests {
         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), Bias::Right, &()).items(&()), [1, 2]);
         assert_eq!(cursor.item(), Some(&3));
         assert_eq!(cursor.prev_item(), Some(&2));
         assert_eq!(cursor.start(), &Sum(3));
 
-        cursor.next();
+        cursor.next(&());
         assert_eq!(cursor.item(), Some(&4));
         assert_eq!(cursor.prev_item(), Some(&3));
         assert_eq!(cursor.start(), &Sum(6));
 
-        cursor.next();
+        cursor.next(&());
         assert_eq!(cursor.item(), Some(&5));
         assert_eq!(cursor.prev_item(), Some(&4));
         assert_eq!(cursor.start(), &Sum(10));
 
-        cursor.next();
+        cursor.next(&());
         assert_eq!(cursor.item(), Some(&6));
         assert_eq!(cursor.prev_item(), Some(&5));
         assert_eq!(cursor.start(), &Sum(15));
 
-        cursor.next();
-        cursor.next();
+        cursor.next(&());
+        cursor.next(&());
         assert_eq!(cursor.item(), None);
         assert_eq!(cursor.prev_item(), Some(&6));
         assert_eq!(cursor.start(), &Sum(21));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&6));
         assert_eq!(cursor.prev_item(), Some(&5));
         assert_eq!(cursor.start(), &Sum(15));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&5));
         assert_eq!(cursor.prev_item(), Some(&4));
         assert_eq!(cursor.start(), &Sum(10));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&4));
         assert_eq!(cursor.prev_item(), Some(&3));
         assert_eq!(cursor.start(), &Sum(6));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&3));
         assert_eq!(cursor.prev_item(), Some(&2));
         assert_eq!(cursor.start(), &Sum(3));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&2));
         assert_eq!(cursor.prev_item(), Some(&1));
         assert_eq!(cursor.start(), &Sum(1));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), Some(&1));
         assert_eq!(cursor.prev_item(), None);
         assert_eq!(cursor.start(), &Sum(0));
 
-        cursor.prev();
+        cursor.prev(&());
         assert_eq!(cursor.item(), None);
         assert_eq!(cursor.prev_item(), None);
         assert_eq!(cursor.start(), &Sum(0));
 
-        cursor.next();
+        cursor.next(&());
         assert_eq!(cursor.item(), Some(&1));
         assert_eq!(cursor.prev_item(), None);
         assert_eq!(cursor.start(), &Sum(0));
@@ -824,19 +837,19 @@ mod tests {
         let mut cursor = tree.cursor::<Count, Sum>();
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
-                .items(),
-            tree.items()
+                .slice(&tree.extent::<Count>(&()), Bias::Right, &())
+                .items(&()),
+            tree.items(&())
         );
         assert_eq!(cursor.item(), None);
         assert_eq!(cursor.prev_item(), Some(&6));
         assert_eq!(cursor.start(), &Sum(21));
 
-        cursor.seek(&Count(3), SeekBias::Right, &());
+        cursor.seek(&Count(3), Bias::Right, &());
         assert_eq!(
             cursor
-                .slice(&tree.extent::<Count>(), SeekBias::Right, &())
-                .items(),
+                .slice(&tree.extent::<Count>(&()), Bias::Right, &())
+                .items(&()),
             [4, 5, 6]
         );
         assert_eq!(cursor.item(), None);
@@ -844,23 +857,23 @@ mod tests {
         assert_eq!(cursor.start(), &Sum(21));
 
         // Seeking can bias left or right
-        cursor.seek(&Count(1), SeekBias::Left, &());
+        cursor.seek(&Count(1), Bias::Left, &());
         assert_eq!(cursor.item(), Some(&1));
-        cursor.seek(&Count(1), SeekBias::Right, &());
+        cursor.seek(&Count(1), Bias::Right, &());
         assert_eq!(cursor.item(), Some(&2));
 
         // Slicing without resetting starts from where the cursor is parked at.
-        cursor.seek(&Count(1), SeekBias::Right, &());
+        cursor.seek(&Count(1), Bias::Right, &());
         assert_eq!(
-            cursor.slice(&Count(3), SeekBias::Right, &()).items(),
+            cursor.slice(&Count(3), Bias::Right, &()).items(&()),
             vec![2, 3]
         );
         assert_eq!(
-            cursor.slice(&Count(6), SeekBias::Left, &()).items(),
+            cursor.slice(&Count(6), Bias::Left, &()).items(&()),
             vec![4, 5]
         );
         assert_eq!(
-            cursor.slice(&Count(6), SeekBias::Right, &()).items(),
+            cursor.slice(&Count(6), Bias::Right, &()).items(&()),
             vec![6]
         );
     }
@@ -870,7 +883,7 @@ mod tests {
         let mut tree = SumTree::<u8>::new();
 
         let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)], &());
-        assert_eq!(tree.items(), vec![0, 1, 2]);
+        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));
@@ -878,7 +891,7 @@ mod tests {
         assert_eq!(tree.get(&4, &()), None);
 
         let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)], &());
-        assert_eq!(tree.items(), vec![1, 2, 4]);
+        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));
@@ -933,19 +946,19 @@ mod tests {
     }
 
     impl<'a> Dimension<'a, IntegersSummary> for u8 {
-        fn add_summary(&mut self, summary: &IntegersSummary) {
+        fn add_summary(&mut self, summary: &IntegersSummary, _: &()) {
             *self = summary.max;
         }
     }
 
     impl<'a> Dimension<'a, IntegersSummary> for Count {
-        fn add_summary(&mut self, summary: &IntegersSummary) {
+        fn add_summary(&mut self, summary: &IntegersSummary, _: &()) {
             self.0 += summary.count.0;
         }
     }
 
     impl<'a> Dimension<'a, IntegersSummary> for Sum {
-        fn add_summary(&mut self, summary: &IntegersSummary) {
+        fn add_summary(&mut self, summary: &IntegersSummary, _: &()) {
             self.0 += summary.sum.0;
         }
     }

zed/src/sum_tree/cursor.rs 🔗

@@ -49,10 +49,10 @@ where
         &self.sum_dimension
     }
 
-    pub fn end(&self) -> U {
+    pub fn end(&self, cx: &<T::Summary as Summary>::Context) -> U {
         if let Some(item_summary) = self.item_summary() {
             let mut end = self.start().clone();
-            end.add_summary(item_summary);
+            end.add_summary(item_summary, cx);
             end
         } else {
             self.start().clone()
@@ -134,13 +134,13 @@ where
     }
 
     #[allow(unused)]
-    pub fn prev(&mut self) {
+    pub fn prev(&mut self, cx: &<T::Summary as Summary>::Context) {
         assert!(self.did_seek, "Must seek before calling this method");
 
         if self.at_end {
             self.seek_dimension = S::default();
             self.sum_dimension = U::default();
-            self.descend_to_last_item(self.tree);
+            self.descend_to_last_item(self.tree, cx);
             self.at_end = false;
         } else {
             while let Some(entry) = self.stack.pop() {
@@ -167,8 +167,8 @@ where
                             ..
                         } => {
                             for summary in &child_summaries[0..new_index] {
-                                self.seek_dimension.add_summary(summary);
-                                self.sum_dimension.add_summary(summary);
+                                self.seek_dimension.add_summary(summary, cx);
+                                self.sum_dimension.add_summary(summary, cx);
                             }
                             self.stack.push(StackEntry {
                                 tree: entry.tree,
@@ -176,12 +176,12 @@ where
                                 seek_dimension: self.seek_dimension.clone(),
                                 sum_dimension: self.sum_dimension.clone(),
                             });
-                            self.descend_to_last_item(&child_trees[new_index]);
+                            self.descend_to_last_item(&child_trees[new_index], cx);
                         }
                         Node::Leaf { item_summaries, .. } => {
                             for item_summary in &item_summaries[0..new_index] {
-                                self.seek_dimension.add_summary(item_summary);
-                                self.sum_dimension.add_summary(item_summary);
+                                self.seek_dimension.add_summary(item_summary, cx);
+                                self.sum_dimension.add_summary(item_summary, cx);
                             }
                             self.stack.push(StackEntry {
                                 tree: entry.tree,
@@ -198,11 +198,11 @@ where
         }
     }
 
-    pub fn next(&mut self) {
-        self.next_internal(|_| true)
+    pub fn next(&mut self, cx: &<T::Summary as Summary>::Context) {
+        self.next_internal(|_| true, cx)
     }
 
-    fn next_internal<F>(&mut self, filter_node: F)
+    fn next_internal<F>(&mut self, filter_node: F, cx: &<T::Summary as Summary>::Context)
     where
         F: Fn(&T::Summary) -> bool,
     {
@@ -229,9 +229,8 @@ where
                         ..
                     } => {
                         if !descend {
-                            let summary = &child_summaries[entry.index];
-                            entry.seek_dimension.add_summary(summary);
-                            entry.sum_dimension.add_summary(summary);
+                            entry.seek_dimension = self.seek_dimension.clone();
+                            entry.sum_dimension = self.sum_dimension.clone();
                             entry.index += 1;
                         }
 
@@ -240,8 +239,8 @@ where
                             if filter_node(next_summary) {
                                 break;
                             } else {
-                                self.seek_dimension.add_summary(next_summary);
-                                self.sum_dimension.add_summary(next_summary);
+                                self.seek_dimension.add_summary(next_summary, cx);
+                                self.sum_dimension.add_summary(next_summary, cx);
                             }
                             entry.index += 1;
                         }
@@ -251,10 +250,10 @@ where
                     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);
+                            self.seek_dimension.add_summary(item_summary, cx);
+                            entry.seek_dimension.add_summary(item_summary, cx);
+                            self.sum_dimension.add_summary(item_summary, cx);
+                            entry.sum_dimension.add_summary(item_summary, cx);
                             entry.index += 1;
                         }
 
@@ -263,10 +262,10 @@ where
                                 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);
+                                    self.seek_dimension.add_summary(next_item_summary, cx);
+                                    entry.seek_dimension.add_summary(next_item_summary, cx);
+                                    self.sum_dimension.add_summary(next_item_summary, cx);
+                                    entry.sum_dimension.add_summary(next_item_summary, cx);
                                     entry.index += 1;
                                 }
                             } else {
@@ -295,7 +294,11 @@ where
         debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf());
     }
 
-    fn descend_to_last_item(&mut self, mut subtree: &'a SumTree<T>) {
+    fn descend_to_last_item(
+        &mut self,
+        mut subtree: &'a SumTree<T>,
+        cx: &<T::Summary as Summary>::Context,
+    ) {
         self.did_seek = true;
         loop {
             match subtree.0.as_ref() {
@@ -305,8 +308,8 @@ where
                     ..
                 } => {
                     for summary in &child_summaries[0..child_summaries.len() - 1] {
-                        self.seek_dimension.add_summary(summary);
-                        self.sum_dimension.add_summary(summary);
+                        self.seek_dimension.add_summary(summary, cx);
+                        self.sum_dimension.add_summary(summary, cx);
                     }
 
                     self.stack.push(StackEntry {
@@ -320,8 +323,8 @@ where
                 Node::Leaf { item_summaries, .. } => {
                     let last_index = item_summaries.len().saturating_sub(1);
                     for item_summary in &item_summaries[0..last_index] {
-                        self.seek_dimension.add_summary(item_summary);
-                        self.sum_dimension.add_summary(item_summary);
+                        self.seek_dimension.add_summary(item_summary, cx);
+                        self.sum_dimension.add_summary(item_summary, cx);
                     }
                     self.stack.push(StackEntry {
                         tree: subtree,
@@ -342,28 +345,28 @@ where
     S: SeekDimension<'a, T::Summary>,
     U: Dimension<'a, T::Summary>,
 {
-    pub fn seek(&mut self, pos: &S, bias: SeekBias, cx: &<T::Summary as Summary>::Context) -> bool {
+    pub fn seek(&mut self, pos: &S, bias: Bias, cx: &<T::Summary as Summary>::Context) -> bool {
         self.reset();
-        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, cx)
+        self.seek_internal::<()>(Some(pos), bias, &mut SeekAggregate::None, cx)
     }
 
     pub fn seek_forward(
         &mut self,
         pos: &S,
-        bias: SeekBias,
+        bias: Bias,
         cx: &<T::Summary as Summary>::Context,
     ) -> bool {
-        self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, cx)
+        self.seek_internal::<()>(Some(pos), bias, &mut SeekAggregate::None, cx)
     }
 
     pub fn slice(
         &mut self,
         end: &S,
-        bias: SeekBias,
+        bias: Bias,
         cx: &<T::Summary as Summary>::Context,
     ) -> SumTree<T> {
         let mut slice = SeekAggregate::Slice(SumTree::new());
-        self.seek_internal::<()>(end, bias, &mut slice, cx);
+        self.seek_internal::<()>(Some(end), bias, &mut slice, cx);
         if let SeekAggregate::Slice(slice) = slice {
             slice
         } else {
@@ -372,9 +375,8 @@ where
     }
 
     pub fn suffix(&mut self, cx: &<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, cx);
+        self.seek_internal::<()>(None, Bias::Right, &mut slice, cx);
         if let SeekAggregate::Slice(slice) = slice {
             slice
         } else {
@@ -382,17 +384,12 @@ where
         }
     }
 
-    pub fn summary<D>(
-        &mut self,
-        end: &S,
-        bias: SeekBias,
-        cx: &<T::Summary as Summary>::Context,
-    ) -> D
+    pub fn summary<D>(&mut self, end: &S, bias: Bias, cx: &<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, cx);
+        self.seek_internal(Some(end), bias, &mut summary, cx);
         if let SeekAggregate::Summary(summary) = summary {
             summary
         } else {
@@ -402,229 +399,126 @@ where
 
     fn seek_internal<D>(
         &mut self,
-        target: &S,
-        bias: SeekBias,
+        target: Option<&S>,
+        bias: Bias,
         aggregate: &mut SeekAggregate<T, D>,
         cx: &<T::Summary as Summary>::Context,
     ) -> bool
     where
         D: Dimension<'a, T::Summary>,
     {
-        debug_assert!(target.cmp(&self.seek_dimension, cx) >= Ordering::Equal);
-        let mut containing_subtree = None;
+        if let Some(target) = target {
+            debug_assert!(
+                target.cmp(&self.seek_dimension, cx) >= Ordering::Equal,
+                "cannot seek backward from {:?} to {:?}",
+                self.seek_dimension,
+                target
+            );
+        }
 
-        if self.did_seek {
-            'outer: while let Some(entry) = self.stack.last_mut() {
-                {
-                    match *entry.tree.0 {
-                        Node::Internal {
-                            ref child_summaries,
-                            ref child_trees,
-                            ..
-                        } => {
-                            entry.index += 1;
-                            for (child_tree, child_summary) in child_trees[entry.index..]
-                                .iter()
-                                .zip(&child_summaries[entry.index..])
-                            {
-                                let mut child_end = self.seek_dimension.clone();
-                                child_end.add_summary(&child_summary);
-
-                                let comparison = target.cmp(&child_end, cx);
-                                if comparison == Ordering::Greater
-                                    || (comparison == Ordering::Equal && bias == SeekBias::Right)
-                                {
-                                    self.seek_dimension = child_end;
-                                    self.sum_dimension.add_summary(child_summary);
-                                    match aggregate {
-                                        SeekAggregate::None => {}
-                                        SeekAggregate::Slice(slice) => {
-                                            slice.push_tree(child_tree.clone(), cx);
-                                        }
-                                        SeekAggregate::Summary(summary) => {
-                                            summary.add_summary(child_summary);
-                                        }
-                                    }
-                                    entry.index += 1;
-                                } else {
-                                    containing_subtree = Some(child_tree);
-                                    break 'outer;
-                                }
-                            }
-                        }
-                        Node::Leaf {
-                            ref items,
-                            ref item_summaries,
-                            ..
-                        } => {
-                            let mut slice_items = ArrayVec::<[T; 2 * TREE_BASE]>::new();
-                            let mut slice_item_summaries =
-                                ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new();
-                            let mut slice_items_summary = match aggregate {
-                                SeekAggregate::Slice(_) => Some(T::Summary::default()),
-                                _ => None,
-                            };
-
-                            for (item, item_summary) in items[entry.index..]
-                                .iter()
-                                .zip(&item_summaries[entry.index..])
-                            {
-                                let mut child_end = self.seek_dimension.clone();
-                                child_end.add_summary(item_summary);
-
-                                let comparison = target.cmp(&child_end, cx);
-                                if comparison == Ordering::Greater
-                                    || (comparison == Ordering::Equal && bias == SeekBias::Right)
-                                {
-                                    self.seek_dimension = child_end;
-                                    self.sum_dimension.add_summary(item_summary);
-                                    match aggregate {
-                                        SeekAggregate::None => {}
-                                        SeekAggregate::Slice(_) => {
-                                            slice_items.push(item.clone());
-                                            slice_item_summaries.push(item_summary.clone());
-                                            slice_items_summary
-                                                .as_mut()
-                                                .unwrap()
-                                                .add_summary(item_summary, cx);
-                                        }
-                                        SeekAggregate::Summary(summary) => {
-                                            summary.add_summary(item_summary);
-                                        }
-                                    }
-                                    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,
-                                            })),
-                                            cx,
-                                        );
-                                    }
-                                    break 'outer;
-                                }
-                            }
+        if !self.did_seek {
+            self.did_seek = true;
+            self.stack.push(StackEntry {
+                tree: self.tree,
+                index: 0,
+                seek_dimension: Default::default(),
+                sum_dimension: Default::default(),
+            });
+        }
 
-                            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,
-                                        })),
-                                        cx,
-                                    );
-                                }
-                            }
-                        }
+        let mut ascending = false;
+        'outer: while let Some(entry) = self.stack.last_mut() {
+            match *entry.tree.0 {
+                Node::Internal {
+                    ref child_summaries,
+                    ref child_trees,
+                    ..
+                } => {
+                    if ascending {
+                        entry.index += 1;
                     }
-                }
 
-                self.stack.pop();
-            }
-        } else {
-            self.did_seek = true;
-            containing_subtree = Some(self.tree);
-        }
+                    for (child_tree, child_summary) in child_trees[entry.index..]
+                        .iter()
+                        .zip(&child_summaries[entry.index..])
+                    {
+                        let mut child_end = self.seek_dimension.clone();
+                        child_end.add_summary(&child_summary, cx);
 
-        if let Some(mut subtree) = containing_subtree {
-            loop {
-                let mut next_subtree = None;
-                match *subtree.0 {
-                    Node::Internal {
-                        ref child_summaries,
-                        ref child_trees,
-                        ..
-                    } => {
-                        for (index, (child_tree, child_summary)) in
-                            child_trees.iter().zip(child_summaries).enumerate()
+                        let comparison =
+                            target.map_or(Ordering::Greater, |t| t.cmp(&child_end, cx));
+                        if comparison == Ordering::Greater
+                            || (comparison == Ordering::Equal && bias == Bias::Right)
                         {
-                            let mut child_end = self.seek_dimension.clone();
-                            child_end.add_summary(child_summary);
-
-                            let comparison = target.cmp(&child_end, cx);
-                            if comparison == Ordering::Greater
-                                || (comparison == Ordering::Equal && bias == SeekBias::Right)
-                            {
-                                self.seek_dimension = child_end;
-                                self.sum_dimension.add_summary(child_summary);
-                                match aggregate {
-                                    SeekAggregate::None => {}
-                                    SeekAggregate::Slice(slice) => {
-                                        slice.push_tree(child_trees[index].clone(), cx);
-                                    }
-                                    SeekAggregate::Summary(summary) => {
-                                        summary.add_summary(child_summary);
-                                    }
+                            self.seek_dimension = child_end;
+                            self.sum_dimension.add_summary(child_summary, cx);
+                            match aggregate {
+                                SeekAggregate::None => {}
+                                SeekAggregate::Slice(slice) => {
+                                    slice.push_tree(child_tree.clone(), cx);
+                                }
+                                SeekAggregate::Summary(summary) => {
+                                    summary.add_summary(child_summary, cx);
                                 }
-                            } else {
-                                self.stack.push(StackEntry {
-                                    tree: subtree,
-                                    index,
-                                    seek_dimension: self.seek_dimension.clone(),
-                                    sum_dimension: self.sum_dimension.clone(),
-                                });
-                                next_subtree = Some(child_tree);
-                                break;
                             }
+                            entry.index += 1;
+                            entry.seek_dimension = self.seek_dimension.clone();
+                            entry.sum_dimension = self.sum_dimension.clone();
+                        } else {
+                            self.stack.push(StackEntry {
+                                tree: child_tree,
+                                index: 0,
+                                seek_dimension: self.seek_dimension.clone(),
+                                sum_dimension: self.sum_dimension.clone(),
+                            });
+                            ascending = false;
+                            continue 'outer;
                         }
                     }
-                    Node::Leaf {
-                        ref items,
-                        ref item_summaries,
-                        ..
-                    } => {
-                        let mut slice_items = ArrayVec::<[T; 2 * TREE_BASE]>::new();
-                        let mut slice_item_summaries =
-                            ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new();
-                        let mut slice_items_summary = match aggregate {
-                            SeekAggregate::Slice(_) => Some(T::Summary::default()),
-                            _ => None,
-                        };
-
-                        for (index, (item, item_summary)) in
-                            items.iter().zip(item_summaries).enumerate()
+                }
+                Node::Leaf {
+                    ref items,
+                    ref item_summaries,
+                    ..
+                } => {
+                    let mut slice_items = ArrayVec::<[T; 2 * TREE_BASE]>::new();
+                    let mut slice_item_summaries = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new();
+                    let mut slice_items_summary = match aggregate {
+                        SeekAggregate::Slice(_) => Some(T::Summary::default()),
+                        _ => None,
+                    };
+
+                    for (item, item_summary) in items[entry.index..]
+                        .iter()
+                        .zip(&item_summaries[entry.index..])
+                    {
+                        let mut child_end = self.seek_dimension.clone();
+                        child_end.add_summary(item_summary, cx);
+
+                        let comparison =
+                            target.map_or(Ordering::Greater, |t| t.cmp(&child_end, cx));
+                        if comparison == Ordering::Greater
+                            || (comparison == Ordering::Equal && bias == Bias::Right)
                         {
-                            let mut child_end = self.seek_dimension.clone();
-                            child_end.add_summary(item_summary);
-
-                            let comparison = target.cmp(&child_end, cx);
-                            if comparison == Ordering::Greater
-                                || (comparison == Ordering::Equal && bias == SeekBias::Right)
-                            {
-                                self.seek_dimension = child_end;
-                                self.sum_dimension.add_summary(item_summary);
-                                match aggregate {
-                                    SeekAggregate::None => {}
-                                    SeekAggregate::Slice(_) => {
-                                        slice_items.push(item.clone());
-                                        slice_items_summary
-                                            .as_mut()
-                                            .unwrap()
-                                            .add_summary(item_summary, cx);
-                                        slice_item_summaries.push(item_summary.clone());
-                                    }
-                                    SeekAggregate::Summary(summary) => {
-                                        summary.add_summary(item_summary);
-                                    }
+                            self.seek_dimension = child_end;
+                            self.sum_dimension.add_summary(item_summary, cx);
+                            match aggregate {
+                                SeekAggregate::None => {}
+                                SeekAggregate::Slice(_) => {
+                                    slice_items.push(item.clone());
+                                    slice_item_summaries.push(item_summary.clone());
+                                    slice_items_summary
+                                        .as_mut()
+                                        .unwrap()
+                                        .add_summary(item_summary, cx);
+                                }
+                                SeekAggregate::Summary(summary) => {
+                                    summary.add_summary(item_summary, cx);
                                 }
-                            } else {
-                                self.stack.push(StackEntry {
-                                    tree: subtree,
-                                    index,
-                                    seek_dimension: self.seek_dimension.clone(),
-                                    sum_dimension: self.sum_dimension.clone(),
-                                });
-                                break;
                             }
-                        }
-
-                        if let SeekAggregate::Slice(slice) = aggregate {
-                            if !slice_items.is_empty() {
+                            entry.index += 1;
+                        } else {
+                            if let SeekAggregate::Slice(slice) = aggregate {
                                 slice.push_tree(
                                     SumTree(Arc::new(Node::Leaf {
                                         summary: slice_items_summary.unwrap(),
@@ -634,47 +528,59 @@ where
                                     cx,
                                 );
                             }
+                            break 'outer;
                         }
                     }
-                };
 
-                if let Some(next_subtree) = next_subtree {
-                    subtree = next_subtree;
-                } else {
-                    break;
+                    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,
+                                })),
+                                cx,
+                            );
+                        }
+                    }
                 }
             }
+
+            self.stack.pop();
+            ascending = true;
         }
 
         self.at_end = self.stack.is_empty();
         debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf());
-        if bias == SeekBias::Left {
-            let mut end = self.seek_dimension.clone();
+
+        let mut end = self.seek_dimension.clone();
+        if bias == Bias::Left {
             if let Some(summary) = self.item_summary() {
-                end.add_summary(summary);
+                end.add_summary(summary, cx);
             }
-            target.cmp(&end, cx) == Ordering::Equal
-        } else {
-            target.cmp(&self.seek_dimension, cx) == Ordering::Equal
         }
+
+        target.map_or(false, |t| t.cmp(&end, cx) == Ordering::Equal)
     }
 }
 
-impl<'a, T, S, U> Iterator for Cursor<'a, T, S, U>
+impl<'a, T, S, Seek, Sum> Iterator for Cursor<'a, T, Seek, Sum>
 where
-    T: Item,
-    S: Dimension<'a, T::Summary>,
-    U: Dimension<'a, T::Summary>,
+    T: Item<Summary = S>,
+    S: Summary<Context = ()>,
+    Seek: Dimension<'a, T::Summary>,
+    Sum: Dimension<'a, T::Summary>,
 {
     type Item = &'a T;
 
     fn next(&mut self) -> Option<Self::Item> {
         if !self.did_seek {
-            self.next();
+            self.next(&());
         }
 
         if let Some(item) = self.item() {
-            self.next();
+            self.next(&());
             Some(item)
         } else {
             None
@@ -693,9 +599,13 @@ where
     T: Item,
     U: Dimension<'a, T::Summary>,
 {
-    pub fn new(tree: &'a SumTree<T>, filter_node: F) -> Self {
+    pub fn new(
+        tree: &'a SumTree<T>,
+        filter_node: F,
+        cx: &<T::Summary as Summary>::Context,
+    ) -> Self {
         let mut cursor = tree.cursor::<(), U>();
-        cursor.next_internal(&filter_node);
+        cursor.next_internal(&filter_node, cx);
         Self {
             cursor,
             filter_node,
@@ -710,22 +620,23 @@ where
         self.cursor.item()
     }
 
-    pub fn next(&mut self) {
-        self.cursor.next_internal(&self.filter_node);
+    pub fn next(&mut self, cx: &<T::Summary as Summary>::Context) {
+        self.cursor.next_internal(&self.filter_node, cx);
     }
 }
 
-impl<'a, F, T, U> Iterator for FilterCursor<'a, F, T, U>
+impl<'a, F, T, S, U> Iterator for FilterCursor<'a, F, T, U>
 where
     F: Fn(&T::Summary) -> bool,
-    T: Item,
+    T: Item<Summary = S>,
+    S: Summary<Context = ()>,
     U: Dimension<'a, T::Summary>,
 {
     type Item = &'a T;
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(item) = self.item() {
-            self.cursor.next_internal(&self.filter_node);
+            self.cursor.next_internal(&self.filter_node, &());
             Some(item)
         } else {
             None

zed/src/time.rs 🔗

@@ -1,20 +1,24 @@
 use smallvec::SmallVec;
-use std::cmp::{self, Ordering};
-use std::ops::{Add, AddAssign};
+use std::{
+    cmp::{self, Ordering},
+    fmt,
+    ops::{Add, AddAssign},
+    slice,
+};
 
 pub type ReplicaId = u16;
 pub type Seq = u32;
 
-#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)]
+#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Ord, PartialOrd)]
 pub struct Local {
     pub replica_id: ReplicaId,
     pub value: Seq,
 }
 
-#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
 pub struct Lamport {
-    pub value: Seq,
     pub replica_id: ReplicaId,
+    pub value: Seq,
 }
 
 impl Local {
@@ -54,7 +58,7 @@ impl<'a> AddAssign<&'a Local> for Local {
     }
 }
 
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
+#[derive(Clone, Default, Hash, Eq, PartialEq)]
 pub struct Global(SmallVec<[Local; 3]>);
 
 impl Global {
@@ -81,12 +85,26 @@ impl Global {
         }
     }
 
-    pub fn observe_all(&mut self, other: &Self) {
+    pub fn join(&mut self, other: &Self) {
         for timestamp in other.0.iter() {
             self.observe(*timestamp);
         }
     }
 
+    pub fn meet(&mut self, other: &Self) {
+        for timestamp in other.0.iter() {
+            if let Some(entry) = self
+                .0
+                .iter_mut()
+                .find(|t| t.replica_id == timestamp.replica_id)
+            {
+                entry.value = cmp::min(entry.value, timestamp.value);
+            } else {
+                self.0.push(*timestamp);
+            }
+        }
+    }
+
     pub fn observed(&self, timestamp: Local) -> bool {
         self.get(timestamp.replica_id) >= timestamp.value
     }
@@ -94,6 +112,10 @@ impl Global {
     pub fn changed_since(&self, other: &Self) -> bool {
         self.0.iter().any(|t| t.value > other.get(t.replica_id))
     }
+
+    pub fn iter(&self) -> slice::Iter<Local> {
+        self.0.iter()
+    }
 }
 
 impl PartialOrd for Global {
@@ -117,6 +139,21 @@ impl PartialOrd for Global {
     }
 }
 
+impl Ord for Lamport {
+    fn cmp(&self, other: &Self) -> Ordering {
+        // Use the replica id to break ties between concurrent events.
+        self.value
+            .cmp(&other.value)
+            .then_with(|| self.replica_id.cmp(&other.replica_id))
+    }
+}
+
+impl PartialOrd for Lamport {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
 impl Lamport {
     pub fn new(replica_id: ReplicaId) -> Self {
         Self {
@@ -135,3 +172,28 @@ impl Lamport {
         self.value = cmp::max(self.value, timestamp.value) + 1;
     }
 }
+
+impl fmt::Debug for Local {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Local {{{}: {}}}", self.replica_id, self.value)
+    }
+}
+
+impl fmt::Debug for Lamport {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Lamport {{{}: {}}}", self.replica_id, self.value)
+    }
+}
+
+impl fmt::Debug for Global {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Global {{")?;
+        for (i, element) in self.0.iter().enumerate() {
+            if i > 0 {
+                write!(f, ", ")?;
+            }
+            write!(f, "{}: {}", element.replica_id, element.value)?;
+        }
+        write!(f, "}}")
+    }
+}

zed/src/util.rs 🔗

@@ -1,6 +1,29 @@
 use rand::prelude::*;
 use std::cmp::Ordering;
 
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
+pub enum Bias {
+    Left,
+    Right,
+}
+
+impl PartialOrd for Bias {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for Bias {
+    fn cmp(&self, other: &Self) -> Ordering {
+        match (self, other) {
+            (Self::Left, Self::Left) => Ordering::Equal,
+            (Self::Left, Self::Right) => Ordering::Less,
+            (Self::Right, Self::Right) => Ordering::Equal,
+            (Self::Right, Self::Left) => Ordering::Greater,
+        }
+    }
+}
+
 pub fn post_inc(value: &mut usize) -> usize {
     let prev = *value;
     *value += 1;

zed/src/worktree.rs 🔗

@@ -4,7 +4,8 @@ mod ignore;
 
 use crate::{
     editor::{History, Rope},
-    sum_tree::{self, Cursor, Edit, SeekBias, SumTree},
+    sum_tree::{self, Cursor, Edit, SumTree},
+    util::Bias,
 };
 use ::ignore::gitignore::Gitignore;
 use anyhow::{Context, Result};
@@ -295,7 +296,7 @@ impl Snapshot {
         }
         let path = path.as_ref();
         let mut cursor = self.entries.cursor::<_, ()>();
-        if cursor.seek(&PathSearch::Exact(path), SeekBias::Left, &()) {
+        if cursor.seek(&PathSearch::Exact(path), Bias::Left, &()) {
             let entry = cursor.item().unwrap();
             if entry.path.as_ref() == path {
                 return matches!(entry.kind, EntryKind::PendingDir);
@@ -310,7 +311,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()), Bias::Left, &()) {
             cursor.item()
         } else {
             None
@@ -367,8 +368,8 @@ impl Snapshot {
     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, &());
+            let mut new_entries = cursor.slice(&PathSearch::Exact(path), Bias::Left, &());
+            cursor.seek_forward(&PathSearch::Successor(path), Bias::Left, &());
             new_entries.push_tree(cursor.suffix(&()), &());
             new_entries
         };
@@ -603,7 +604,7 @@ impl Default for PathKey {
 }
 
 impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
-    fn add_summary(&mut self, summary: &'a EntrySummary) {
+    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
         self.0 = summary.max_path.clone();
     }
 }
@@ -643,7 +644,7 @@ impl<'a> Default for PathSearch<'a> {
 }
 
 impl<'a: 'b, 'b> sum_tree::Dimension<'a, EntrySummary> for PathSearch<'b> {
-    fn add_summary(&mut self, summary: &'a EntrySummary) {
+    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
         *self = Self::Exact(summary.max_path.as_ref());
     }
 }
@@ -652,7 +653,7 @@ impl<'a: 'b, 'b> sum_tree::Dimension<'a, EntrySummary> for PathSearch<'b> {
 pub struct FileCount(usize);
 
 impl<'a> sum_tree::Dimension<'a, EntrySummary> for FileCount {
-    fn add_summary(&mut self, summary: &'a EntrySummary) {
+    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
         self.0 += summary.file_count;
     }
 }
@@ -661,7 +662,7 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for FileCount {
 pub struct VisibleFileCount(usize);
 
 impl<'a> sum_tree::Dimension<'a, EntrySummary> for VisibleFileCount {
-    fn add_summary(&mut self, summary: &'a EntrySummary) {
+    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
         self.0 += summary.visible_file_count;
     }
 }
@@ -1296,13 +1297,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), Bias::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), Bias::Right, &());
         Self::Visible(cursor)
     }
 
@@ -1310,11 +1311,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), Bias::Right, &());
             }
             Self::Visible(cursor) => {
                 let ix = *cursor.start();
-                cursor.seek_forward(&VisibleFileCount(ix.0 + 1), SeekBias::Right, &());
+                cursor.seek_forward(&VisibleFileCount(ix.0 + 1), Bias::Right, &());
             }
         }
     }
@@ -1348,7 +1349,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), Bias::Right, &());
         Self {
             parent_path,
             cursor,
@@ -1363,7 +1364,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()), Bias::Left, &());
                 Some(item)
             } else {
                 None