WIP: Get the type checker passing...

Antonio Scandurra and Max Brunsfeld created

...but not the borrow checker!

Co-Authored-By: Max Brunsfeld <max@zed.dev>

Change summary

zed/src/editor/buffer/mod.rs           | 526 +++++++++------------------
zed/src/editor/buffer/text.rs          |  54 --
zed/src/editor/display_map/fold_map.rs |  19 
zed/src/editor/display_map/mod.rs      |   2 
zed/src/operation_queue.rs             |   3 
zed/src/sum_tree/mod.rs                | 111 ++---
zed/src/worktree.rs                    |  19 
7 files changed, 240 insertions(+), 494 deletions(-)

Detailed changes

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

@@ -1,15 +1,13 @@
 mod anchor;
 mod point;
 mod selection;
-mod text;
 
 pub use anchor::*;
 pub use point::*;
-use ropey::Rope;
+use ropey::{Rope, RopeSlice};
 use seahash::SeaHasher;
 pub use selection::*;
 use similar::{ChangeTag, TextDiff};
-pub use text::*;
 
 use crate::{
     operation_queue::{self, OperationQueue},
@@ -79,10 +77,6 @@ pub struct Buffer {
     lamport_clock: time::Lamport,
 }
 
-pub struct Snapshot {
-    fragments: SumTree<Fragment>,
-}
-
 #[derive(Clone)]
 struct Transaction {
     start: time::Global,
@@ -243,18 +237,6 @@ impl UndoMap {
     }
 }
 
-#[derive(Clone)]
-pub struct CharIter<'a> {
-    fragments_cursor: Cursor<'a, Fragment, usize, usize>,
-    fragment_chars: str::Chars<'a>,
-}
-
-#[derive(Clone)]
-pub struct FragmentIter<'a> {
-    cursor: Cursor<'a, Fragment, usize, usize>,
-    started: bool,
-}
-
 struct Edits<'a, F: Fn(&FragmentSummary) -> bool> {
     cursor: FilterCursor<'a, F, Fragment, usize>,
     undos: &'a UndoMap,
@@ -312,21 +294,60 @@ pub struct FragmentSummary {
 
 #[derive(Default, Clone, Debug, PartialEq, Eq)]
 struct FragmentTextSummary {
-    visible: TextSummary,
-    deleted: TextSummary,
+    visible: usize,
+    deleted: usize,
 }
 
 impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
     fn add_summary(&mut self, summary: &'a FragmentSummary) {
-        self.visible += &summary.text.visible;
-        self.deleted += &summary.text.deleted;
+        self.visible += summary.text.visible;
+        self.deleted += summary.text.deleted;
     }
 }
 
-#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd)]
-struct FragmentExtent {
-    chars: usize,
-    lines: Point,
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
+pub struct TextSummary {
+    pub chars: usize,
+    pub bytes: usize,
+    pub lines: Point,
+}
+
+impl<'a> From<RopeSlice<'a>> for TextSummary {
+    fn from(slice: RopeSlice<'a>) -> Self {
+        let last_row = slice.len_lines() - 1;
+        let last_column = slice.line(last_row).len_chars();
+        Self {
+            chars: slice.len_chars(),
+            bytes: slice.len_bytes(),
+            lines: Point::new(last_row as u32, last_column as u32),
+        }
+    }
+}
+
+impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
+    fn add_assign(&mut self, other: &'a Self) {
+        // let joined_line_len = self.lines.column + other.first_line_len;
+        // if joined_line_len > self.rightmost_point.column {
+        //     self.rightmost_point = Point::new(self.lines.row, joined_line_len);
+        // }
+        // if other.rightmost_point.column > self.rightmost_point.column {
+        //     self.rightmost_point = self.lines + &other.rightmost_point;
+        // }
+
+        // if self.lines.row == 0 {
+        //     self.first_line_len += other.first_line_len;
+        // }
+
+        self.chars += other.chars;
+        self.bytes += other.bytes;
+        self.lines += &other.lines;
+    }
+}
+
+impl std::ops::AddAssign<Self> for TextSummary {
+    fn add_assign(&mut self, other: Self) {
+        *self += &other;
+    }
 }
 
 #[derive(Eq, PartialEq, Clone, Debug)]
@@ -455,7 +476,6 @@ impl Buffer {
                     extent: 0,
                 },
                 &(),
-                &(),
             ),
         );
         fragments.push(
@@ -464,7 +484,6 @@ impl Buffer {
                 base_insertion.clone(),
                 0..0,
             ),
-            &FragmentContext::default(),
             &(),
         );
 
@@ -480,11 +499,9 @@ impl Buffer {
                     extent: range_in_insertion.end,
                 },
                 &(),
-                &(),
             );
             fragments.push(
                 Fragment::new(base_fragment_id, base_insertion, range_in_insertion.clone()),
-                &FragmentContext::new(base_text, Rope::new(), Default::default()),
                 &(),
             );
         }
@@ -511,10 +528,8 @@ impl Buffer {
         }
     }
 
