Implement applying transactions to mark sets.

This commit is contained in:
Nathan Vegdahl 2023-10-24 23:35:15 +02:00
parent fa55afeebe
commit 9087019542
2 changed files with 236 additions and 4 deletions

View File

@ -128,6 +128,10 @@ impl MarkSet {
}
}
pub fn len(&self) -> usize {
self.marks.len()
}
pub fn clear(&mut self) {
self.main_mark_idx = 0;
self.marks.clear();

View File

@ -359,8 +359,52 @@ impl Transaction {
}
/// Applies the Transaction to a set of Marks.
pub fn apply_to_marks(&self, _marks: &mut MarkSet) {
todo!()
pub fn apply_to_marks(&self, marks: &mut MarkSet) {
marks.make_consistent();
// We do this in two passes: first heads, then tails.
for tail in 0..=1 {
let mut mark_idx = 0;
let mut byte_idx_old = 0;
let mut byte_idx_new = 0;
for op in self.ops.iter() {
let old_len = op.len_pre();
let new_len = op.len_post();
while mark_idx < marks.len() {
let idx = if tail == 1 {
&mut marks[mark_idx].tail
} else {
&mut marks[mark_idx].head
};
if *idx < byte_idx_old {
mark_idx += 1;
} else if *idx < (byte_idx_old + old_len) {
*idx = *idx + byte_idx_new - byte_idx_old;
*idx = (*idx).min(byte_idx_new + new_len);
mark_idx += 1;
} else {
break;
}
}
byte_idx_old += old_len;
byte_idx_new += new_len;
}
while mark_idx < marks.len() {
let idx = if tail == 1 {
&mut marks[mark_idx].tail
} else {
&mut marks[mark_idx].head
};
*idx = *idx + byte_idx_new - byte_idx_old;
mark_idx += 1;
}
}
marks.make_consistent();
}
/// Applies the inverse of the Transaction to a set of Marks.
@ -368,8 +412,52 @@ impl Transaction {
/// This is essentially an "undo" of the Transaction. However, on
/// marks this is a lossy operation and is not guaranteed to return
/// the marks to their original count and position.
pub fn apply_inverse_to_marks(&self, _marks: &mut MarkSet) {
todo!()
pub fn apply_inverse_to_marks(&self, marks: &mut MarkSet) {
marks.make_consistent();
// We do this in two passes: first heads, then tails.
for tail in 0..=1 {
let mut mark_idx = 0;
let mut byte_idx_old = 0;
let mut byte_idx_new = 0;
for op in self.ops.iter() {
let old_len = op.len_post();
let new_len = op.len_pre();
while mark_idx < marks.len() {
let idx = if tail == 1 {
&mut marks[mark_idx].tail
} else {
&mut marks[mark_idx].head
};
if *idx < byte_idx_old {
mark_idx += 1;
} else if *idx < (byte_idx_old + old_len) {
*idx = *idx + byte_idx_new - byte_idx_old;
*idx = (*idx).min(byte_idx_new + new_len);
mark_idx += 1;
} else {
break;
}
}
byte_idx_old += old_len;
byte_idx_new += new_len;
}
while mark_idx < marks.len() {
let idx = if tail == 1 {
&mut marks[mark_idx].tail
} else {
&mut marks[mark_idx].head
};
*idx = *idx + byte_idx_new - byte_idx_old;
mark_idx += 1;
}
}
marks.make_consistent();
}
//---------------------------------------------------------
@ -455,3 +543,143 @@ impl std::fmt::Debug for Transaction {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::marks::{Mark, MarkSet};
#[test]
fn apply_to_marks_01() {
let trans = Transaction::from_edit(5, "", "hi");
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6));
trans.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
assert_eq!(mark_set.marks[1].tail, 7);
assert_eq!(mark_set.marks[2].head, 8);
assert_eq!(mark_set.marks[2].tail, 8);
}
#[test]
fn apply_to_marks_02() {
let trans = Transaction {
ops: vec![
Op::Retain(5),
Op::Replace {
old: (0, 0),
new: (0, 1),
},
Op::Replace {
old: (0, 0),
new: (1, 2),
},
],
buffer: "hi".into(),
};
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6));
trans.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
assert_eq!(mark_set.marks[1].tail, 7);
assert_eq!(mark_set.marks[2].head, 8);
assert_eq!(mark_set.marks[2].tail, 8);
}
#[test]
fn apply_to_marks_03() {
let trans = Transaction::from_edit(5, "hi", "");
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8));
trans.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5);
assert_eq!(mark_set.marks[1].tail, 5);
assert_eq!(mark_set.marks[2].head, 6);
assert_eq!(mark_set.marks[2].tail, 6);
}
#[test]
fn apply_to_marks_04() {
let trans = Transaction {
ops: vec![
Op::Retain(5),
Op::Replace {
old: (0, 1),
new: (0, 0),
},
Op::Replace {
old: (1, 2),
new: (0, 0),
},
],
buffer: "hi".into(),
};
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8));
trans.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5);
assert_eq!(mark_set.marks[1].tail, 5);
assert_eq!(mark_set.marks[2].head, 6);
assert_eq!(mark_set.marks[2].tail, 6);
}
#[test]
fn apply_inverse_to_marks_01() {
let trans = Transaction::from_edit(5, "hi", "");
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6));
trans.apply_inverse_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
assert_eq!(mark_set.marks[1].tail, 7);
assert_eq!(mark_set.marks[2].head, 8);
assert_eq!(mark_set.marks[2].tail, 8);
}
#[test]
fn apply_inverse_to_marks_02() {
let trans = Transaction::from_edit(5, "", "hi");
let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8));
trans.apply_inverse_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5);
assert_eq!(mark_set.marks[1].tail, 5);
assert_eq!(mark_set.marks[2].head, 6);
assert_eq!(mark_set.marks[2].tail, 6);
}
}