From c6261d9fc129b7567a1b8f24487a8ebbd6ba2c95 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 15 Jun 2025 10:23:14 +0100 Subject: [PATCH] Elongate equals too --- ...ted_text__tests__calculate_operations.snap | 14 +-- ...ts__calculate_operations_with_no_diff.snap | 10 +- .../utils/elongate_operations.rs | 110 ++++++++++++++++-- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap b/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap index 246b2fe0..995db9c8 100644 --- a/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap +++ b/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap @@ -16,19 +16,7 @@ EditedText { }, OrderedOperation { order: 12, - operation: , - }, - OrderedOperation { - order: 13, - operation: , - }, - OrderedOperation { - order: 16, - operation: , - }, - OrderedOperation { - order: 17, - operation: , + operation: , }, OrderedOperation { order: 20, diff --git a/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap b/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap index 33414f8c..7639dbcc 100644 --- a/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap +++ b/backend/reconcile/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap @@ -8,15 +8,7 @@ EditedText { operations: [ OrderedOperation { order: 0, - operation: , - }, - OrderedOperation { - order: 5, - operation: , - }, - OrderedOperation { - order: 6, - operation: , + operation: , }, ], cursors: [], diff --git a/backend/reconcile/src/operation_transformation/utils/elongate_operations.rs b/backend/reconcile/src/operation_transformation/utils/elongate_operations.rs index c0a19811..b728bf0c 100644 --- a/backend/reconcile/src/operation_transformation/utils/elongate_operations.rs +++ b/backend/reconcile/src/operation_transformation/utils/elongate_operations.rs @@ -17,6 +17,9 @@ where let mut maybe_previous_insert: Option> = None; let mut maybe_previous_delete: Option> = None; + // Equals can't be interleaved with inserts and deletes + let mut maybe_previous_equal: Option> = None; + let mut result: Vec> = raw_operations .into_iter() .flat_map(|next| match next { @@ -27,7 +30,12 @@ where } prev => { maybe_previous_insert = Some(next); - Box::new(prev.into_iter()) + Box::new( + maybe_previous_equal + .take() + .into_iter() + .chain(prev.into_iter()), + ) as Box>> } }, RawOperation::Delete(..) => match maybe_previous_delete.take() { @@ -37,16 +45,30 @@ where } prev => { maybe_previous_delete = Some(next); - Box::new(prev.into_iter()) + Box::new( + maybe_previous_equal + .take() + .into_iter() + .chain(prev.into_iter()), + ) as Box>> + } + }, + RawOperation::Equal(..) => match maybe_previous_equal.take() { + Some(prev) if prev.is_right_joinable() && next.is_left_joinable() => { + maybe_previous_equal = Some(prev.extend(next)); + Box::new(iter::empty()) as Box>> + } + prev => { + maybe_previous_equal = Some(next); + Box::new( + maybe_previous_insert + .take() + .into_iter() + .chain(maybe_previous_delete.take()) + .chain(prev.into_iter()), + ) as Box>> } }, - RawOperation::Equal(..) => Box::new( - maybe_previous_insert - .take() - .into_iter() - .chain(maybe_previous_delete.take()) - .chain(iter::once(next)), - ) as Box>>, }) .collect(); @@ -58,5 +80,75 @@ where result.push(prev); } + if let Some(prev) = maybe_previous_equal { + result.push(prev); + } + result } + +#[cfg(test)] + +mod tests { + + use super::*; + + #[test] + fn test_elongate_operations_empty() { + let operations: Vec> = vec![]; + let result = elongate_operations(operations); + assert_eq!(result, vec![]); + } + + #[test] + fn test_elongate_operations_single_operation() { + let operations = vec![RawOperation::Insert(vec!["test".into()])]; + let result = elongate_operations(operations); + assert_eq!(result.len(), 1); + assert!(matches!(result[0], RawOperation::Insert(_))); + } + + #[test] + fn test_elongate_operations_interleaved() { + let operations = vec![ + RawOperation::Insert(vec!["a".into()]), + RawOperation::Delete(vec!["b".into()]), + RawOperation::Insert(vec!["c".into()]), + RawOperation::Delete(vec!["d".into()]), + ]; + let result = elongate_operations(operations); + assert_eq!(result.len(), 2); + assert!(matches!(result[0], RawOperation::Insert(_))); + assert!(matches!(result[1], RawOperation::Delete(_))); + } + + #[test] + fn test_elongate_operations_with_equal() { + let operations = vec![ + RawOperation::Equal(vec!["a".into()]), + RawOperation::Equal(vec!["b".into()]), + RawOperation::Insert(vec!["c".into()]), + RawOperation::Insert(vec!["d".into()]), + ]; + let result = elongate_operations(operations); + assert_eq!(result.len(), 2); + assert!(matches!(result[0], RawOperation::Equal(_))); + assert!(matches!(result[1], RawOperation::Insert(_))); + } + + #[test] + fn test_elongate_operations_mixed_sequence() { + let operations = vec![ + RawOperation::Insert(vec!["a".into()]), + RawOperation::Equal(vec!["b".into()]), + RawOperation::Delete(vec!["c".into()]), + RawOperation::Equal(vec!["d".into()]), + ]; + let result = elongate_operations(operations); + assert_eq!(result.len(), 4); + assert!(matches!(result[0], RawOperation::Insert(_))); + assert!(matches!(result[1], RawOperation::Equal(_))); + assert!(matches!(result[2], RawOperation::Delete(_))); + assert!(matches!(result[3], RawOperation::Equal(_))); + } +}