-    pub fn snapshot(&self) -> Snapshot {
-        Snapshot {
-            fragments: self.fragments.clone(),
-        }
+    pub fn snapshot(&self) -> Rope {
+        self.visible_text.clone()
     }
 
     pub fn file(&self) -> Option<&FileHandle> {
@@ -625,7 +640,7 @@ impl Buffer {
     }
 
     pub fn text_summary(&self) -> TextSummary {
-        self.fragments.extent::<TextSummary>()
+        TextSummary::from(self.visible_text.slice(..))
     }
 
     pub fn text_summary_for_range(&self, range: Range<usize>) -> TextSummary {
@@ -680,7 +695,7 @@ impl Buffer {
     }
 
     pub fn max_point(&self) -> Point {
-        TextSummary::from(&self.visible_text).lines
+        self.text_summary().lines
     }
 
     pub fn line(&self, row: u32) -> Result<String> {
@@ -703,13 +718,13 @@ impl Buffer {
         Ok(self.chars_at(start)?.take(end - start))
     }
 
-    pub fn chars(&self) -> CharIter {
+    pub fn chars(&self) -> ropey::iter::Chars {
         self.chars_at(0).unwrap()
     }
 
-    pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<CharIter> {
+    pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<ropey::iter::Chars> {
         let offset = position.to_offset(self)?;
-        Ok(CharIter::new(&self.fragments, offset))
+        Ok(self.visible_text.chars_at(offset))
     }
 
     pub fn selections_changed_since(&self, since: SelectionsVersion) -> bool {
@@ -1081,15 +1096,7 @@ impl Buffer {
         let start_fragment = cursor.item().unwrap();
         if start_offset == start_fragment.range_in_insertion.end {
             // TODO: maybe don't recompute this fragment and its summary.
-            new_fragments.push(
-                cursor.item().unwrap().clone(),
-                &FragmentContext::new(
-                    new_visible_text.clone(),
-                    new_deleted_text.clone(),
-                    cursor.start().clone(),
-                ),
-                &(),
-            );
+            new_fragments.push(cursor.item().unwrap().clone(), &());
             cursor.next();
         }
 
@@ -1128,30 +1135,12 @@ impl Buffer {
                     None
                 };
                 if let Some(fragment) = before_range {
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(fragment) = insertion {
-                    new_visible_text.insert(
-                        new_fragments.summary().text.visible.chars,
-                        new_text.unwrap(),
-                    );
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_visible_text
+                        .insert(new_fragments.summary().text.visible, new_text.unwrap());
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(mut fragment) = within_range {
                     if fragment.was_visible(&version_in_range, &self.undo_map) {
@@ -1159,35 +1148,19 @@ impl Buffer {
                         fragment.visible = false;
 
                         // TODO: avoid calling to_string on rope slice.
-                        let deleted_start = new_fragments.summary().text.visible.chars;
+                        let deleted_start = new_fragments.summary().text.visible;
                         let deleted_range = deleted_start..deleted_start + fragment.len();
                         new_deleted_text.insert(
-                            new_fragments.summary().text.deleted.chars,
+                            new_fragments.summary().text.deleted,
                             &new_visible_text.slice(deleted_range).to_string(),
                         );
                         new_visible_text.remove(deleted_range);
                     }
 
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_fragments.push(fragment, &());
                 }
                 if let Some(fragment) = after_range {
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_fragments.push(fragment, &());
                 }
             } else {
                 if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
@@ -1199,16 +1172,8 @@ impl Buffer {
                         local_timestamp,
                         lamport_timestamp,
                     );
-                    new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_visible_text.insert(new_fragments.summary().text.visible, new_text);
+                    new_fragments.push(fragment, &());
                 }
 
                 if fragment.id < end_fragment_id
@@ -1218,24 +1183,16 @@ impl Buffer {
                     fragment.visible = false;
 
                     // TODO: avoid calling to_string on rope slice.
-                    let deleted_start = new_fragments.summary().text.visible.chars;
+                    let deleted_start = new_fragments.summary().text.visible;
                     let deleted_range = deleted_start..deleted_start + fragment.len();
                     new_deleted_text.insert(
-                        new_fragments.summary().text.deleted.chars,
+                        new_fragments.summary().text.deleted,
                         &new_visible_text.slice(deleted_range).to_string(),
                     );
                     new_visible_text.remove(deleted_range);
                 }
 
-                new_fragments.push(
-                    fragment,
-                    &FragmentContext::new(
-                        new_visible_text.clone(),
-                        new_deleted_text.clone(),
-                        new_fragments.summary().text,
-                    ),
-                    &(),
-                );
+                new_fragments.push(fragment, &());
             }
 
             cursor.next();
@@ -1249,16 +1206,8 @@ impl Buffer {
                 local_timestamp,
                 lamport_timestamp,
             );
-            new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
-            new_fragments.push(
-                fragment,
-                &FragmentContext::new(
-                    new_visible_text.clone(),
-                    new_deleted_text.clone(),
-                    new_fragments.summary().text,
-                ),
-                &(),
-            );
+            new_visible_text.insert(new_fragments.summary().text.visible, new_text);
+            new_fragments.push(fragment, &());
         }
 
         new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
@@ -1363,32 +1312,24 @@ impl Buffer {
 
                 // TODO: avoid calling to_string on rope slice.
                 if fragment.visible && !was_visible {
-                    let visible_start = new_fragments.summary().text.deleted.chars;
+                    let visible_start = new_fragments.summary().text.deleted;
                     let visible_range = visible_start..visible_start + fragment.len();
                     new_visible_text.insert(
-                        new_fragments.summary().text.visible.chars,
+                        new_fragments.summary().text.visible,
                         &new_deleted_text.slice(visible_range).to_string(),
                     );
                     new_deleted_text.remove(visible_range);
                 } else if !fragment.visible && was_visible {
-                    let deleted_start = new_fragments.summary().text.visible.chars;
+                    let deleted_start = new_fragments.summary().text.visible;
                     let deleted_range = deleted_start..deleted_start + fragment.len();
                     new_deleted_text.insert(
-                        new_fragments.summary().text.deleted.chars,
+                        new_fragments.summary().text.deleted,
                         &new_visible_text.slice(deleted_range).to_string(),
                     );
                     new_visible_text.remove(deleted_range);
                 }
 
-                new_fragments.push(
-                    fragment,
-                    &FragmentContext::new(
-                        new_visible_text.clone(),
-                        new_deleted_text.clone(),
-                        new_fragments.summary().text,
-                    ),
-                    &(),
-                );
+                new_fragments.push(fragment, &());
                 cursor.next();
                 if let Some(split_id) = insertion_splits.next() {
                     new_fragments.push_tree(
@@ -1418,33 +1359,25 @@ impl Buffer {
 
                         // TODO: avoid calling to_string on rope slice.
                         if fragment.visible && !was_visible {
-                            let visible_start = new_fragments.summary().text.deleted.chars;
+                            let visible_start = new_fragments.summary().text.deleted;
                             let visible_range = visible_start..visible_start + fragment.len();
                             new_visible_text.insert(
-                                new_fragments.summary().text.visible.chars,
+                                new_fragments.summary().text.visible,
                                 &new_deleted_text.slice(visible_range).to_string(),
                             );
                             new_deleted_text.remove(visible_range);
                         } else if !fragment.visible && was_visible {
-                            let deleted_start = new_fragments.summary().text.visible.chars;
+                            let deleted_start = new_fragments.summary().text.visible;
                             let deleted_range = deleted_start..deleted_start + fragment.len();
                             new_deleted_text.insert(
-                                new_fragments.summary().text.deleted.chars,
+                                new_fragments.summary().text.deleted,
                                 &new_visible_text.slice(deleted_range).to_string(),
                             );
                             new_visible_text.remove(deleted_range);
                         }
                     }
 
-                    new_fragments.push(
-                        fragment,
-                        &FragmentContext::new(
-                            new_visible_text.clone(),
-                            new_deleted_text.clone(),
-                            new_fragments.summary().text,
-                        ),
-                        &(),
-                    );
+                    new_fragments.push(fragment, &());
                     cursor.next();
                 }
             }
@@ -1579,17 +1512,13 @@ impl Buffer {
                     prefix.id =
                         FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
                     fragment.range_in_insertion.start = prefix.range_in_insertion.end;
-                    new_fragments.push(
-                        prefix.clone(),
-                        &FragmentContext::new(new_visible_text.clone()) & (),
-                    );
+                    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;
                 }
@@ -1614,6 +1543,7 @@ impl Buffer {
                             local_timestamp,
                             lamport_timestamp,
                         );
+                        new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
                         new_fragments.push(new_fragment, &());
                     }
                 }
@@ -1629,6 +1559,15 @@ impl Buffer {
                         if fragment.visible {
                             prefix.deletions.insert(local_timestamp);
                             prefix.visible = false;
+
+                            // TODO: avoid calling to_string on rope slice.
+                            let deleted_start = new_fragments.summary().text.visible;
+                            let deleted_range = deleted_start..deleted_start + prefix.len();
+                            new_deleted_text.insert(
+                                new_fragments.summary().text.deleted,
+                                &new_visible_text.slice(deleted_range).to_string(),
+                            );
+                            new_visible_text.remove(deleted_range);
                         }
                         fragment.range_in_insertion.start = prefix.range_in_insertion.end;
                         new_fragments.push(prefix.clone(), &());
@@ -1649,6 +1588,15 @@ impl Buffer {
                     if fragment.visible {
                         fragment.deletions.insert(local_timestamp);
                         fragment.visible = false;
+
+                        // TODO: avoid calling to_string on rope slice.
+                        let deleted_start = new_fragments.summary().text.visible;
+                        let deleted_range = deleted_start..deleted_start + fragment.len();
+                        new_deleted_text.insert(
+                            new_fragments.summary().text.deleted,
+                            &new_visible_text.slice(deleted_range).to_string(),
+                        );
+                        new_visible_text.remove(deleted_range);
                     }
                 }
 
@@ -1712,6 +1660,15 @@ impl Buffer {
                         if new_fragment.visible {
                             new_fragment.deletions.insert(local_timestamp);
                             new_fragment.visible = false;
+
+                            // TODO: avoid calling to_string on rope slice.
+                            let deleted_start = new_fragments.summary().text.visible;
+                            let deleted_range = deleted_start..deleted_start + new_fragment.len();
+                            new_deleted_text.insert(
+                                new_fragments.summary().text.deleted,
+                                &new_visible_text.slice(deleted_range).to_string(),
+                            );
+                            new_visible_text.remove(deleted_range);
                         }
                         new_fragments.push(new_fragment, &());
                         cursor.next();
@@ -1775,6 +1732,7 @@ impl Buffer {
                     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,
@@ -1784,10 +1742,11 @@ impl Buffer {
                 let new_fragment = self.build_fragment_to_insert(
                     &last_fragment,
                     None,
-                    new_text,
+                    &new_text,
                     local_timestamp,
                     lamport_timestamp,
                 );
+                new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
                 new_fragments.push(new_fragment, &());
             }
         } else {
@@ -1797,6 +1756,8 @@ impl Buffer {
             );
         }
 
+        self.visible_text = new_visible_text;
+        self.deleted_text = new_deleted_text;
         self.fragments = new_fragments;
         ops
     }
@@ -1864,7 +1825,6 @@ impl Buffer {
                         fragment_id: fragment.id.clone(),
                     },
                     &(),
-                    &(),
                 );
             }
 
@@ -1875,7 +1835,6 @@ impl Buffer {
                         fragment_id: fragment.id.clone(),
                     },
                     &(),
-                    &(),
                 );
             }
 
@@ -1886,7 +1845,6 @@ impl Buffer {
                         fragment_id: fragment.id.clone(),
                     },
                     &(),
-                    &(),
                 );
             }
 
@@ -1927,7 +1885,6 @@ impl Buffer {
                 fragment_id: new_fragment_id.clone(),
             },
             &(),
-            &(),
         );
         self.insertion_splits.insert(insertion_id, split_tree);
 
@@ -2021,7 +1978,7 @@ impl Buffer {
     fn summary_for_anchor(&self, anchor: &Anchor) -> Result<TextSummary> {
         match anchor {
             Anchor::Start => Ok(TextSummary::default()),
-            Anchor::End => Ok(self.fragments.summary().text_summary),
+            Anchor::End => Ok(self.text_summary()),
             Anchor::Middle {
                 insertion_id,
                 offset,
@@ -2042,37 +1999,29 @@ impl Buffer {
                     .item()
                     .ok_or_else(|| anyhow!("split offset is out of range"))?;
 
-                let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, TextSummary>();
+                let mut fragments_cursor = self
+                    .fragments
+                    .cursor::<FragmentIdRef, FragmentTextSummary>();
                 fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &());
                 let fragment = fragments_cursor
                     .item()
                     .ok_or_else(|| anyhow!("fragment id does not exist"))?;
 
-                let mut summary = fragments_cursor.start().clone();
+                let mut ix = fragments_cursor.start().clone().visible;
                 if fragment.visible {
-                    summary += fragment
-                        .text
-                        .slice(..offset - fragment.range_in_insertion.start)
-                        .summary();
+                    ix += offset - fragment.range_in_insertion.start;
                 }
-                Ok(summary)
+                Ok(self.text_summary_for_range(0..ix))
             }
         }
     }
 
-    #[allow(dead_code)]
     pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
-        let mut fragments_cursor = self.fragments.cursor::<usize, TextSummary>();
-        fragments_cursor.seek(&offset, SeekBias::Left, &());
-        fragments_cursor
-            .item()
-            .ok_or_else(|| anyhow!("offset is out of range"))
-            .map(|fragment| {
-                let overshoot = fragment
-                    .point_for_offset(offset - &fragments_cursor.start().chars)
-                    .unwrap();
-                fragments_cursor.start().lines + &overshoot
-            })
+        if offset <= self.len() {
+            Ok(self.text_summary_for_range(0..offset).lines)
+        } else {
+            Err(anyhow!("offset out of bounds"))
+        }
     }
 }
 
