Start implementing transaction composition.

This commit is contained in:
Nathan Vegdahl 2022-03-26 05:51:02 -07:00
parent f3c12d6cee
commit 19fa7b6abe

View File

@ -1,10 +1,8 @@
use std::ops::Range;
use ropey::Rope; use ropey::Rope;
use crate::marks::MarkSet; use crate::marks::MarkSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
enum Op { enum Op {
Retain { Retain {
byte_count: usize, byte_count: usize,
@ -12,9 +10,9 @@ enum Op {
Replace { Replace {
// These both represent strings, and are byte-index ranges into // These both represent strings, and are byte-index ranges into
// the Transaction's `buffer` where their actual string data is // the Transaction's `buffer` where their actual string data is
// stored. `0..0` can be used to indicate no string data. // stored. `(0, 0)` can be used to indicate no string data.
old: Range<usize>, old: (usize, usize),
new: Range<usize>, new: (usize, usize),
}, },
} }
@ -44,8 +42,8 @@ impl Transaction {
byte_count: byte_idx, byte_count: byte_idx,
}, },
Op::Replace { Op::Replace {
old: 0..old.len(), old: (0, old.len()),
new: old.len()..(old.len() + new.len()), new: (old.len(), old.len() + new.len()),
}, },
]; ];
@ -75,9 +73,9 @@ impl Transaction {
let adjusted_byte_idx = (byte_idx as isize + len_delta) as usize; let adjusted_byte_idx = (byte_idx as isize + len_delta) as usize;
let retained = adjusted_byte_idx - i; let retained = adjusted_byte_idx - i;
let old_range = trans.buffer.len()..(trans.buffer.len() + old.len()); let old_range = (trans.buffer.len(), trans.buffer.len() + old.len());
trans.buffer.push_str(old); trans.buffer.push_str(old);
let new_range = trans.buffer.len()..(trans.buffer.len() + new.len()); let new_range = (trans.buffer.len(), trans.buffer.len() + new.len());
trans.buffer.push_str(new); trans.buffer.push_str(new);
if retained > 0 { if retained > 0 {
@ -99,10 +97,110 @@ impl Transaction {
/// Build a Transaction that is functionally identical to applying /// Build a Transaction that is functionally identical to applying
/// first `self` and then `other` sequentially. /// first `self` and then `other` sequentially.
#[must_use] #[must_use]
pub fn compose(&self, _other: &Transaction) -> Transaction { pub fn compose(&self, other: &Transaction) -> Transaction {
use Op::*;
let mut trans = Transaction::new();
let mut ops1 = self.ops.iter();
let mut ops2 = other.ops.iter();
let mut op1 = ops1.next();
let mut op2 = ops2.next();
let mut range1 = (0, 0);
let mut range2 = (0, 0);
loop {
match (op1, op2) {
//-------------------------------
// Move past the unchanged bits.
(Some(Retain { byte_count: bc }), _) => {
range1.1 += bc;
op1 = ops1.next();
}
(_, Some(Retain { byte_count: bc })) => {
range2.1 += bc;
op2 = ops2.next();
}
//-----------------------------------------------------
// Handle changes when there are still changes in both
// source transactions.
(
Some(Replace {
old: old1,
new: new1,
}),
Some(Replace {
old: old2,
new: new2,
}),
) => {
// This is the complex case.
todo!() todo!()
} }
//------------------------------------------------------
// Handle changes when there are only changes remaining
// in one of the source transactions.
(Some(Replace { old, new }), None) => {
if range1.0 < range1.1 {
trans.ops.push(Retain {
byte_count: range1.1 - range1.0,
});
range1.0 = range1.1;
}
let ti = trans.buffer.len();
trans.buffer.push_str(&self.buffer[old.0..old.1]);
let old_range = (ti, ti + (old.1 - old.0));
let ti = trans.buffer.len();
trans.buffer.push_str(&self.buffer[new.0..new.1]);
let new_range = (ti, ti + (new.1 - new.0));
trans.ops.push(Replace {
old: old_range,
new: new_range,
});
op1 = ops1.next();
}
(None, Some(Replace { old, new })) => {
if range2.0 < range2.1 {
trans.ops.push(Retain {
byte_count: range2.1 - range2.0,
});
range2.0 = range2.1;
}
let ti = trans.buffer.len();
trans.buffer.push_str(&other.buffer[old.0..old.1]);
let old_range = (ti, ti + (old.1 - old.0));
let ti = trans.buffer.len();
trans.buffer.push_str(&other.buffer[new.0..new.1]);
let new_range = (ti, ti + (new.1 - new.0));
trans.ops.push(Replace {
old: old_range,
new: new_range,
});
op2 = ops2.next();
}
//-------
// Done.
(None, None) => {
break;
}
}
}
trans
}
/// Build a Transaction that is functionally identical to undoing /// Build a Transaction that is functionally identical to undoing
/// this Transaction. /// this Transaction.
/// ///
@ -135,14 +233,18 @@ impl Transaction {
i += byte_count; i += byte_count;
} }
Op::Replace { old, new } => { Op::Replace { old, new } => {
let old = &self.buffer[old.clone()]; let old = &self.buffer[old.0..old.1];
let new = &self.buffer[new.clone()]; let new = &self.buffer[new.0..new.1];
let char_i = text.byte_to_char(i); let char_i = text.byte_to_char(i);
let old_char_len = old.chars().count();
if !old.is_empty() {
let old_char_len = old.chars().count();
debug_assert_eq!(text.slice(char_i..(char_i + old_char_len)), old); debug_assert_eq!(text.slice(char_i..(char_i + old_char_len)), old);
text.remove(char_i..(char_i + old_char_len)); text.remove(char_i..(char_i + old_char_len));
}
if !new.is_empty() {
text.insert(char_i, new); text.insert(char_i, new);
}
i = i + new.len(); i = i + new.len();
} }