Express rightmost_point in terms of chars as opposed to bytes

Antonio Scandurra created

Change summary

zed/src/editor/buffer/mod.rs           | 91 ++++-----------------------
zed/src/editor/buffer/rope.rs          | 45 +++++++++----
zed/src/editor/display_map/fold_map.rs | 19 +++--
3 files changed, 57 insertions(+), 98 deletions(-)

Detailed changes

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

@@ -616,14 +616,6 @@ impl Buffer {
         (row_end_offset - row_start_offset) as u32
     }
 
-    pub fn rightmost_point(&self) -> Point {
-        self.visible_text.summary().rightmost_point
-    }
-
-    pub fn rightmost_point_in_range(&self, range: Range<usize>) -> Point {
-        self.text_summary_for_range(range).rightmost_point
-    }
-
     pub fn max_point(&self) -> Point {
         self.visible_text.max_point()
     }
@@ -2360,7 +2352,6 @@ mod tests {
     use std::{
         cell::RefCell,
         cmp::Ordering,
-        collections::BTreeMap,
         fs,
         rc::Rc,
         sync::atomic::{self, AtomicUsize},
@@ -2468,33 +2459,11 @@ mod tests {
                         reference_string = buffer.text();
                     }
 
-                    {
-                        let line_lengths = line_lengths_in_range(&buffer, 0..buffer.len());
-
-                        for (len, rows) in &line_lengths {
-                            for row in rows {
-                                assert_eq!(buffer.line_len(*row), *len);
-                            }
-                        }
-
-                        let (longest_column, longest_rows) =
-                            line_lengths.iter().next_back().unwrap();
-                        let rightmost_point = buffer.rightmost_point();
-                        assert_eq!(rightmost_point.column, *longest_column);
-                        assert!(longest_rows.contains(&rightmost_point.row));
-                    }
-
-                    for _ in 0..5 {
-                        let range = buffer.random_byte_range(0, rng);
-                        let line_lengths = line_lengths_in_range(&buffer, range.clone());
-                        let (longest_column, longest_rows) =
-                            line_lengths.iter().next_back().unwrap();
-                        let range_sum = buffer.text_summary_for_range(range.clone());
-                        assert_eq!(range_sum.rightmost_point.column, *longest_column);
-                        assert!(longest_rows.contains(&range_sum.rightmost_point.row));
-                        let range_text = &buffer.text()[range];
-                        assert_eq!(range_sum.bytes, range_text.len());
-                    }
+                    let range = buffer.random_byte_range(0, rng);
+                    assert_eq!(
+                        buffer.text_summary_for_range(range.clone()),
+                        TextSummary::from(&reference_string[range])
+                    );
 
                     if rng.gen_bool(0.3) {
                         buffer_versions.push(buffer.clone());
@@ -2545,25 +2514,6 @@ mod tests {
         });
     }
 
-    #[gpui::test]
-    fn test_rightmost_point(ctx: &mut gpui::MutableAppContext) {
-        ctx.add_model(|ctx| {
-            let mut buffer = Buffer::new(0, "", ctx);
-            assert_eq!(buffer.rightmost_point().row, 0);
-            buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap();
-            assert_eq!(buffer.rightmost_point().row, 0);
-            buffer.edit(vec![12..12], "kl\nmno", None).unwrap();
-            assert_eq!(buffer.rightmost_point().row, 2);
-            buffer.edit(vec![18..18], "\npqrs", None).unwrap();
-            assert_eq!(buffer.rightmost_point().row, 2);
-            buffer.edit(vec![10..12], "", None).unwrap();
-            assert_eq!(buffer.rightmost_point().row, 0);
-            buffer.edit(vec![24..24], "tuv", None).unwrap();
-            assert_eq!(buffer.rightmost_point().row, 4);
-            buffer
-        });
-    }
-
     #[gpui::test]
     fn test_text_summary_for_range(ctx: &mut gpui::MutableAppContext) {
         ctx.add_model(|ctx| {
@@ -2573,7 +2523,8 @@ mod tests {
                 TextSummary {
                     bytes: 2,
                     lines: Point::new(1, 0),
-                    first_line_len: 1,
+                    first_line_chars: 1,
+                    last_line_chars: 0,
                     rightmost_point: Point::new(0, 1),
                 }
             );
@@ -2582,7 +2533,8 @@ mod tests {
                 TextSummary {
                     bytes: 11,
                     lines: Point::new(3, 0),
-                    first_line_len: 1,
+                    first_line_chars: 1,
+                    last_line_chars: 0,
                     rightmost_point: Point::new(2, 4),
                 }
             );
@@ -2591,7 +2543,8 @@ mod tests {
                 TextSummary {
                     bytes: 20,
                     lines: Point::new(4, 1),
-                    first_line_len: 2,
+                    first_line_chars: 2,
+                    last_line_chars: 1,
                     rightmost_point: Point::new(3, 6),
                 }
             );
@@ -2600,7 +2553,8 @@ mod tests {
                 TextSummary {
                     bytes: 22,
                     lines: Point::new(4, 3),
-                    first_line_len: 2,
+                    first_line_chars: 2,
+                    last_line_chars: 3,
                     rightmost_point: Point::new(3, 6),
                 }
             );
@@ -2609,7 +2563,8 @@ mod tests {
                 TextSummary {
                     bytes: 15,
                     lines: Point::new(2, 3),
-                    first_line_len: 4,
+                    first_line_chars: 4,
+                    last_line_chars: 3,
                     rightmost_point: Point::new(1, 6),
                 }
             );
@@ -3388,20 +3343,4 @@ mod tests {
             }
         }
     }
-
-    fn line_lengths_in_range(buffer: &Buffer, range: Range<usize>) -> BTreeMap<u32, HashSet<u32>> {
-        let mut lengths = BTreeMap::new();
-        for (row, line) in buffer.text()[range.start..range.end].lines().enumerate() {
-            lengths
-                .entry(line.len() as u32)
-                .or_insert(HashSet::default())
-                .insert(row as u32);
-        }
-        if lengths.is_empty() {
-            let mut rows = HashSet::default();
-            rows.insert(0);
-            lengths.insert(0, rows);
-        }
-        lengths
-    }
 }

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

@@ -390,32 +390,41 @@ impl sum_tree::Item for Chunk {
 pub struct TextSummary {
     pub bytes: usize,
     pub lines: Point,
-    pub first_line_len: u32,
+    pub first_line_chars: u32,
+    pub last_line_chars: u32,
     pub rightmost_point: Point,
 }
 
 impl<'a> From<&'a str> for TextSummary {
     fn from(text: &'a str) -> Self {
         let mut lines = Point::new(0, 0);
-        let mut first_line_len = 0;
+        let mut first_line_chars = 0;
+        let mut last_line_chars = 0;
         let mut rightmost_point = Point::new(0, 0);
-        for (i, line) in text.split('\n').enumerate() {
-            if i > 0 {
+        for c in text.chars() {
+            if c == '\n' {
                 lines.row += 1;
+                lines.column = 0;
+                last_line_chars = 0;
+            } else {
+                lines.column += c.len_utf8() as u32;
+                last_line_chars += 1;
             }
-            lines.column = line.len() as u32;
-            if i == 0 {
-                first_line_len = lines.column;
+
+            if lines.row == 0 {
+                first_line_chars = last_line_chars;
             }
-            if lines.column > rightmost_point.column {
-                rightmost_point = lines;
+
+            if last_line_chars > rightmost_point.column {
+                rightmost_point = Point::new(lines.row, last_line_chars);
             }
         }
 
         TextSummary {
             bytes: text.len(),
             lines,
-            first_line_len,
+            first_line_chars,
+            last_line_chars,
             rightmost_point,
         }
     }
@@ -431,16 +440,22 @@ impl sum_tree::Summary for TextSummary {
 
 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);
+        let joined_chars = self.last_line_chars + other.first_line_chars;
+        if joined_chars > self.rightmost_point.column {
+            self.rightmost_point = Point::new(self.lines.row, joined_chars);
         }
         if other.rightmost_point.column > self.rightmost_point.column {
-            self.rightmost_point = self.lines + &other.rightmost_point;
+            self.rightmost_point = self.lines + other.rightmost_point;
         }
 
         if self.lines.row == 0 {
-            self.first_line_len += other.first_line_len;
+            self.first_line_chars += other.first_line_chars;
+        }
+
+        if other.lines.row == 0 {
+            self.last_line_chars += other.first_line_chars;
+        } else {
+            self.last_line_chars = other.last_line_chars;
         }
 
         self.bytes += other.bytes;

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

@@ -330,15 +330,17 @@ impl FoldMap {
 
                 if fold.end > fold.start {
                     let display_text = "…";
-                    let extent = Point::new(0, display_text.len() as u32);
+                    let chars = display_text.chars().count() as u32;
+                    let lines = Point::new(0, display_text.len() as u32);
                     new_transforms.push(
                         Transform {
                             summary: TransformSummary {
                                 display: TextSummary {
                                     bytes: display_text.len(),
-                                    lines: extent,
-                                    first_line_len: extent.column,
-                                    rightmost_point: extent,
+                                    lines,
+                                    first_line_chars: chars,
+                                    last_line_chars: chars,
+                                    rightmost_point: Point::new(0, chars),
                                 },
                                 buffer: buffer.text_summary_for_range(fold.start..fold.end),
                             },
@@ -990,6 +992,7 @@ mod tests {
                 let rightmost_point = map.rightmost_point(app.as_ref());
                 let mut display_point = DisplayPoint::new(0, 0);
                 let mut display_offset = DisplayOffset(0);
+                let mut char_column = 0;
                 for c in expected_text.chars() {
                     let buffer_point = map.to_buffer_point(display_point, app.as_ref());
                     let buffer_offset = buffer_point.to_offset(buffer);
@@ -1015,14 +1018,16 @@ mod tests {
                     if c == '\n' {
                         *display_point.row_mut() += 1;
                         *display_point.column_mut() = 0;
+                        char_column = 0;
                     } else {
                         *display_point.column_mut() += c.len_utf8() as u32;
+                        char_column += 1;
                     }
                     display_offset.0 += c.len_utf8();
-                    if display_point.column() > rightmost_point.column() {
+                    if char_column > rightmost_point.column() {
                         panic!(
-                            "invalid rightmost point {:?}, found point {:?}",
-                            rightmost_point, display_point
+                            "invalid rightmost point {:?}, found point {:?} (char column: {})",
+                            rightmost_point, display_point, char_column
                         );
                     }
                 }