Start implementing transaction composition.
This commit is contained in:
parent
f3c12d6cee
commit
19fa7b6abe
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user