diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/operation.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/operation.dart index d1a0024a98..456be06766 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/operation.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/operation.dart @@ -207,10 +207,10 @@ Path transformPath(Path preInsertPath, Path b, [int delta = 1]) { Operation transformOperation(Operation a, Operation b) { if (a is InsertOperation) { - final newPath = transformPath(a.path, b.path); + final newPath = transformPath(a.path, b.path, a.nodes.length); return b.copyWithPath(newPath); } else if (a is DeleteOperation) { - final newPath = transformPath(a.path, b.path, -1); + final newPath = transformPath(a.path, b.path, -1 * a.nodes.length); return b.copyWithPath(newPath); } // TODO: transform update and textedit diff --git a/shared-lib/lib-ot/src/core/document/document.rs b/shared-lib/lib-ot/src/core/document/document.rs index 9d1d071f95..07626e1d75 100644 --- a/shared-lib/lib-ot/src/core/document/document.rs +++ b/shared-lib/lib-ot/src/core/document/document.rs @@ -124,6 +124,15 @@ impl DocumentTree { return; } + let children_length = parent.children(&self.arena).fold(0, |counter, _| counter + 1); + + if index == children_length { + for id in insert_children { + parent.append(*id, &mut self.arena); + } + return; + } + let node_to_insert = self.child_at_index_of_path(parent, index).unwrap(); for id in insert_children { diff --git a/shared-lib/lib-ot/src/core/document/document_operation.rs b/shared-lib/lib-ot/src/core/document/document_operation.rs index ff1a968af5..5a19f45a75 100644 --- a/shared-lib/lib-ot/src/core/document/document_operation.rs +++ b/shared-lib/lib-ot/src/core/document/document_operation.rs @@ -10,6 +10,14 @@ pub enum DocumentOperation { } impl DocumentOperation { + pub fn path(&self) -> &Position { + match self { + DocumentOperation::Insert(insert) => &insert.path, + DocumentOperation::Update(update) => &update.path, + DocumentOperation::Delete(delete) => &delete.path, + DocumentOperation::TextEdit(text_edit) => &text_edit.path, + } + } pub fn invert(&self) -> DocumentOperation { match self { DocumentOperation::Insert(insert_operation) => DocumentOperation::Delete(DeleteOperation { @@ -32,8 +40,40 @@ impl DocumentOperation { }), } } - pub fn transform(_a: &DocumentOperation, b: &DocumentOperation) -> DocumentOperation { - b.clone() + pub fn clone_with_new_path(&self, path: Position) -> DocumentOperation { + match self { + DocumentOperation::Insert(insert) => DocumentOperation::Insert(InsertOperation { + path, + nodes: insert.nodes.clone(), + }), + DocumentOperation::Update(update) => DocumentOperation::Update(UpdateOperation { + path, + attributes: update.attributes.clone(), + old_attributes: update.old_attributes.clone(), + }), + DocumentOperation::Delete(delete) => DocumentOperation::Delete(DeleteOperation { + path, + nodes: delete.nodes.clone(), + }), + DocumentOperation::TextEdit(text_edit) => DocumentOperation::TextEdit(TextEditOperation { + path, + delta: text_edit.delta.clone(), + inverted: text_edit.inverted.clone(), + }), + } + } + pub fn transform(a: &DocumentOperation, b: &DocumentOperation) -> DocumentOperation { + match a { + DocumentOperation::Insert(insert_op) => { + let new_path = Position::transform(a.path(), b.path(), insert_op.nodes.len() as i64); + b.clone_with_new_path(new_path) + } + DocumentOperation::Delete(delete_op) => { + let new_path = Position::transform(a.path(), b.path(), delete_op.nodes.len() as i64); + b.clone_with_new_path(new_path) + } + _ => b.clone(), + } } } diff --git a/shared-lib/lib-ot/src/core/document/position.rs b/shared-lib/lib-ot/src/core/document/position.rs index b463f207da..76f6a0eec4 100644 --- a/shared-lib/lib-ot/src/core/document/position.rs +++ b/shared-lib/lib-ot/src/core/document/position.rs @@ -12,7 +12,7 @@ impl Position { impl Position { // delta is default to be 1 - pub fn transform(pre_insert_path: &Position, b: &Position, delta: usize) -> Position { + pub fn transform(pre_insert_path: &Position, b: &Position, delta: i64) -> Position { if pre_insert_path.len() > b.len() { return b.clone(); } @@ -30,7 +30,7 @@ impl Position { let prev_insert_last: usize = *pre_insert_path.0.last().unwrap(); let b_at_index = b.0[pre_insert_path.0.len() - 1]; if prev_insert_last <= b_at_index { - prefix.push(b_at_index + delta); + prefix.push(((b_at_index as i64) + delta) as usize); } else { prefix.push(b_at_index); } diff --git a/shared-lib/lib-ot/tests/main.rs b/shared-lib/lib-ot/tests/main.rs index f6d5aa6ace..bfaf470dfa 100644 --- a/shared-lib/lib-ot/tests/main.rs +++ b/shared-lib/lib-ot/tests/main.rs @@ -34,3 +34,23 @@ fn test_documents() { document.apply(transaction); assert!(document.node_at_path(&vec![0].into()).is_none()); } + +#[test] +fn test_transform_paths() { + let mut document = DocumentTree::new(); + let transaction = { + let mut tb = TransactionBuilder::new(&document); + tb.insert_nodes(&vec![0].into(), &vec![NodeData::new("text")]); + tb.insert_nodes(&vec![1].into(), &vec![NodeData::new("text")]); + tb.insert_nodes(&vec![2].into(), &vec![NodeData::new("text")]); + tb.finalize() + }; + document.apply(transaction); + + let transaction = { + let mut tb = TransactionBuilder::new(&document); + tb.insert_nodes(&vec![1].into(), &vec![NodeData::new("text")]); + tb.finalize() + }; + document.apply(transaction); +}