aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_text_edit
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_text_edit')
-rw-r--r--crates/ra_text_edit/src/lib.rs45
1 files changed, 33 insertions, 12 deletions
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index c4f945101..25554f583 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -3,6 +3,7 @@
3//! `rust-analyzer` never mutates text itself and only sends diffs to clients, 3//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
4//! so `TextEdit` is the ultimate representation of the work done by 4//! so `TextEdit` is the ultimate representation of the work done by
5//! rust-analyzer. 5//! rust-analyzer.
6use std::{slice, vec};
6 7
7pub use text_size::{TextRange, TextSize}; 8pub use text_size::{TextRange, TextSize};
8 9
@@ -16,7 +17,7 @@ pub struct Indel {
16 pub delete: TextRange, 17 pub delete: TextRange,
17} 18}
18 19
19#[derive(Debug, Clone)] 20#[derive(Default, Debug, Clone)]
20pub struct TextEdit { 21pub struct TextEdit {
21 indels: Vec<Indel>, 22 indels: Vec<Indel>,
22} 23}
@@ -63,25 +64,24 @@ impl TextEdit {
63 builder.finish() 64 builder.finish()
64 } 65 }
65 66
66 pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit { 67 pub fn len(&self) -> usize {
67 indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); 68 self.indels.len()
68 for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) {
69 assert!(a1.delete.end() <= a2.delete.start())
70 }
71 TextEdit { indels }
72 } 69 }
73 70
74 pub fn is_empty(&self) -> bool { 71 pub fn is_empty(&self) -> bool {
75 self.indels.is_empty() 72 self.indels.is_empty()
76 } 73 }
77 74
78 // FXME: impl IntoIter instead 75 pub fn iter(&self) -> slice::Iter<'_, Indel> {
79 pub fn as_indels(&self) -> &[Indel] { 76 self.indels.iter()
80 &self.indels 77 }
78
79 pub fn into_iter(self) -> vec::IntoIter<Indel> {
80 self.indels.into_iter()
81 } 81 }
82 82
83 pub fn apply(&self, text: &mut String) { 83 pub fn apply(&self, text: &mut String) {
84 match self.indels.len() { 84 match self.len() {
85 0 => return, 85 0 => return,
86 1 => { 86 1 => {
87 self.indels[0].apply(text); 87 self.indels[0].apply(text);
@@ -114,6 +114,17 @@ impl TextEdit {
114 *text = buf 114 *text = buf
115 } 115 }
116 116
117 pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
118 // FIXME: can be done without allocating intermediate vector
119 let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
120 if !check_disjoint(&mut all) {
121 return Err(other);
122 }
123 self.indels.extend(other.indels);
124 assert!(check_disjoint(&mut self.indels));
125 Ok(())
126 }
127
117 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { 128 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
118 let mut res = offset; 129 let mut res = offset;
119 for indel in self.indels.iter() { 130 for indel in self.indels.iter() {
@@ -141,9 +152,19 @@ impl TextEditBuilder {
141 self.indels.push(Indel::insert(offset, text)) 152 self.indels.push(Indel::insert(offset, text))
142 } 153 }
143 pub fn finish(self) -> TextEdit { 154 pub fn finish(self) -> TextEdit {
144 TextEdit::from_indels(self.indels) 155 let mut indels = self.indels;
156 assert!(check_disjoint(&mut indels));
157 TextEdit { indels }
145 } 158 }
146 pub fn invalidates_offset(&self, offset: TextSize) -> bool { 159 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
147 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) 160 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
148 } 161 }
149} 162}
163
164fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
165 indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
166 indels
167 .iter()
168 .zip(indels.iter().skip(1))
169 .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start())
170}