@@ -2080,6 +2029,7 @@ impl Clone for Buffer {
     fn clone(&self) -> Self {
         Self {
             visible_text: self.visible_text.clone(),
+            deleted_text: self.deleted_text.clone(),
             fragments: self.fragments.clone(),
             insertion_splits: self.insertion_splits.clone(),
             version: self.version.clone(),
@@ -2100,16 +2050,6 @@ impl Clone for Buffer {
     }
 }
 
-impl Snapshot {
-    pub fn fragments<'a>(&'a self) -> FragmentIter<'a> {
-        FragmentIter::new(&self.fragments)
-    }
-
-    pub fn text_summary(&self) -> TextSummary {
-        self.fragments.summary().text_summary
-    }
-}
-
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Event {
     Edited,
@@ -2123,81 +2063,6 @@ impl Entity for Buffer {
     type Event = Event;
 }
 
-impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point {
-    fn add_summary(&mut self, summary: &FragmentSummary) {
-        *self += &summary.text_summary.lines;
-    }
-}
-
-impl<'a> CharIter<'a> {
-    fn new(fragments: &'a SumTree<Fragment>, offset: usize) -> Self {
-        let mut fragments_cursor = fragments.cursor::<usize, usize>();
-        fragments_cursor.seek(&offset, SeekBias::Right, &());
-        let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| {
-            let offset_in_fragment = offset - fragments_cursor.start();
-            fragment.text[offset_in_fragment..].chars()
-        });
-        Self {
-            fragments_cursor,
-            fragment_chars,
-        }
-    }
-}
-
-impl<'a> Iterator for CharIter<'a> {
-    type Item = char;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(char) = self.fragment_chars.next() {
-            Some(char)
-        } else {
-            loop {
-                self.fragments_cursor.next();
-                if let Some(fragment) = self.fragments_cursor.item() {
-                    if fragment.visible {
-                        self.fragment_chars = fragment.text.as_str().chars();
-                        return self.fragment_chars.next();
-                    }
-                } else {
-                    return None;
-                }
-            }
-        }
-    }
-}
-
-impl<'a> FragmentIter<'a> {
-    fn new(fragments: &'a SumTree<Fragment>) -> Self {
-        let mut cursor = fragments.cursor::<usize, usize>();
-        cursor.seek(&0, SeekBias::Right, &());
-        Self {
-            cursor,
-            started: false,
-        }
-    }
-}
-
-impl<'a> Iterator for FragmentIter<'a> {
-    type Item = &'a str;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            if self.started {
-                self.cursor.next();
-            } else {
-                self.started = true;
-            }
-            if let Some(fragment) = self.cursor.item() {
-                if fragment.visible {
-                    return Some(fragment.text.as_str());
-                }
-            } else {
-                return None;
-            }
-        }
-    }
-}
-
 impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> {
     type Item = Edit;
 
@@ -2411,28 +2276,10 @@ impl Fragment {
     }
 }
 
