@@ -1,25 +1,293 @@
1−pub struct Change {
2− from: usize,
3− to: usize,
4− insert: Option<String>,
1+// pub struct Change {
2+// from: usize,
3+// to: usize,
4+// insert: Option<String>,
5+// }
6+
7+// 40 bytes (8 + 24 + 8) -> strings are really big 24 as String, 16 as &str
8+// pub struct Change {
9+// /// old extent
10+// old_extent: usize,
11+// /// inserted text, new extent equal to insert length
12+// insert: Option<String>,
13+// /// distance from the previous change
14+// distance: usize,
15+// }
16+
17+use crate::{Buffer, Selection};
18+
19+use ropey::Rope;
20+use tendril::StrTendril as Tendril;
21+
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)]
30+pub enum Change {
31+ /// Move cursor by n characters.
32+ Retain(usize),
33+ /// Delete n characters.
34+ Delete(usize),
35+ /// Insert text at position.
36+ Insert(Tendril),
537}
638
739impl Change {
8− pub fn new(from: usize, to: usize, insert: Option<String>) {
40+ pub fn new(from: usize, to: usize, insert: Option<Tendril>) {
941 // old_extent, new_extent, insert
1042 }
1143}
1244
13−pub struct Transaction {}
14−
1545// ChangeSpec = Change | ChangeSet | Vec<Change>
1646// ChangeDesc as a ChangeSet without text: can't be applied, cheaper to store.
1747// ChangeSet = ChangeDesc with Text
48+#[derive(Debug)]
1849pub struct ChangeSet {
1950 // basically Vec<ChangeDesc> where ChangeDesc = (current len, replacement len?)
2051 // (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.
55+ len: usize,
2256}
23−//
57+
58+impl ChangeSet {
59+ pub fn new(buf: &Buffer) -> Self {
60+ let len = buf.contents.len_chars();
61+ Self {
62+ changes: vec![Change::Retain(len)],
63+ len,
64+ }
65+ }
66+
67+ // TODO: from iter
68+
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 {
74+ // length mismatch
75+ return Err(());
76+ }
77+
78+ let len = self.changes.len();
79+
80+ let mut changes_a = self.changes.into_iter();
81+ let mut changes_b = other.changes.into_iter();
82+
83+ let mut head_a = changes_a.next();
84+ let mut head_b = changes_b.next();
85+
86+ let mut changes: Vec<Change> = Vec::with_capacity(len); // TODO: max(a, b), shrink_to_fit() afterwards
87+
88+ loop {
89+ use std::cmp::Ordering;
90+ use Change::*;
91+ match (head_a, head_b) {
92+ // we are done
93+ (None, None) => {
94+ break;
95+ }
96+ // deletion in A
97+ (Some(change @ Delete(..)), b) => {
98+ changes.push(change);
99+ head_a = changes_a.next();
100+ head_b = b;
101+ }
102+ // insertion in B
103+ (a, Some(change @ Insert(..))) => {
104+ changes.push(change);
105+ head_a = a;
106+ head_b = changes_b.next();
107+ }
108+ (None, _) => return Err(()),
109+ (_, None) => return Err(()),
110+ (Some(Retain(i)), Some(Retain(j))) => match i.cmp(&j) {
111+ Ordering::Less => {
112+ changes.push(Retain(i));
113+ head_a = changes_a.next();
114+ head_b = Some(Retain(j - i));
115+ }
116+ Ordering::Equal => {
117+ changes.push(Retain(i));
118+ head_a = changes_a.next();
119+ head_b = changes_b.next();
120+ }
121+ Ordering::Greater => {
122+ changes.push(Retain(j));
123+ head_a = Some(Retain(i - j));
124+ head_b = changes_b.next();
125+ }
126+ },
127+ (Some(Insert(mut s)), Some(Delete(j))) => {
128+ let len = s.chars().count();
129+ match len.cmp(&j) {
130+ Ordering::Less => {
131+ head_a = changes_a.next();
132+ head_b = Some(Delete(j - len));
133+ }
134+ Ordering::Equal => {
135+ head_a = changes_a.next();
136+ head_b = changes_b.next();
137+ }
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();
146+ }
147+ }
148+ }
149+ (Some(Insert(mut s)), Some(Retain(j))) => {
150+ let len = s.chars().count();
151+ match len.cmp(&j) {
152+ Ordering::Less => {
153+ changes.push(Insert(s));
154+ head_a = changes_a.next();
155+ head_b = Some(Retain(j - len));
156+ }
157+ Ordering::Equal => {
158+ changes.push(Insert(s));
159+ head_a = changes_a.next();
160+ head_b = changes_b.next();
161+ }
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();
170+ }
171+ }
172+ }
173+ (Some(Retain(i)), Some(Delete(j))) => match i.cmp(&j) {
174+ Ordering::Less => {
175+ changes.push(Delete(i));
176+ head_a = changes_a.next();
177+ head_b = Some(Delete(j - i));
178+ }
179+ Ordering::Equal => {
180+ changes.push(Delete(j));
181+ head_a = changes_a.next();
182+ head_b = changes_b.next();
183+ }
184+ Ordering::Greater => {
185+ changes.push(Delete(j));
186+ head_a = Some(Retain(i - j));
187+ head_b = changes_b.next();
188+ }
189+ },
190+ };
191+ }
192+
193+ Ok(Self {
194+ len: self.len,
195+ changes,
196+ })
197+ }
198+
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.
204+ ///
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 {
211+ unimplemented!()
212+ }
213+
214+ /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
215+ pub fn invert(self) -> Self {
216+ unimplemented!()
217+ }
218+
219+ pub fn apply(&self, text: &mut Rope) {
220+ // TODO: validate text.chars() == self.len
221+
222+ let mut pos = 0;
223+
224+ for change in self.changes.iter() {
225+ use Change::*;
226+ match change {
227+ Retain(n) => {
228+ pos += n;
229+ }
230+ Delete(n) => {
231+ text.remove(pos..pos + *n);
232+ // pos += n;
233+ }
234+ Insert(s) => {
235+ text.insert(pos, s);
236+ pos += s.len();
237+ }
238+ }
239+ }
240+ }
241+
242+ // iter over changes
243+}
244+
24245// trait Transaction
25246// trait StrictTransaction
247+
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.
252+ changes: ChangeSet,
253+ /// When set, explicitly updates the selection.
254+ selection: Option<Selection>,
255+ // effects, annotations
256+ // scroll_into_view
257+}
258+
259+#[cfg(test)]
260+mod test {
261+ use super::*;
262+
263+ #[test]
264+ fn composition() {
265+ use Change::*;
266+
267+ let a = ChangeSet {
268+ changes: vec![
269+ Retain(5),
270+ Insert("!".into()),
271+ Retain(1),
272+ Delete(2),
273+ Insert("ab".into()),
274+ ],
275+ len: 7,
276+ };
277+
278+ let b = ChangeSet {
279+ changes: vec![Delete(5), Insert("world".into()), Retain(4)],
280+ len: 7,
281+ };
282+
283+ let mut text = Rope::from("hello xz");
284+
285+ // should probably return cloned text
286+ a.compose(b).unwrap().apply(&mut text);
287+
288+ unimplemented!("{:?}", text);
289+ }
290+
291+ #[test]
292+ fn map() {}
293+}