diff --git a/sub_crates/backend/src/marks.rs b/sub_crates/backend/src/marks.rs index a6a9b1a..c80f8ad 100644 --- a/sub_crates/backend/src/marks.rs +++ b/sub_crates/backend/src/marks.rs @@ -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(); diff --git a/sub_crates/backend/src/transaction.rs b/sub_crates/backend/src/transaction.rs index a5066ec..4694156 100644 --- a/sub_crates/backend/src/transaction.rs +++ b/sub_crates/backend/src/transaction.rs @@ -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); + } +}