-#[derive(Default)]
-struct FragmentContext {
-    visible_text: Rope,
-    deleted_text: Rope,
-    start: FragmentTextSummary,
-}
-
-impl FragmentContext {
-    fn new(visible_text: Rope, deleted_text: Rope, start: FragmentTextSummary) -> Self {
-        Self {
-            visible_text,
-            deleted_text,
-            start,
-        }
-    }
-}
-
 impl sum_tree::Item for Fragment {
-    type Context = FragmentContext;
     type Summary = FragmentSummary;
 
-    fn summary(&self, ctx: &FragmentContext) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         let mut max_version = time::Global::new();
         max_version.observe(self.insertion.id);
         for deletion in &self.deletions {
@@ -2441,21 +2288,19 @@ impl sum_tree::Item for Fragment {
         max_version.observe_all(&self.max_undos);
 
         if self.visible {
-            let start = ctx.start.visible.chars;
             FragmentSummary {
                 text: FragmentTextSummary {
-                    visible: TextSummary::from(ctx.visible_text.slice(start..start + self.len())),
-                    deleted: TextSummary::default(),
+                    visible: self.len(),
+                    deleted: 0,
                 },
                 max_fragment_id: self.id.clone(),
                 max_version,
             }
         } else {
-            let start = ctx.start.deleted.chars;
             FragmentSummary {
                 text: FragmentTextSummary {
-                    visible: TextSummary::default(),
-                    deleted: TextSummary::from(ctx.deleted_text.slice(start..start + self.len())),
+                    visible: 0,
+                    deleted: self.len(),
                 },
                 max_fragment_id: self.id.clone(),
                 max_version,
@@ -2486,46 +2331,16 @@ impl Default for FragmentSummary {
     }
 }
 
-impl<'a> sum_tree::Dimension<'a, FragmentSummary> for TextSummary {
-    fn add_summary(&mut self, summary: &FragmentSummary) {
-        *self += &summary.text_summary;
-    }
-}
-
-impl<'a> AddAssign<&'a FragmentExtent> for FragmentExtent {
-    fn add_assign(&mut self, other: &Self) {
-        self.chars += other.chars;
-        self.lines += &other.lines;
-    }
-}
-
-impl Default for FragmentExtent {
-    fn default() -> Self {
-        FragmentExtent {
-            lines: Point::zero(),
-            chars: 0,
-        }
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentExtent {
-    fn add_summary(&mut self, summary: &FragmentSummary) {
-        self.chars += summary.text_summary.chars;
-        self.lines += &summary.text_summary.lines;
-    }
-}
-
 impl<'a> sum_tree::Dimension<'a, FragmentSummary> for usize {
     fn add_summary(&mut self, summary: &FragmentSummary) {
-        *self += summary.text_summary.chars;
+        *self += summary.text.visible;
     }
 }
 
 impl sum_tree::Item for InsertionSplit {
-    type Context = ();
     type Summary = InsertionSplitSummary;
 
-    fn summary(&self, _: &()) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         InsertionSplitSummary {
             extent: self.extent,
         }
@@ -2591,17 +2406,12 @@ pub trait ToOffset {
 
 impl ToOffset for Point {
     fn to_offset(&self, buffer: &Buffer) -> Result<usize> {
-        let mut fragments_cursor = buffer.fragments.cursor::<Point, TextSummary>();
-        fragments_cursor.seek(self, SeekBias::Left, &());
-        fragments_cursor
-            .item()
-            .ok_or_else(|| anyhow!("point is out of range"))
-            .map(|fragment| {
-                let overshoot = fragment
-                    .offset_for_point(*self - fragments_cursor.start().lines)
-                    .unwrap();
-                fragments_cursor.start().chars + overshoot
-            })
+        if *self <= buffer.max_point() {
+            // TODO: return an error if line is shorter than column.
+            Ok(buffer.visible_text.line_to_char(self.row as usize) + self.column as usize)
+        } else {
+            Err(anyhow!("point is out of bounds"))
+        }
     }
 }
 
@@ -2635,17 +2445,13 @@ impl ToPoint for Anchor {
 
 impl ToPoint for usize {
     fn to_point(&self, buffer: &Buffer) -> Result<Point> {
-        let mut fragments_cursor = buffer.fragments.cursor::<usize, TextSummary>();
-        fragments_cursor.seek(&self, SeekBias::Left, &());
-        fragments_cursor
-            .item()
-            .ok_or_else(|| anyhow!("offset is out of range"))
-            .map(|fragment| {
-                let overshoot = fragment
-                    .point_for_offset(*self - &fragments_cursor.start().chars)
-                    .unwrap();
-                fragments_cursor.start().lines + overshoot
-            })
+        if *self <= buffer.len() {
+            let row = buffer.visible_text.char_to_line(*self);
+            let column = *self - buffer.visible_text.line_to_char(row);
+            Ok(Point::new(row as u32, column as u32))
+        } else {
+            Err(anyhow!("offset is out of bounds"))
+        }
     }
 }
 
@@ -2798,8 +2604,9 @@ mod tests {
                         let (longest_column, longest_rows) =
                             line_lengths.iter().next_back().unwrap();
                         let range_sum = buffer.text_summary_for_range(start..end);
-                        assert_eq!(range_sum.rightmost_point.column, *longest_column);
-                        assert!(longest_rows.contains(&range_sum.rightmost_point.row));
+                        // TODO: re-enable when we have rightmost point again.
+                        // assert_eq!(range_sum.rightmost_point.column, *longest_column);
+                        // assert!(longest_rows.contains(&range_sum.rightmost_point.row));
                         let range_text = &buffer.text()[start..end];
                         assert_eq!(range_sum.chars, range_text.chars().count());
                         assert_eq!(range_sum.bytes, range_text.len());
@@ -2878,26 +2685,45 @@ mod tests {
     fn test_text_summary_for_range(ctx: &mut gpui::MutableAppContext) {
         ctx.add_model(|ctx| {
             let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx);
-            let text = Text::from(buffer.text());
             assert_eq!(
                 buffer.text_summary_for_range(1..3),
-                text.slice(1..3).summary()
+                TextSummary {
+                    chars: 2,
+                    bytes: 2,
+                    lines: Point::new(1, 0)
+                }
             );
             assert_eq!(
                 buffer.text_summary_for_range(1..12),
-                text.slice(1..12).summary()
+                TextSummary {
+                    chars: 2,
+                    bytes: 2,
+                    lines: Point::new(1, 0)
+                }
             );
             assert_eq!(
                 buffer.text_summary_for_range(0..20),
-                text.slice(0..20).summary()
+                TextSummary {
+                    chars: 2,
+                    bytes: 2,
+                    lines: Point::new(1, 0)
+                }
             );
             assert_eq!(
                 buffer.text_summary_for_range(0..22),
-                text.slice(0..22).summary()
+                TextSummary {
+                    chars: 2,
+                    bytes: 2,
+                    lines: Point::new(1, 0)
+                }
             );
             assert_eq!(
                 buffer.text_summary_for_range(7..22),
-                text.slice(7..22).summary()
+                TextSummary {
+                    chars: 2,
+                    bytes: 2,
+                    lines: Point::new(1, 0)
+                }
             );
             buffer
         });

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

@@ -1,54 +0,0 @@
-use ropey::{Rope, RopeSlice};
-
-use super::Point;
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct TextSummary {
-    pub chars: usize,
-    pub bytes: usize,
-    pub lines: Point,
-}
-
-impl<'a> From<RopeSlice<'a>> for TextSummary {
-    fn from(slice: RopeSlice<'a>) -> Self {
-        let last_row = slice.len_lines() - 1;
-        let last_column = slice.line(last_row).len_chars();
-        Self {
-            chars: slice.len_chars(),
-            bytes: slice.len_bytes(),
-            lines: Point::new(last_row as u32, last_column as u32),
-        }
-    }
-}
-
-impl<'a> From<&'a Rope> for TextSummary {
-    fn from(text: &'a Rope) -> Self {
-        Self::from(text.slice(..))
-    }
-}
-
-impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
-    fn add_assign(&mut self, other: &'a Self) {
-        // let joined_line_len = self.lines.column + other.first_line_len;
-        // if joined_line_len > self.rightmost_point.column {
-        //     self.rightmost_point = Point::new(self.lines.row, joined_line_len);
-        // }
-        // if other.rightmost_point.column > self.rightmost_point.column {
-        //     self.rightmost_point = self.lines + &other.rightmost_point;
-        // }
-
-        // if self.lines.row == 0 {
-        //     self.first_line_len += other.first_line_len;
-        // }
-
-        self.chars += other.chars;
-        self.bytes += other.bytes;
-        self.lines += &other.lines;
-    }
-}
-
-impl std::ops::AddAssign<Self> for TextSummary {
-    fn add_assign(&mut self, other: Self) {
-        *self += &other;
-    }
-}

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

@@ -1,6 +1,6 @@
 use super::{
-    buffer::{self, AnchorRangeExt},
-    Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
+    buffer::{self, AnchorRangeExt, TextSummary},
+    Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset,
 };
 use crate::{
     sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
@@ -38,7 +38,6 @@ impl FoldMap {
                     display_text: None,
                 },
                 &(),
-                &(),
             )),
             last_sync: Mutex::new(buffer.version()),
         }
@@ -124,7 +123,7 @@ impl FoldMap {
             let mut cursor = self.folds.cursor::<_, ()>();
             for fold in folds {
                 new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
-                new_tree.push(fold, &(), buffer);
+                new_tree.push(fold, buffer);
             }
             new_tree.push_tree(cursor.suffix(buffer), buffer);
             new_tree
@@ -343,7 +342,6 @@ impl FoldMap {
                             display_text: None,
                         },
                         &(),
-                        &(),
                     );
                 }
 
@@ -363,7 +361,6 @@ impl FoldMap {
                             display_text: Some('…'),
                         },
                         &(),
-                        &(),
                     );
                 }
             }
@@ -381,7 +378,6 @@ impl FoldMap {
                         display_text: None,
                     },
                     &(),
-                    &(),
                 );
             }
         }
@@ -398,7 +394,6 @@ impl FoldMap {
                     display_text: None,
                 },
                 &(),
-                &(),
             );
         }
 
