Coalesce contiguous ranges when editing locally

Antonio Scandurra created

Change summary

zed/src/editor/buffer.rs | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)

Detailed changes

zed/src/editor/buffer.rs 🔗

@@ -902,7 +902,7 @@ impl Buffer {
 
     pub fn edit<I, S, T>(
         &mut self,
-        ranges: I,
+        ranges_iter: I,
         new_text: T,
         cx: Option<&mut ModelContext<Self>>,
     ) -> Option<Operation>
@@ -918,17 +918,23 @@ impl Buffer {
             None
         };
         let has_new_text = new_text.is_some();
-        let ranges = ranges
-            .into_iter()
-            .filter_map(|range| {
-                let range = range.start.to_offset(self)..range.end.to_offset(self);
-                if has_new_text || !range.is_empty() {
-                    Some(range)
+
+        // 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 {
-                    None
+                    ranges.push(range);
                 }
-            })
-            .collect::<Vec<Range<usize>>>();
+            }
+        }
 
         if ranges.is_empty() {
             None
@@ -3344,7 +3350,7 @@ mod tests {
     impl Buffer {
         fn random_byte_range(&mut self, start_offset: usize, rng: &mut impl Rng) -> Range<usize> {
             let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
-            let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Left);
+            let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
             start..end
         }