@@ -1,25 +1,293 @@
1−pub struct Change {
1+// pub struct Change {
2− from: usize,
2+// from: usize,
3− to: usize,
3+// to: usize,
4+// insert: Option<String>,
7+// 40 bytes (8 + 24 + 8) -> strings are really big 24 as String, 16 as &str
11+// /// inserted text, new extent equal to insert length
4− insert: Option<String>,
12+// insert: Option<String>,
13+// /// distance from the previous change
17+use crate::{Buffer, Selection};
20+use tendril::StrTendril as Tendril;
22+// TODO: divided into three different operations, I sort of like having just
23+// Splice { extent, Option<text>, distance } better.
24+// insert: Splice { extent: 0, text: Some("a"), distance: 2 }
25+// delete: Splice { extent: 2, text: None, distance: 2 }
26+// replace: Splice { extent: 2, text: Some("abc"), distance: 2 }
27+// unchanged?: Splice { extent: 0, text: None, distance: 2 }
28+// harder to compose though.
29+#[derive(Debug, Clone, PartialEq, Eq)]
31+ /// Move cursor by n characters.
33+ /// Delete n characters.
35+ /// Insert text at position.
7impl Change {
39impl Change {
8− pub fn new(from: usize, to: usize, insert: Option<String>) {
40+ pub fn new(from: usize, to: usize, insert: Option<Tendril>) {
9 // old_extent, new_extent, insert
41 // old_extent, new_extent, insert
13−pub struct Transaction {}
15// ChangeSpec = Change | ChangeSet | Vec<Change>
45// ChangeSpec = Change | ChangeSet | Vec<Change>
16// ChangeDesc as a ChangeSet without text: can't be applied, cheaper to store.
46// ChangeDesc as a ChangeSet without text: can't be applied, cheaper to store.
17// ChangeSet = ChangeDesc with Text
47// ChangeSet = ChangeDesc with Text
18pub struct ChangeSet {
49pub struct ChangeSet {
19 // basically Vec<ChangeDesc> where ChangeDesc = (current len, replacement len?)
50 // basically Vec<ChangeDesc> where ChangeDesc = (current len, replacement len?)
20 // (0, n>0) for insertion, (n>0, 0) for deletion, (>0, >0) for replacement
51 // (0, n>0) for insertion, (n>0, 0) for deletion, (>0, >0) for replacement
21− sections: Vec<(usize, isize)>,
52+ // sections: Vec<(usize, isize)>,
53+ changes: Vec<Change>,
54+ /// The required document length. Will refuse to apply changes unless it matches.
59+ pub fn new(buf: &Buffer) -> Self {
60+ let len = buf.contents.len_chars();
62+ changes: vec![Change::Retain(len)],
69+ /// Combine two changesets together.
70+ /// In other words, If `this` goes `docA` → `docB` and `other` represents `docB` → `docC`, the
71+ /// returned value will represent the change `docA` → `docC`.
72+ pub fn compose(self, other: ChangeSet) -> Result<Self, ()> {
73+ if self.len != other.len {
78+ let len = self.changes.len();
80+ let mut changes_a = self.changes.into_iter();
81+ let mut changes_b = other.changes.into_iter();
83+ let mut head_a = changes_a.next();
84+ let mut head_b = changes_b.next();
86+ let mut changes: Vec<Change> = Vec::with_capacity(len); // TODO: max(a, b), shrink_to_fit() afterwards
89+ use std::cmp::Ordering;
91+ match (head_a, head_b) {
97+ (Some(change @ Delete(..)), b) => {
98+ changes.push(change);
99+ head_a = changes_a.next();
103+ (a, Some(change @ Insert(..))) => {
104+ changes.push(change);
106+ head_b = changes_b.next();
108+ (None, _) => return Err(()),
109+ (_, None) => return Err(()),
110+ (Some(Retain(i)), Some(Retain(j))) => match i.cmp(&j) {
112+ changes.push(Retain(i));
113+ head_a = changes_a.next();
114+ head_b = Some(Retain(j - i));
116+ Ordering::Equal => {
117+ changes.push(Retain(i));
118+ head_a = changes_a.next();
119+ head_b = changes_b.next();
121+ Ordering::Greater => {
122+ changes.push(Retain(j));
123+ head_a = Some(Retain(i - j));
124+ head_b = changes_b.next();
127+ (Some(Insert(mut s)), Some(Delete(j))) => {
128+ let len = s.chars().count();
131+ head_a = changes_a.next();
132+ head_b = Some(Delete(j - len));
134+ Ordering::Equal => {
135+ head_a = changes_a.next();
136+ head_b = changes_b.next();
138+ Ordering::Greater => {
139+ // figure out the byte index of the truncated string end
140+ let (pos, _) = s.char_indices().nth(len - j).unwrap();
141+ // calculate the difference
142+ let to_drop = s.len() - pos;
143+ s.pop_back(to_drop as u32);
144+ head_a = Some(Insert(s));
145+ head_b = changes_b.next();
149+ (Some(Insert(mut s)), Some(Retain(j))) => {
150+ let len = s.chars().count();
153+ changes.push(Insert(s));
154+ head_a = changes_a.next();
155+ head_b = Some(Retain(j - len));
157+ Ordering::Equal => {
158+ changes.push(Insert(s));
159+ head_a = changes_a.next();
160+ head_b = changes_b.next();
162+ Ordering::Greater => {
163+ // figure out the byte index of the truncated string end
164+ let (pos, _) = s.char_indices().nth(j).unwrap();
165+ // calculate the difference
166+ let to_drop = s.len() - pos;
167+ s.pop_back(to_drop as u32);
168+ head_a = Some(Insert(s));
169+ head_b = changes_b.next();
173+ (Some(Retain(i)), Some(Delete(j))) => match i.cmp(&j) {
175+ changes.push(Delete(i));
176+ head_a = changes_a.next();
177+ head_b = Some(Delete(j - i));
179+ Ordering::Equal => {
180+ changes.push(Delete(j));
181+ head_a = changes_a.next();
182+ head_b = changes_b.next();
184+ Ordering::Greater => {
185+ changes.push(Delete(j));
186+ head_a = Some(Retain(i - j));
187+ head_b = changes_b.next();
199+ /// Given another change set starting in the same document, maps this
200+ /// change set over the other, producing a new change set that can be
201+ /// applied to the document produced by applying `other`. When
202+ /// `before` is `true`, order changes as if `this` comes before
203+ /// `other`, otherwise (the default) treat `other` as coming first.
205+ /// Given two changes `A` and `B`, `A.compose(B.map(A))` and
206+ /// `B.compose(A.map(B, true))` will produce the same document. This
207+ /// provides a basic form of [operational
208+ /// transformation](https://en.wikipedia.org/wiki/Operational_transformation),
209+ /// and can be used for collaborative editing.
210+ pub fn map(self, other: Self) -> Self {
214+ /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
215+ pub fn invert(self) -> Self {
219+ pub fn apply(&self, text: &mut Rope) {
220+ // TODO: validate text.chars() == self.len
224+ for change in self.changes.iter() {
231+ text.remove(pos..pos + *n);
235+ text.insert(pos, s);
242+ // iter over changes
24// trait Transaction
245// trait Transaction
25// trait StrictTransaction
246// trait StrictTransaction
248+/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
249+/// a single transaction.
250+pub struct Transaction {
251+ /// Changes made to the buffer.
253+ /// When set, explicitly updates the selection.
254+ selection: Option<Selection>,
255+ // effects, annotations
273+ Insert("ab".into()),
279+ changes: vec![Delete(5), Insert("world".into()), Retain(4)],
283+ let mut text = Rope::from("hello xz");
285+ // should probably return cloned text
286+ a.compose(b).unwrap().apply(&mut text);
288+ unimplemented!("{:?}", text);