diff --git a/cli/src/tests/mod.rs b/cli/src/tests/mod.rs index beafa172..3641cc3e 100644 --- a/cli/src/tests/mod.rs +++ b/cli/src/tests/mod.rs @@ -1,3 +1,4 @@ mod corpus_test; mod helpers; mod parser_api_test; +mod tree_test; diff --git a/cli/src/tests/tree_test.rs b/cli/src/tests/tree_test.rs new file mode 100644 index 00000000..401ff03a --- /dev/null +++ b/cli/src/tests/tree_test.rs @@ -0,0 +1,191 @@ +use super::helpers::fixtures::get_language; +use tree_sitter::{InputEdit, Language, Parser, Point}; + +#[test] +fn test_edit() { + let mut parser = Parser::new(); + parser.set_language(javascript()).unwrap(); + let tree = parser.parse_str(" abc !== def", None).unwrap(); + + assert_eq!( + tree.root_node().to_sexp(), + "(program (expression_statement (binary_expression (identifier) (identifier))))" + ); + + // edit entirely within the tree's padding: + // resize the padding of the tree and its leftmost descendants. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 1, + old_end_byte: 1, + new_end_byte: 2, + start_position: Point::new(0, 1), + old_end_position: Point::new(0, 1), + new_end_position: Point::new(0, 2), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 3); + assert_eq!(expr.end_byte(), 16); + assert!(child1.has_changes()); + assert_eq!(child1.start_byte(), 3); + assert_eq!(child1.end_byte(), 6); + assert!(!child2.has_changes()); + assert_eq!(child2.start_byte(), 8); + assert_eq!(child2.end_byte(), 11); + } + + // edit starting in the tree's padding but extending into its content: + // shrink the content to compenstate for the expanded padding. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 1, + old_end_byte: 4, + new_end_byte: 5, + start_position: Point::new(0, 1), + old_end_position: Point::new(0, 5), + new_end_position: Point::new(0, 5), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 5); + assert_eq!(expr.end_byte(), 16); + assert!(child1.has_changes()); + assert_eq!(child1.start_byte(), 5); + assert_eq!(child1.end_byte(), 6); + assert!(!child2.has_changes()); + assert_eq!(child2.start_byte(), 8); + assert_eq!(child2.end_byte(), 11); + } + + // insertion at the edge of a tree's padding: + // expand the tree's padding. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 2, + old_end_byte: 2, + new_end_byte: 4, + start_position: Point::new(0, 2), + old_end_position: Point::new(0, 2), + new_end_position: Point::new(0, 4), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 4); + assert_eq!(expr.end_byte(), 17); + assert!(child1.has_changes()); + assert_eq!(child1.start_byte(), 4); + assert_eq!(child1.end_byte(), 7); + assert!(!child2.has_changes()); + assert_eq!(child2.start_byte(), 9); + assert_eq!(child2.end_byte(), 12); + } + + // replacement starting at the edge of the tree's padding: + // resize the content and not the padding. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 2, + old_end_byte: 2, + new_end_byte: 4, + start_position: Point::new(0, 2), + old_end_position: Point::new(0, 2), + new_end_position: Point::new(0, 4), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 4); + assert_eq!(expr.end_byte(), 17); + assert!(child1.has_changes()); + assert_eq!(child1.start_byte(), 4); + assert_eq!(child1.end_byte(), 7); + assert!(!child2.has_changes()); + assert_eq!(child2.start_byte(), 9); + assert_eq!(child2.end_byte(), 12); + } + + // deletion that spans more than one child node: + // shrink subsequent child nodes. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 1, + old_end_byte: 11, + new_end_byte: 4, + start_position: Point::new(0, 1), + old_end_position: Point::new(0, 11), + new_end_position: Point::new(0, 4), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + let child3 = expr.child(2).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 4); + assert_eq!(expr.end_byte(), 8); + assert!(child1.has_changes()); + assert_eq!(child1.start_byte(), 4); + assert_eq!(child1.end_byte(), 4); + assert!(child2.has_changes()); + assert_eq!(child2.start_byte(), 4); + assert_eq!(child2.end_byte(), 4); + assert!(child3.has_changes()); + assert_eq!(child3.start_byte(), 5); + assert_eq!(child3.end_byte(), 8); + } + + // insertion at the end of the tree: + // extend the tree's content. + { + let mut tree = tree.clone(); + tree.edit(&InputEdit { + start_byte: 15, + old_end_byte: 15, + new_end_byte: 16, + start_position: Point::new(0, 15), + old_end_position: Point::new(0, 15), + new_end_position: Point::new(0, 16), + }); + + let expr = tree.root_node().child(0).unwrap().child(0).unwrap(); + let child1 = expr.child(0).unwrap(); + let child2 = expr.child(1).unwrap(); + let child3 = expr.child(2).unwrap(); + + assert!(expr.has_changes()); + assert_eq!(expr.start_byte(), 2); + assert_eq!(expr.end_byte(), 16); + assert!(!child1.has_changes()); + assert_eq!(child1.end_byte(), 5); + assert!(!child2.has_changes()); + assert_eq!(child2.end_byte(), 10); + assert!(child3.has_changes()); + assert_eq!(child3.end_byte(), 16); + } +} + +fn javascript() -> Language { + get_language("javascript") +}