on master help sign in
transaction: Use builder methods to generate compact changesets.
b4312c9Blaž Hrastnik
helix-core/src/syntax.rs+29−29
@@ -451,7 +451,7 @@ impl LanguageLayer {
451 Delete(i) | Retain(i) => *i,
451 Delete(i) | Retain(i) => *i,
452 Insert(_) => 0,
452 Insert(_) => 0,
453 };
453 };
454− let old_end = old_pos + len;
454+ let mut old_end = old_pos + len;
455
455
456 match change {
456 match change {
457 Retain(_) => {
457 Retain(_) => {
@@ -467,10 +467,27 @@ impl LanguageLayer {
467 // let line_start_byte = line_to_byte()
467 // let line_start_byte = line_to_byte()
468 // Position::new(line, line_start_byte - byte)
468 // Position::new(line, line_start_byte - byte)
469
469
470+ // deletion
471+ edits.push(tree_sitter::InputEdit {
472+ start_byte, // old_pos to byte
473+ old_end_byte, // old_end to byte
474+ new_end_byte: start_byte, // old_pos to byte
475+ start_position, // old pos to coords
476+ old_end_position, // old_end to coords
477+ new_end_position: start_position, // old pos to coords
478+ });
479+ }
480+ Insert(s) => {
481+ let (start_byte, start_position) = point_at_pos(&old_text, old_pos);
482+
483+ let ins = s.chars().count();
484+
470− // a subsequent ins means a replace, consume it
485+ // a subsequent delete means a replace, consume it
471− if let Some(Insert(s)) = iter.peek() {
486+ if let Some(Delete(len)) = iter.peek() {
487+ old_end = old_pos + len;
488+ let (old_end_byte, old_end_position) = point_at_pos(&old_text, old_end);
489+
472 iter.next();
490 iter.next();
473− let ins = s.chars().count();
474
491
475 // replacement
492 // replacement
476 edits.push(tree_sitter::InputEdit {
493 edits.push(tree_sitter::InputEdit {
@@ -481,34 +498,17 @@ impl LanguageLayer {
481 old_end_position, // old_end to coords
498 old_end_position, // old_end to coords
482 new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
499 new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
483 });
500 });
484−
485− new_pos += ins;
486 } else {
501 } else {
487− // deletion
502+ // insert
488 edits.push(tree_sitter::InputEdit {
503 edits.push(tree_sitter::InputEdit {
489− start_byte, // old_pos to byte
504+ start_byte, // old_pos to byte
490− old_end_byte, // old_end to byte
505+ old_end_byte: start_byte, // same
491− new_end_byte: start_byte, // old_pos to byte
506+ new_end_byte: start_byte + s.len(), // old_pos + s.len()
492− start_position, // old pos to coords
507+ start_position, // old pos to coords
493− old_end_position, // old_end to coords
508+ old_end_position: start_position, // same
494− new_end_position: start_position, // old pos to coords
509+ new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
495 });
510 });
496− };
511+ }
497− }
498− Insert(s) => {
499− let (start_byte, start_position) = point_at_pos(&old_text, old_pos);
500−
501− let ins = s.chars().count();
502−
503− // insert
504− edits.push(tree_sitter::InputEdit {
505− start_byte, // old_pos to byte
506− old_end_byte: start_byte, // same
507− new_end_byte: start_byte + s.len(), // old_pos + s.len()
508− start_position, // old pos to coords
509− old_end_position: start_position, // same
510− new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
511− });
512
512
513 new_pos += ins;
513 new_pos += ins;
514 }
514 }
helix-core/src/transaction.rs+77−40
@@ -63,6 +63,56 @@ impl ChangeSet {
63 len
63 len
64 }
64 }
65
65
66+ // Changeset builder operations: delete/insert/retain
67+ fn delete(&mut self, n: usize) {
68+ use Operation::*;
69+ if n == 0 {
70+ return;
71+ }
72+
73+ if let Some(Delete(count)) = self.changes.last_mut() {
74+ *count += n;
75+ } else {
76+ self.changes.push(Delete(n));
77+ }
78+ }
79+
80+ fn insert(&mut self, fragment: Tendril) {
81+ use Operation::*;
82+
83+ if fragment.is_empty() {
84+ return;
85+ }
86+
87+ let new_last = match self.changes.as_mut_slice() {
88+ [.., Insert(prev)] => {
89+ prev.push_tendril(&fragment);
90+ return;
91+ }
92+ [.., Insert(prev), Delete(_)] => {
93+ prev.push_tendril(&fragment);
94+ return;
95+ }
96+ [.., last @ Delete(_)] => std::mem::replace(last, Insert(fragment)),
97+ _ => Insert(fragment),
98+ };
99+
100+ self.changes.push(new_last);
101+ }
102+
103+ fn retain(&mut self, n: usize) {
104+ use Operation::*;
105+ if n == 0 {
106+ return;
107+ }
108+
109+ if let Some(Retain(count)) = self.changes.last_mut() {
110+ *count += n;
111+ } else {
112+ self.changes.push(Retain(n));
113+ }
114+ }
115+
66 /// Combine two changesets together.
116 /// Combine two changesets together.
67 /// In other words, If `this` goes `docA` → `docB` and `other` represents `docB` → `docC`, the
117 /// In other words, If `this` goes `docA` → `docB` and `other` represents `docB` → `docC`, the
68 /// returned value will represent the change `docA` → `docC`.
118 /// returned value will represent the change `docA` → `docC`.
@@ -77,7 +127,10 @@ impl ChangeSet {
77 let mut head_a = changes_a.next();
127 let mut head_a = changes_a.next();
78 let mut head_b = changes_b.next();
128 let mut head_b = changes_b.next();
79
129
130+ let mut changes = Self {
131+ len: self.len,
80− let mut changes: Vec<Operation> = Vec::with_capacity(len); // TODO: max(a, b), shrink_to_fit() afterwards
132+ changes: Vec::with_capacity(len), // TODO: max(a, b), shrink_to_fit() afterwards
133+ };
81
134
82 loop {
135 loop {
83 use std::cmp::Ordering;
136 use std::cmp::Ordering;
@@ -88,37 +141,31 @@ impl ChangeSet {
88 break;
141 break;
89 }
142 }
90 // deletion in A
143 // deletion in A
91− (Some(change @ Delete(..)), b) => {
144+ (Some(Delete(i)), b) => {
92− changes.push(change);
145+ changes.delete(i);
93 head_a = changes_a.next();
146 head_a = changes_a.next();
94 head_b = b;
147 head_b = b;
95 }
148 }
96 // insertion in B
149 // insertion in B
97 (a, Some(Insert(current))) => {
150 (a, Some(Insert(current))) => {
98− // merge onto previous insert if possible
151+ changes.insert(current);
99− // TODO: do these as operations on a changeset
100− if let Some(Insert(prev)) = changes.last_mut() {
101− prev.push_tendril(&current);
102− } else {
103− changes.push(Insert(current));
104− }
105 head_a = a;
152 head_a = a;
106 head_b = changes_b.next();
153 head_b = changes_b.next();
107 }
154 }
108 (None, _) | (_, None) => return unreachable!(),
155 (None, _) | (_, None) => return unreachable!(),
109 (Some(Retain(i)), Some(Retain(j))) => match i.cmp(&j) {
156 (Some(Retain(i)), Some(Retain(j))) => match i.cmp(&j) {
110 Ordering::Less => {
157 Ordering::Less => {
111− changes.push(Retain(i));
158+ changes.retain(i);
112 head_a = changes_a.next();
159 head_a = changes_a.next();
113 head_b = Some(Retain(j - i));
160 head_b = Some(Retain(j - i));
114 }
161 }
115 Ordering::Equal => {
162 Ordering::Equal => {
116− changes.push(Retain(i));
163+ changes.retain(i);
117 head_a = changes_a.next();
164 head_a = changes_a.next();
118 head_b = changes_b.next();
165 head_b = changes_b.next();
119 }
166 }
120 Ordering::Greater => {
167 Ordering::Greater => {
121− changes.push(Retain(j));
168+ changes.retain(j);
122 head_a = Some(Retain(i - j));
169 head_a = Some(Retain(i - j));
123 head_b = changes_b.next();
170 head_b = changes_b.next();
124 }
171 }
@@ -147,12 +194,12 @@ impl ChangeSet {
147 let len = s.chars().count();
194 let len = s.chars().count();
148 match len.cmp(&j) {
195 match len.cmp(&j) {
149 Ordering::Less => {
196 Ordering::Less => {
150− changes.push(Insert(s));
197+ changes.insert(s);
151 head_a = changes_a.next();
198 head_a = changes_a.next();
152 head_b = Some(Retain(j - len));
199 head_b = Some(Retain(j - len));
153 }
200 }
154 Ordering::Equal => {
201 Ordering::Equal => {
155− changes.push(Insert(s));
202+ changes.insert(s);
156 head_a = changes_a.next();
203 head_a = changes_a.next();
157 head_b = changes_b.next();
204 head_b = changes_b.next();
158 }
205 }
@@ -160,7 +207,7 @@ impl ChangeSet {
160 // figure out the byte index of the truncated string end
207 // figure out the byte index of the truncated string end
161 let (pos, _) = s.char_indices().nth(j).unwrap();
208 let (pos, _) = s.char_indices().nth(j).unwrap();
162 let pos = pos as u32;
209 let pos = pos as u32;
163− changes.push(Insert(s.subtendril(0, pos)));
210+ changes.insert(s.subtendril(0, pos));
164 head_a = Some(Insert(s.subtendril(pos, s.len() as u32 - pos)));
211 head_a = Some(Insert(s.subtendril(pos, s.len() as u32 - pos)));
165 head_b = changes_b.next();
212 head_b = changes_b.next();
166 }
213 }
@@ -168,17 +215,17 @@ impl ChangeSet {
168 }
215 }
169 (Some(Retain(i)), Some(Delete(j))) => match i.cmp(&j) {
216 (Some(Retain(i)), Some(Delete(j))) => match i.cmp(&j) {
170 Ordering::Less => {
217 Ordering::Less => {
171− changes.push(Delete(i));
218+ changes.delete(i);
172 head_a = changes_a.next();
219 head_a = changes_a.next();
173 head_b = Some(Delete(j - i));
220 head_b = Some(Delete(j - i));
174 }
221 }
175 Ordering::Equal => {
222 Ordering::Equal => {
176− changes.push(Delete(j));
223+ changes.delete(j);
177 head_a = changes_a.next();
224 head_a = changes_a.next();
178 head_b = changes_b.next();
225 head_b = changes_b.next();
179 }
226 }
180 Ordering::Greater => {
227 Ordering::Greater => {
181− changes.push(Delete(j));
228+ changes.delete(j);
182 head_a = Some(Retain(i - j));
229 head_a = Some(Retain(i - j));
183 head_b = changes_b.next();
230 head_b = changes_b.next();
184 }
231 }
@@ -186,10 +233,7 @@ impl ChangeSet {
186 };
233 };
187 }
234 }
188
235
189− Self {
190− len: self.len,
191− changes,
236+ changes
192− }
193 }
237 }
194
238
195 /// Given another change set starting in the same document, maps this
239 /// Given another change set starting in the same document, maps this
@@ -421,37 +465,29 @@ impl Transaction {
421 I: IntoIterator<Item = Change> + ExactSizeIterator,
465 I: IntoIterator<Item = Change> + ExactSizeIterator,
422 {
466 {
423 let len = state.doc.len_chars();
467 let len = state.doc.len_chars();
424− let mut acc = Vec::with_capacity(2 * changes.len() + 1);
468+ let acc = Vec::with_capacity(2 * changes.len() + 1);
469+ let mut changeset = ChangeSet { changes: acc, len };
425
470
426 // TODO: verify ranges are ordered and not overlapping or change will panic.
471 // TODO: verify ranges are ordered and not overlapping or change will panic.
427
472
428 let mut last = 0;
473 let mut last = 0;
429 for (from, to, tendril) in changes {
474 for (from, to, tendril) in changes {
430 // Retain from last "to" to current "from"
475 // Retain from last "to" to current "from"
431− if from - last > 0 {
476+ changeset.retain(from - last);
432− acc.push(Operation::Retain(from - last));
433− }
434 let span = to - from;
477 let span = to - from;
435 match tendril {
478 match tendril {
436 Some(text) => {
479 Some(text) => {
437− if span > 0 {
480+ changeset.delete(span);
438− acc.push(Operation::Delete(span));
481+ changeset.insert(text);
439− }
440− acc.push(Operation::Insert(text));
441 }
482 }
442− None if span > 0 => acc.push(Operation::Delete(span)),
483+ None => changeset.delete(span),
443− // empty delete is useless
444− None => (),
445 }
484 }
446 last = to;
485 last = to;
447 }
486 }
448
487
449− let span = len - last;
488+ changeset.retain(len - last);
450− if span > 0 {
451− acc.push(Operation::Retain(span));
452− }
453
489
454− Self::from(ChangeSet { changes: acc, len })
490+ Self::from(changeset)
455 }
491 }
456
492
457 /// Generate a transaction with a change per selection range.
493 /// Generate a transaction with a change per selection range.
@@ -591,7 +627,7 @@ mod test {
591 }
627 }
592
628
593 #[test]
629 #[test]
594− fn insert_composition() {
630+ fn optimized_composition() {
595 let mut state = State::new("".into());
631 let mut state = State::new("".into());
596 let t1 = Transaction::insert(&state, Tendril::from_char('h'));
632 let t1 = Transaction::insert(&state, Tendril::from_char('h'));
597 t1.apply(&mut state);
633 t1.apply(&mut state);
@@ -620,5 +656,6 @@ mod test {
620
656
621 use Operation::*;
657 use Operation::*;
622 assert_eq!(changes.changes, &[Insert("hello".into())]);
658 assert_eq!(changes.changes, &[Insert("hello".into())]);
659+ // instead of insert h, insert e, insert l, insert l, insert o
623 }
660 }
624}
661}