Don't underflow Rope chunks on append

Antonio Scandurra created

Change summary

zed/src/editor/buffer/rope.rs | 40 +++++++++++++++++++++++++++++++++---
1 file changed, 36 insertions(+), 4 deletions(-)

Detailed changes

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

@@ -7,7 +7,7 @@ use std::{cmp, ops::Range, str};
 const CHUNK_BASE: usize = 2;
 
 #[cfg(not(test))]
-const CHUNK_BASE: usize = 8;
+const CHUNK_BASE: usize = 16;
 
 #[derive(Clone, Default, Debug)]
 pub struct Rope {
@@ -20,7 +20,20 @@ impl Rope {
     }
 
     pub fn append(&mut self, rope: Rope) {
-        self.chunks.push_tree(rope.chunks, &());
+        let mut chunks = rope.chunks.cursor::<(), ()>();
+        chunks.next();
+
+        while let Some((last_chunk, first_chunk)) = self.chunks.last().zip(chunks.item()) {
+            if last_chunk.0.len() < CHUNK_BASE || first_chunk.0.len() < CHUNK_BASE {
+                self.push(&first_chunk.0);
+                chunks.next();
+            } else {
+                break;
+            }
+        }
+
+        self.chunks.push_tree(chunks.suffix(&()), &());
+        self.check_invariants();
     }
 
     pub fn push(&mut self, mut text: &str) {
@@ -48,7 +61,7 @@ impl Rope {
 
                         let split = chunk.0.split_at(take_len);
                         suffix.push_str(split.1);
-                        chunk.0 = ArrayString::from(split.0).unwrap();
+                        chunk.0.truncate(take_len);
                     }
                 }
             },
@@ -68,6 +81,25 @@ impl Rope {
             chunks.push(Chunk(chunk));
         }
         self.chunks.extend(chunks, &());
+        self.check_invariants();
+    }
+
+    fn check_invariants(&self) {
+        #[cfg(test)]
+        {
+            let mut chunks = self.chunks.cursor::<(), ()>().peekable();
+            chunks.next();
+            while let Some(chunk) = chunks.next() {
+                if chunks.peek().is_some() {
+                    assert!(
+                        chunk.0.len() >= CHUNK_BASE,
+                        "Underflowing chunk: {:?}\nChunks: {:?}",
+                        chunk,
+                        self.chunks.items()
+                    );
+                }
+            }
+        }
     }
 
     pub fn slice(&self, range: Range<usize>) -> Rope {
@@ -90,7 +122,7 @@ impl Rope {
                 slice.push(&end_chunk.0[..range.end - cursor.start()]);
             }
         }
-
+        slice.check_invariants();
         slice
     }