diff --git a/crates/loro-core/src/container/text/text_container.rs b/crates/loro-core/src/container/text/text_container.rs index dd279483..8ea3d83b 100644 --- a/crates/loro-core/src/container/text/text_container.rs +++ b/crates/loro-core/src/container/text/text_container.rs @@ -150,8 +150,10 @@ impl Container for TextContainer { &self.head ); // TODO: reuse tracker - // let head = if common_ancestors.is_empty() || !common_ancestors.iter().all(|x| self.tracker.contains(*x)) - let head = if true { + // let head = if true { + let head = if common_ancestors.is_empty() + || !common_ancestors.iter().all(|x| self.tracker.contains(*x)) + { debug_log!("NewTracker"); // FIXME use common ancestors self.tracker = Tracker::new(common_ancestors_vv, Counter::MAX / 2); diff --git a/crates/loro-core/src/container/text/tracker/effects_iter.rs b/crates/loro-core/src/container/text/tracker/effects_iter.rs index f8efefbc..3840d0f6 100644 --- a/crates/loro-core/src/container/text/tracker/effects_iter.rs +++ b/crates/loro-core/src/container/text/tracker/effects_iter.rs @@ -2,7 +2,8 @@ use rle::HasLength; use crate::{ container::text::text_content::ListSlice, - id::Counter, + debug_log, + id::{Counter, ID}, span::{CounterSpan, HasId, HasIdSpan, IdSpan}, version::IdSpanVector, }; @@ -13,7 +14,7 @@ pub struct EffectIter<'a> { tracker: &'a mut Tracker, left_spans: Vec, current_span: Option, - current_delete_targets: Option>, + current_delete_targets: Option<(ID, Vec)>, } impl<'a> EffectIter<'a> { @@ -43,7 +44,9 @@ impl<'a> Iterator for EffectIter<'a> { fn next(&mut self) -> Option { loop { - while let Some(ref mut delete_targets) = self.current_delete_targets { + while let Some((ref mut delete_op_id, ref mut delete_targets)) = + self.current_delete_targets + { if let Some(target) = delete_targets.pop() { let result = self .tracker @@ -73,6 +76,8 @@ impl<'a> Iterator for EffectIter<'a> { // SAFETY: we know that the cursor is valid here let pos = unsafe { cursor.get_index() }; let len = cursor.len; + *delete_op_id = delete_op_id.inc(cursor.len as Counter); + self.tracker.head_vv.set_end(*delete_op_id); let length = -self.tracker.update_cursors(cursor, StatusChange::Delete); assert!(length >= 0); if length > 0 { @@ -92,8 +97,8 @@ impl<'a> Iterator for EffectIter<'a> { if let Some(cursor) = cursor { match cursor { FirstCursorResult::Ins(id, cursor) => { - assert!(current.contains_id(id)); - assert!(current.contains_id(id.inc(cursor.len as Counter - 1))); + debug_assert!(current.contains_id(id)); + debug_assert!(current.contains_id(id.inc(cursor.len as Counter - 1))); current .counter .set_start(id.counter + cursor.len as Counter); @@ -102,6 +107,7 @@ impl<'a> Iterator for EffectIter<'a> { // SAFETY: cursor is valid here let content = unsafe { cursor.get_sliced().slice }; let len = cursor.len; + self.tracker.head_vv.set_end(id.inc(cursor.len as Counter)); let length_diff = self .tracker .update_cursors(cursor, StatusChange::SetAsCurrent); @@ -115,12 +121,14 @@ impl<'a> Iterator for EffectIter<'a> { } } FirstCursorResult::Del(id, del) => { - assert!(current.contains_id(id)); - assert!(current.contains_id(id.inc(del.atom_len() as Counter - 1))); + debug_assert!(current.contains_id(id)); + debug_assert!( + current.contains_id(id.inc(del.atom_len() as Counter - 1)) + ); current .counter .set_start(id.counter + del.atom_len() as Counter); - self.current_delete_targets = Some(del.iter().cloned().collect()); + self.current_delete_targets = Some((id, del.iter().cloned().collect())); } } } else { diff --git a/crates/loro-core/src/fuzz.rs b/crates/loro-core/src/fuzz.rs index 9c60f8c4..df168acd 100644 --- a/crates/loro-core/src/fuzz.rs +++ b/crates/loro-core/src/fuzz.rs @@ -306,6 +306,32 @@ mod test { use super::Action::*; use super::*; + #[test] + fn test_12() { + // retreat failed + test_multi_sites( + 3, + vec![ + Ins { + content: "x".into(), + pos: 0, + site: 0, + }, + Sync { from: 0, to: 1 }, + Ins { + content: "y".into(), + pos: 1, + site: 1, + }, + Del { + pos: 0, + len: 1, + site: 0, + }, + ], + ) + } + #[test] fn test_11() { test_multi_sites(