on master help sign in
transaction: Use builder methods to generate compact changesets.
commitb4312c9492445a6a3e3baabebbc4451f89b8bd0e authorBlaž Hrastnik <blaz@mxxn.io> dateTue 16 Feb 2021, 11:03 (6 years ago) parent19fb4ed
2 files changed+106−69
helix-core/src/syntax.rs+29−29 helix-core/src/transaction.rs+77−40
helix-core/src/syntax.rs+29−29
@@ -451,7 +451,7 @@ impl LanguageLayer {
451451 Delete(i) | Retain(i) => *i,
452452 Insert(_) => 0,
453453 };
454− let old_end = old_pos + len;
454+ let mut old_end = old_pos + len;
455455
456456 match change {
457457 Retain(_) => {
@@ -467,10 +467,27 @@ impl LanguageLayer {
467467 // let line_start_byte = line_to_byte()
468468 // Position::new(line, line_start_byte - byte)
469469
470− // a subsequent ins means a replace, consume it
471− if let Some(Insert(s)) = iter.peek() {
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+
485+ // a subsequent delete means a replace, consume it
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+
472490 iter.next();
473− let ins = s.chars().count();
474491
475492 // replacement
476493 edits.push(tree_sitter::InputEdit {
@@ -481,34 +498,17 @@ impl LanguageLayer {
481498 old_end_position, // old_end to coords
482499 new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
483500 });
484−
485− new_pos += ins;
486501 } else {
487− // deletion
502+ // insert
488503 edits.push(tree_sitter::InputEdit {
489− start_byte, // old_pos to byte
490− old_end_byte, // old_end to byte
491− new_end_byte: start_byte, // old_pos to byte
492− start_position, // old pos to coords
493− old_end_position, // old_end to coords
494− new_end_position: start_position, // old pos to coords
504+ start_byte, // old_pos to byte
505+ old_end_byte: start_byte, // same
506+ new_end_byte: start_byte + s.len(), // old_pos + s.len()
507+ start_position, // old pos to coords
508+ old_end_position: start_position, // same
509+ new_end_position: traverse(start_position, s), // old pos + chars, newlines matter too (iter over)
495510 });
496− };
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− });
511+ }
512512
513513 new_pos += ins;
514514 }
helix-core/src/transaction.rs+77−40
@@ -63,6 +63,56 @@ impl ChangeSet {
6363 len
6464 }
6565
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+
66116 /// Combine two changesets together.
67117 /// In other words, If `this` goes `docA` → `docB` and `other` represents `docB` → `docC`, the
68118 /// returned value will represent the change `docA` → `docC`.
@@ -77,7 +127,10 @@ impl ChangeSet {
77127 let mut head_a = changes_a.next();
78128 let mut head_b = changes_b.next();
79129
80− let mut changes: Vec<Operation> = Vec::with_capacity(len); // TODO: max(a, b), shrink_to_fit() afterwards
130+ let mut changes = Self {
131+ len: self.len,
132+ changes: Vec::with_capacity(len), // TODO: max(a, b), shrink_to_fit() afterwards
133+ };
81134
82135 loop {
83136 use std::cmp::Ordering;
@@ -88,37 +141,31 @@ impl ChangeSet {
88141 break;
89142 }
90143 // deletion in A
91− (Some(change @ Delete(..)), b) => {
92− changes.push(change);
144+ (Some(Delete(i)), b) => {
145+ changes.delete(i);
93146 head_a = changes_a.next();
94147 head_b = b;
95148 }
96149 // insertion in B
97150 (a, Some(Insert(current))) => {
98− // merge onto previous insert if possible
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− }
151+ changes.insert(current);
105152 head_a = a;
106153 head_b = changes_b.next();
107154 }
108155 (None, _) | (_, None) => return unreachable!(),
109156 (Some(Retain(i)), Some(Retain(j))) => match i.cmp(&j) {
110157 Ordering::Less => {
111− changes.push(Retain(i));
158+ changes.retain(i);
112159 head_a = changes_a.next();
113160 head_b = Some(Retain(j - i));
114161 }
115162 Ordering::Equal => {
116− changes.push(Retain(i));
163+ changes.retain(i);
117164 head_a = changes_a.next();
118165 head_b = changes_b.next();
119166 }
120167 Ordering::Greater => {
121− changes.push(Retain(j));
168+ changes.retain(j);
122169 head_a = Some(Retain(i - j));
123170 head_b = changes_b.next();
124171 }
@@ -147,12 +194,12 @@ impl ChangeSet {
147194 let len = s.chars().count();
148195 match len.cmp(&j) {
149196 Ordering::Less => {
150− changes.push(Insert(s));
197+ changes.insert(s);
151198 head_a = changes_a.next();
152199 head_b = Some(Retain(j - len));
153200 }
154201 Ordering::Equal => {
155− changes.push(Insert(s));
202+ changes.insert(s);
156203 head_a = changes_a.next();
157204 head_b = changes_b.next();
158205 }
@@ -160,7 +207,7 @@ impl ChangeSet {
160207 // figure out the byte index of the truncated string end
161208 let (pos, _) = s.char_indices().nth(j).unwrap();
162209 let pos = pos as u32;
163− changes.push(Insert(s.subtendril(0, pos)));
210+ changes.insert(s.subtendril(0, pos));
164211 head_a = Some(Insert(s.subtendril(pos, s.len() as u32 - pos)));
165212 head_b = changes_b.next();
166213 }
@@ -168,17 +215,17 @@ impl ChangeSet {
168215 }
169216 (Some(Retain(i)), Some(Delete(j))) => match i.cmp(&j) {
170217 Ordering::Less => {
171− changes.push(Delete(i));
218+ changes.delete(i);
172219 head_a = changes_a.next();
173220 head_b = Some(Delete(j - i));
174221 }
175222 Ordering::Equal => {
176− changes.push(Delete(j));
223+ changes.delete(j);
177224 head_a = changes_a.next();
178225 head_b = changes_b.next();
179226 }
180227 Ordering::Greater => {
181− changes.push(Delete(j));
228+ changes.delete(j);
182229 head_a = Some(Retain(i - j));
183230 head_b = changes_b.next();
184231 }
@@ -186,10 +233,7 @@ impl ChangeSet {
186233 };
187234 }
188235
189− Self {
190− len: self.len,
191− changes,
192− }
236+ changes
193237 }
194238
195239 /// Given another change set starting in the same document, maps this
@@ -421,37 +465,29 @@ impl Transaction {
421465 I: IntoIterator<Item = Change> + ExactSizeIterator,
422466 {
423467 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 };
425470
426471 // TODO: verify ranges are ordered and not overlapping or change will panic.
427472
428473 let mut last = 0;
429474 for (from, to, tendril) in changes {
430475 // Retain from last "to" to current "from"
431− if from - last > 0 {
432− acc.push(Operation::Retain(from - last));
433− }
476+ changeset.retain(from - last);
434477 let span = to - from;
435478 match tendril {
436479 Some(text) => {
437− if span > 0 {
438− acc.push(Operation::Delete(span));
439− }
440− acc.push(Operation::Insert(text));
480+ changeset.delete(span);
481+ changeset.insert(text);
441482 }
442− None if span > 0 => acc.push(Operation::Delete(span)),
443− // empty delete is useless
444− None => (),
483+ None => changeset.delete(span),
445484 }
446485 last = to;
447486 }
448487
449− let span = len - last;
450− if span > 0 {
451− acc.push(Operation::Retain(span));
452− }
488+ changeset.retain(len - last);
453489
454− Self::from(ChangeSet { changes: acc, len })
490+ Self::from(changeset)
455491 }
456492
457493 /// Generate a transaction with a change per selection range.
@@ -591,7 +627,7 @@ mod test {
591627 }
592628
593629 #[test]
594− fn insert_composition() {
630+ fn optimized_composition() {
595631 let mut state = State::new("".into());
596632 let t1 = Transaction::insert(&state, Tendril::from_char('h'));
597633 t1.apply(&mut state);
@@ -620,5 +656,6 @@ mod test {
620656
621657 use Operation::*;
622658 assert_eq!(changes.changes, &[Insert("hello".into())]);
659+ // instead of insert h, insert e, insert l, insert l, insert o
623660 }
624661}