@@ -471,10 +466,9 @@ struct TransformSummary {
 }
 
 impl sum_tree::Item for Transform {
-    type Context = ();
     type Summary = TransformSummary;
 
-    fn summary(&self, _: &()) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         self.summary.clone()
     }
 }
@@ -504,10 +498,9 @@ impl Default for Fold {
 }
 
 impl sum_tree::Item for Fold {
-    type Context = ();
     type Summary = FoldSummary;
 
-    fn summary(&self, _: &()) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         FoldSummary {
             start: self.0.start.clone(),
             end: self.0.end.clone(),
@@ -615,7 +608,7 @@ pub struct Chars<'a> {
     cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
     offset: usize,
     buffer: &'a Buffer,
-    buffer_chars: Option<Take<buffer::CharIter<'a>>>,
+    buffer_chars: Option<Take<ropey::iter::Chars<'a>>>,
 }
 
 impl<'a> Iterator for Chars<'a> {

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

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

zed/src/operation_queue.rs 🔗

@@ -48,10 +48,9 @@ impl<T: Operation> OperationQueue<T> {
 }
 
 impl<T: Operation> Item for T {
-    type Context = ();
     type Summary = OperationSummary;
 
-    fn summary(&self, _: &()) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         OperationSummary {
             key: OperationKey(self.timestamp()),
             len: 1,

zed/src/sum_tree/mod.rs 🔗

@@ -11,13 +11,12 @@ const TREE_BASE: usize = 2;
 const TREE_BASE: usize = 6;
 
 pub trait Item: Clone + fmt::Debug {
-    type Context;
     type Summary: Summary;
 
-    fn summary(&self, ctx: &Self::Context) -> Self::Summary;
+    fn summary(&self) -> Self::Summary;
 }
 
-pub trait KeyedItem: Item<Context = ()> {
+pub trait KeyedItem: Item {
     type Key: for<'a> Dimension<'a, Self::Summary> + Ord;
 
     fn key(&self) -> Self::Key;
@@ -65,13 +64,9 @@ impl<T: Item> SumTree<T> {
         }))
     }
 
-    pub fn from_item(
-        item: T,
-        item_ctx: &T::Context,
-        summary_ctx: &<T::Summary as Summary>::Context,
-    ) -> Self {
+    pub fn from_item(item: T, ctx: &<T::Summary as Summary>::Context) -> Self {
         let mut tree = Self::new();
-        tree.push(item, item_ctx, summary_ctx);
+        tree.push(item, ctx);
         tree
     }
 
@@ -130,13 +125,47 @@ impl<T: Item> SumTree<T> {
         }
     }
 
-    pub fn push(
-        &mut self,
-        item: T,
-        item_ctx: &T::Context,
-        summary_ctx: &<T::Summary as Summary>::Context,
-    ) {
-        let summary = item.summary(item_ctx);
+    pub fn extend<I>(&mut self, iter: I, ctx: &<T::Summary as Summary>::Context)
+    where
+        I: IntoIterator<Item = T>,
+    {
+        let mut leaf: Option<Node<T>> = None;
+
+        for item in iter {
+            if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
+                self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
+            }
+
+            if leaf.is_none() {
+                leaf = Some(Node::Leaf::<T> {
+                    summary: T::Summary::default(),
+                    items: ArrayVec::new(),
+                    item_summaries: ArrayVec::new(),
+                });
+            }
+
+            if let Some(Node::Leaf {
+                summary,
+                items,
+                item_summaries,
+            }) = leaf.as_mut()
+            {
+                let item_summary = item.summary();
+                summary.add_summary(&item_summary, ctx);
+                items.push(item);
+                item_summaries.push(item_summary);
+            } else {
+                unreachable!()
+            }
+        }
+
+        if leaf.is_some() {
+            self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
+        }
+    }
+
+    pub fn push(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
+        let summary = item.summary();
         self.push_tree(
             SumTree::from_child_trees(
                 vec![SumTree(Arc::new(Node::Leaf {
@@ -144,9 +173,9 @@ impl<T: Item> SumTree<T> {
                     items: ArrayVec::from_iter(Some(item)),
                     item_summaries: ArrayVec::from_iter(Some(summary)),
                 }))],
-                summary_ctx,
+                ctx,
             ),
-            summary_ctx,
+            ctx,
         )
     }
 
@@ -320,54 +349,13 @@ impl<T: Item> SumTree<T> {
     }
 }
 
-impl<T: Item<Context = ()>> SumTree<T> {
-    pub fn extend<I>(&mut self, iter: I, ctx: &<T::Summary as Summary>::Context)
-    where
-        I: IntoIterator<Item = T>,
-    {
-        let mut leaf: Option<Node<T>> = None;
-
-        for item in iter {
-            if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
-                self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
-            }
-
-            if leaf.is_none() {
-                leaf = Some(Node::Leaf::<T> {
-                    summary: T::Summary::default(),
-                    items: ArrayVec::new(),
-                    item_summaries: ArrayVec::new(),
-                });
-            }
-
-            if let Some(Node::Leaf {
-                summary,
-                items,
-                item_summaries,
-            }) = leaf.as_mut()
-            {
-                let item_summary = item.summary(&());
-                summary.add_summary(&item_summary, ctx);
-                items.push(item);
-                item_summaries.push(item_summary);
-            } else {
-                unreachable!()
-            }
-        }
-
-        if leaf.is_some() {
-            self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
-        }
-    }
-}
-
 impl<T: KeyedItem> SumTree<T> {
     #[allow(unused)]
     pub fn insert(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
         *self = {
             let mut cursor = self.cursor::<T::Key, ()>();
             let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx);
-            new_tree.push(item, &(), ctx);
+            new_tree.push(item, ctx);
             new_tree.push_tree(cursor.suffix(ctx), ctx);
             new_tree
         };
@@ -875,10 +863,9 @@ mod tests {
     struct Sum(usize);
 
     impl Item for u8 {
-        type Context = ();
         type Summary = IntegersSummary;
 
-        fn summary(&self, _: &()) -> Self::Summary {
+        fn summary(&self) -> Self::Summary {
             IntegersSummary {
                 count: Count(1),
                 sum: Sum(*self as usize),

zed/src/worktree.rs 🔗

@@ -3,7 +3,7 @@ mod fuzzy;
 mod ignore;
 
 use crate::{
-    editor::{History, Snapshot as BufferSnapshot},
+    editor::History,
     sum_tree::{self, Cursor, Edit, SeekBias, SumTree},
 };
 use ::ignore::gitignore::Gitignore;
@@ -16,6 +16,7 @@ use postage::{
     prelude::{Sink, Stream},
     watch,
 };
+use ropey::Rope;
 use smol::channel::Sender;
 use std::{
     cmp,
@@ -198,20 +199,15 @@ impl Worktree {
         })
     }
 
-    pub fn save<'a>(
-        &self,
-        path: &Path,
-        content: BufferSnapshot,
-        ctx: &AppContext,
-    ) -> Task<Result<()>> {
+    pub fn save<'a>(&self, path: &Path, content: Rope, ctx: &AppContext) -> Task<Result<()>> {
         let handles = self.handles.clone();
         let path = path.to_path_buf();
         let abs_path = self.absolutize(&path);
         ctx.background_executor().spawn(async move {
-            let buffer_size = content.text_summary().bytes.min(10 * 1024);
+            let buffer_size = content.len_bytes().min(10 * 1024);
             let file = fs::File::create(&abs_path)?;
             let mut writer = io::BufWriter::with_capacity(buffer_size, &file);
-            for chunk in content.fragments() {
+            for chunk in content.chunks() {
                 writer.write(chunk.as_bytes())?;
             }
             writer.flush()?;
@@ -459,7 +455,7 @@ impl FileHandle {
         self.worktree.read(ctx).load_history(&self.path(), ctx)
     }
 
-    pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task<Result<()>> {
+    pub fn save<'a>(&self, content: Rope, ctx: &AppContext) -> Task<Result<()>> {
         let worktree = self.worktree.read(ctx);
         worktree.save(&self.path(), content, ctx)
     }
@@ -538,10 +534,9 @@ impl Entry {
 }
 
 impl sum_tree::Item for Entry {
-    type Context = ();
     type Summary = EntrySummary;
 
-    fn summary(&self, _: &()) -> Self::Summary {
+    fn summary(&self) -> Self::Summary {
         let file_count;
         let visible_file_count;
         if self.is_file() {