about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-25 13:16:43 +0000
committerbors <bors@rust-lang.org>2023-03-25 13:16:43 +0000
commit68aa1331087e51857e91be2a3dc38a5b7ac49bad (patch)
treeddbab09ed7ffd030084c27e01d224b6a8daec143
parenteb791f31e688ae00908eb75d4c704ef60c430a92 (diff)
parent28225cc33d60615aaa140dc4b0d32e2a1c21462c (diff)
downloadrust-68aa1331087e51857e91be2a3dc38a5b7ac49bad.tar.gz
rust-68aa1331087e51857e91be2a3dc38a5b7ac49bad.zip
Auto merge of #14380 - DropDemBits:coalesce-indels, r=Veykril
internal: Coalesce adjacent Indels

Originally part of working on a structured snippet API (since sometimes the `$` bit of snippets would be broken off and would lead to it not being recognized), though since this is a pretty separate change, I thought it would make sense to put it into it's own PR.

The implementation is relatively straight forward and not overly optimized, though it's pretty low hanging fruit to optimize it when need be.
-rw-r--r--crates/text-edit/src/lib.rs52
1 files changed, 52 insertions, 0 deletions
diff --git a/crates/text-edit/src/lib.rs b/crates/text-edit/src/lib.rs
index 9bb4271b65f..4705d18187a 100644
--- a/crates/text-edit/src/lib.rs
+++ b/crates/text-edit/src/lib.rs
@@ -176,6 +176,7 @@ impl TextEditBuilder {
     pub fn finish(self) -> TextEdit {
         let mut indels = self.indels;
         assert_disjoint_or_equal(&mut indels);
+        indels = coalesce_indels(indels);
         TextEdit { indels }
     }
     pub fn invalidates_offset(&self, offset: TextSize) -> bool {
@@ -205,6 +206,21 @@ where
     indels.clone().zip(indels.skip(1)).all(|(l, r)| l.delete.end() <= r.delete.start() || l == r)
 }
 
+fn coalesce_indels(indels: Vec<Indel>) -> Vec<Indel> {
+    indels
+        .into_iter()
+        .coalesce(|mut a, b| {
+            if a.delete.end() == b.delete.start() {
+                a.insert.push_str(&b.insert);
+                a.delete = TextRange::new(a.delete.start(), b.delete.end());
+                Ok(a)
+            } else {
+                Err((a, b))
+            }
+        })
+        .collect_vec()
+}
+
 #[cfg(test)]
 mod tests {
     use super::{TextEdit, TextEditBuilder, TextRange};
@@ -261,4 +277,40 @@ mod tests {
         let edit2 = TextEdit::delete(range(9, 13));
         assert!(edit1.union(edit2).is_err());
     }
+
+    #[test]
+    fn test_coalesce_disjoint() {
+        let mut builder = TextEditBuilder::default();
+        builder.replace(range(1, 3), "aa".into());
+        builder.replace(range(5, 7), "bb".into());
+        let edit = builder.finish();
+
+        assert_eq!(edit.indels.len(), 2);
+    }
+
+    #[test]
+    fn test_coalesce_adjacent() {
+        let mut builder = TextEditBuilder::default();
+        builder.replace(range(1, 3), "aa".into());
+        builder.replace(range(3, 5), "bb".into());
+
+        let edit = builder.finish();
+        assert_eq!(edit.indels.len(), 1);
+        assert_eq!(edit.indels[0].insert, "aabb");
+        assert_eq!(edit.indels[0].delete, range(1, 5));
+    }
+
+    #[test]
+    fn test_coalesce_adjacent_series() {
+        let mut builder = TextEditBuilder::default();
+        builder.replace(range(1, 3), "au".into());
+        builder.replace(range(3, 5), "www".into());
+        builder.replace(range(5, 8), "".into());
+        builder.replace(range(8, 9), "ub".into());
+
+        let edit = builder.finish();
+        assert_eq!(edit.indels.len(), 1);
+        assert_eq!(edit.indels[0].insert, "auwwwub");
+        assert_eq!(edit.indels[0].delete, range(1, 9));
+    }
 }