feat: support delete consecutive nodes on backend (#1877)

This commit is contained in:
Lucas.Xu 2023-02-21 19:27:52 +08:00 committed by GitHub
parent f76d722b4c
commit b356354cd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 5 deletions

View File

@ -285,7 +285,7 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
final transaction = widget.editorState.transaction;
transaction.deleteNodesAtPath(
start,
end.last - start.last,
end.last - start.last + 1,
);
await widget.editorState.apply(transaction);
}

View File

@ -43,6 +43,16 @@ impl Path {
pub fn is_root(&self) -> bool {
self.0.len() == 1 && self.0[0] == 0
}
pub fn next(&self) -> Self {
let mut cloned_self = self.clone();
if !self.is_valid() {
return cloned_self;
}
let last = cloned_self.pop();
cloned_self.push(last.unwrap() + 1);
cloned_self
}
}
impl std::ops::Deref for Path {

View File

@ -293,7 +293,13 @@ impl NodeTree {
match op {
NodeOperation::Insert { path, nodes } => self.insert_nodes(&path, nodes),
NodeOperation::Update { path, changeset } => self.update(&path, changeset),
NodeOperation::Delete { path, nodes: _ } => self.delete_node(&path),
NodeOperation::Delete { path, nodes } => {
if nodes.is_empty() {
self.delete_node(&path)
} else {
self.delete_nodes(&path, nodes)
}
},
}
}
/// Inserts nodes at given path
@ -456,6 +462,32 @@ impl NodeTree {
Ok(())
}
/// Removes a node and the consecutive nodes behide it
///
/// if the nodes is empty, it will remove the single node at the path.
/// else it will remove the nodes at the path and the consecutive nodes behind it.
fn delete_nodes(&mut self, path: &Path, nodes: Vec<NodeData>) -> Result<(), OTError> {
if !path.is_valid() {
return Err(OTErrorCode::InvalidPath.into());
}
let node_id = self.node_id_at_path(path).ok_or_else(|| {
tracing::warn!("Can't find any node at path: {:?}", path);
OTError::internal().context("Can't find any node at path")
});
if node_id.is_err() {
return Err(OTErrorCode::PathNotFound.into());
}
for _ in 0..nodes.len() {
let res = self.delete_node(path);
res?
}
Ok(())
}
/// Update the node at path with the `changeset`
///
/// Do nothing if there is no node at the path.

View File

@ -31,6 +31,11 @@ pub enum NodeScript {
path: Path,
rev_id: usize,
},
DeleteNodes {
path: Path,
node_data_list: Vec<NodeData>,
rev_id: usize,
},
AssertNumberOfChildrenAtPath {
path: Option<Path>,
expected: usize,
@ -137,7 +142,17 @@ impl NodeTest {
self.transform_transaction_if_need(&mut transaction, rev_id);
self.apply_transaction(transaction);
},
NodeScript::DeleteNodes {
path,
node_data_list,
rev_id,
} => {
let mut transaction = TransactionBuilder::new()
.delete_nodes_at_path(&self.node_tree, &path, node_data_list.len())
.build();
self.transform_transaction_if_need(&mut transaction, rev_id);
self.apply_transaction(transaction);
},
NodeScript::AssertNode { path, expected } => {
let node = self.node_tree.get_node_data_at_path(&path);
assert_eq!(node, expected.map(|e| e.into()));

View File

@ -349,6 +349,44 @@ fn node_delete_test() {
test.run_scripts(scripts);
}
#[test]
fn nodes_delete_test() {
let mut test = NodeTest::new();
let node_1 = NodeData::new("a");
let node_2 = NodeData::new("b");
let node_3 = NodeData::new("c");
let node_data_list = vec![node_1, node_2, node_3];
let path: Path = 0.into();
let scripts = vec![
InsertNodes {
path: path.clone(),
node_data_list: node_data_list.clone(),
rev_id: 1,
},
DeleteNodes {
path: path.clone(),
node_data_list: node_data_list.clone(),
rev_id: 2,
},
AssertNode {
path: path.clone(),
expected: None,
},
AssertNode {
path: path.next(),
expected: None,
},
AssertNode {
path: path.next().next(),
expected: None,
},
AssertTreeJSON {
expected: r#""""#.to_string(),
},
];
test.run_scripts(scripts);
}
#[test]
fn node_delete_node_from_list_test() {
let mut test = NodeTest::new();