diff --git a/Cargo.lock b/Cargo.lock index 4d4565f..3fb050a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,9 @@ dependencies = [ [[package]] name = "model" version = "0.1.0" +dependencies = [ + "tree-sitter", +] [[package]] name = "nu-ansi-term" diff --git a/bin/src/main.rs b/bin/src/main.rs index dcfa77f..1428b6c 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -1,32 +1,52 @@ fn main() { let base = r#" - public class Main { - public static void main(String[] args) { - System.out.println("Hello, world!"); - int y = 4; - int j = 0; - } + public interface Repository { } "#; - let left = r#" - public class Main { - public static void main(String[] args) { - int x = 0; - System.out.println("Hello, João!"); - int y = 3; - int j = 0; - } + let left: &str = r#" + public interface Repository { + void create(Pessoa pessoa); + void delete(Pessoa pessoa); } "#; + let right = r#" - public class Main { - public static void main(String[] args) { - System.out.println("Hello, Paulo!"); - int y = 3; - } + public interface Repository { + void create(Pessoa pessoa); + void delete(Pessoa pessoa); + void remove(Pessoa pessoa); + void insert(Pessoa pessoa); } "#; + // let base = r#" + // public class Main { + // public static void main(String[] args) { + // System.out.println("Hello, world!"); + // int y = 4; + // int j = 0; + // } + // } + // "#; + // let left = r#" + // public class Main { + // public static void main(String[] args) { + // int x = 0; + // System.out.println("Hello, João!"); + // int y = 3; + // int j = 0; + // } + // } + // "#; + // let right = r#" + // public class Main { + // public static void main(String[] args) { + // System.out.println("Hello, Paulo!"); + // int y = 3; + // } + // } + // "#; + let parser_configuration = parsing::ParserConfiguration::from_language(model::Language::Java); let base_tree = parsing::parse_string(base, &parser_configuration).unwrap(); diff --git a/matching/src/lib.rs b/matching/src/lib.rs index 9d7b851..53468d4 100644 --- a/matching/src/lib.rs +++ b/matching/src/lib.rs @@ -2,6 +2,7 @@ mod matching; mod matching_entry; mod matchings; mod ordered_tree_matching; +mod unordered_tree_matching; pub use matching_entry::MatchingEntry; pub use matchings::Matchings; @@ -11,5 +12,9 @@ pub fn calculate_matchings<'a>( left: &'a model::CSTNode, right: &'a model::CSTNode, ) -> Matchings<'a> { - return ordered_tree_matching::ordered_tree_matching(left, right); + if left.can_be_matching_unordered() && right.can_be_matching_unordered() { + unordered_tree_matching::unordered_tree_matching(left, right) + } else { + ordered_tree_matching::ordered_tree_matching(left, right) + } } diff --git a/matching/src/matching_entry.rs b/matching/src/matching_entry.rs index d3424c8..e1fdc34 100644 --- a/matching/src/matching_entry.rs +++ b/matching/src/matching_entry.rs @@ -12,3 +12,12 @@ impl MatchingEntry { }; } } + +impl Default for &MatchingEntry { + fn default() -> Self { + &MatchingEntry { + score: 0, + is_perfect_match: false, + } + } +} diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index aeb8954..6b7b363 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -82,6 +82,8 @@ impl<'a> IntoIterator for Matchings<'a> { #[cfg(test)] mod tests { + use model::Point; + use super::*; #[test] @@ -89,6 +91,8 @@ mod tests { let a_node = CSTNode::Terminal { kind: "kind", value: "value".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; assert_eq!(None, Matchings::empty().find_matching_for(&a_node)) @@ -99,6 +103,8 @@ mod tests { let a_node = CSTNode::Terminal { kind: "kind", value: "value".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let mut matchings = HashMap::new(); diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index 6fc4ed5..3f0247e 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -24,10 +24,12 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match CSTNode::NonTerminal { kind: kind_left, children: children_left, + .. }, CSTNode::NonTerminal { kind: kind_right, children: children_right, + .. }, ) => { let root_matching: usize = (kind_left == kind_right).into(); @@ -44,7 +46,9 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match let right_child = children_right.get(j - 1).unwrap(); let w = calculate_matchings(left_child, right_child); - let matching = w.get_matching_entry(left_child, right_child).unwrap(); + let matching = w + .get_matching_entry(left_child, right_child) + .unwrap_or_default(); if matrix_m[i][j - 1] > matrix_m[i - 1][j] { if matrix_m[i][j - 1] > matrix_m[i - 1][j - 1] + matching.score { @@ -94,10 +98,12 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match CSTNode::Terminal { kind: kind_left, value: value_left, + .. }, CSTNode::Terminal { kind: kind_right, value: value_right, + .. }, ) => { let is_perfetch_match = kind_left == kind_right && value_left == value_right; @@ -116,17 +122,21 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match #[cfg(test)] mod tests { use crate::{matching_entry::MatchingEntry, *}; - use model::CSTNode; + use model::{CSTNode, Point}; #[test] fn two_terminal_nodes_matches_with_a_score_of_one_if_they_have_the_same_kind_and_value() { let left = CSTNode::Terminal { kind: "kind", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let right = CSTNode::Terminal { kind: "kind", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -142,10 +152,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::Terminal { kind: "kind", value: "value_b".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -161,10 +175,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind_a", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let right = CSTNode::Terminal { kind: "kind_b", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -180,10 +198,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind_a", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::Terminal { kind: "kind_b", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -199,13 +221,19 @@ mod tests { let child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 1, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 1, column: 7 }, children: vec![child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 1, column: 7 }, children: vec![child.clone()], }; @@ -222,19 +250,27 @@ mod tests { let left_child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right_child = CSTNode::Terminal { kind: "kind_c".into(), value: "value_c".into(), + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 1, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", children: vec![left_child.clone()], + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::NonTerminal { kind: "kind_a", children: vec![right_child.clone()], + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -250,18 +286,26 @@ mod tests { let common_child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let unique_right_child = CSTNode::Terminal { kind: "kind_c".into(), value: "value_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone(), unique_right_child], }; @@ -277,15 +321,21 @@ mod tests { fn perfect_matching_deep_nodes() { let common_child = CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; @@ -301,20 +351,28 @@ mod tests { fn perfect_matching_deeper_nodes() { let leaf = CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }; let intermediate = CSTNode::NonTerminal { kind: "intermediate".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![leaf], }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![intermediate.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![intermediate.clone()], }; diff --git a/matching/src/unordered_tree_matching.rs b/matching/src/unordered_tree_matching.rs new file mode 100644 index 0000000..d6f41ec --- /dev/null +++ b/matching/src/unordered_tree_matching.rs @@ -0,0 +1,97 @@ +use model::CSTNode; +use utils::unordered_pair::UnorderedPair; + +use crate::{calculate_matchings, MatchingEntry, Matchings}; + +pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> crate::Matchings<'a> { + match (left, right) { + ( + CSTNode::Terminal { + kind: kind_left, + value: value_left, + .. + }, + CSTNode::Terminal { + kind: kind_right, + value: value_right, + .. + }, + ) => { + let is_perfetch_match = kind_left == kind_right && value_left == value_right; + Matchings::from_single( + UnorderedPair::new(left, right), + MatchingEntry::new(is_perfetch_match.into(), is_perfetch_match), + ) + } + ( + CSTNode::NonTerminal { + children: children_left, + .. + }, + CSTNode::NonTerminal { + children: children_right, + .. + }, + ) => { + let root_matching: usize = (left == right).into(); + + let mut sum = 0; + let mut result = Matchings::empty(); + + for child_left in children_left { + for child_right in children_right { + let matching_score = compute_matching_score(&child_left, &child_right); + + if matching_score == 1 { + let child_matching = calculate_matchings(child_left, child_right); + sum += child_matching + .get_matching_entry(child_left, child_right) + .map_or(0, |matching| matching.score); + result.extend(child_matching); + } + } + } + + result.extend(Matchings::from_single( + UnorderedPair(left, right), + MatchingEntry { + score: sum + root_matching, + is_perfect_match: left == right, + }, + )); + + result + } + (_, _) => panic!("Invalid configuration reached"), + } +} + +fn compute_matching_score<'a>(left: &'a CSTNode, right: &'a CSTNode) -> usize { + match (left, right) { + (CSTNode::Terminal { .. }, CSTNode::Terminal { .. }) => (left == right).into(), + ( + CSTNode::NonTerminal { + children: children_left, + .. + }, + CSTNode::NonTerminal { + children: children_right, + .. + }, + ) => { + // Try to find an identifier on children, and compare them + let identifier_left = children_left.iter().find(|node| match node { + CSTNode::Terminal { kind, .. } => kind == &"identifier", + _ => false, + }); + + let identifier_right = children_right.iter().find(|node| match node { + CSTNode::Terminal { kind, .. } => kind == &"identifier", + _ => false, + }); + + (identifier_left == identifier_right).into() + } + (_, _) => 0, + } +} diff --git a/merge/src/odered_merge.rs b/merge/src/odered_merge.rs index 532e377..a6ca282 100644 --- a/merge/src/odered_merge.rs +++ b/merge/src/odered_merge.rs @@ -20,7 +20,10 @@ pub fn ordered_merge<'a>( value: value_left, .. }, CSTNode::Terminal { - value: value_right, .. + value: value_right, + start_position, + end_position, + .. }, ) => { // Unchanged @@ -29,8 +32,18 @@ pub fn ordered_merge<'a>( // Changed in both } else if value_left != value_base && value_right != value_base { match diffy::merge(&value_base, &value_left, &value_right) { - Ok(value) => CSTNode::Terminal { kind, value: value }, - Err(value) => CSTNode::Terminal { kind, value: value }, + Ok(value) => CSTNode::Terminal { + kind, + value, + start_position: *start_position, + end_position: *end_position, + }, + Err(value) => CSTNode::Terminal { + kind, + value, + start_position: *start_position, + end_position: *end_position, + }, } // Only left changed } else if value_left != value_base { @@ -48,6 +61,8 @@ pub fn ordered_merge<'a>( }, CSTNode::NonTerminal { children: children_right, + start_position, + end_position, .. }, ) => { @@ -239,6 +254,8 @@ pub fn ordered_merge<'a>( CSTNode::NonTerminal { kind, children: result_children, + start_position: *start_position, + end_position: *end_position, } } (_, _, _) => panic!("Can not merge Terminal with Non-Terminal"), @@ -250,7 +267,7 @@ mod tests { use std::vec; use matching::{ordered_tree_matching, Matchings}; - use model::CSTNode; + use model::{CSTNode, Point}; use super::ordered_merge; @@ -311,6 +328,8 @@ mod tests { fn if_i_am_merging_three_unchanged_nodes_it_is_a_success() { let node = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; @@ -326,14 +345,20 @@ mod tests { fn returns_success_if_there_are_changes_in_both_parents_and_they_are_not_conflicting() { let base = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "\nvalue\n".into(), }; let left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left\nvalue\n".into(), }; let right = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "\nvalue\nright".into(), }; @@ -343,6 +368,8 @@ mod tests { right, CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left\nvalue\nright".into(), }, ) @@ -352,14 +379,20 @@ mod tests { fn returns_conflict_if_there_are_changes_in_both_parents_and_they_are_conflicting() { let base = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; let left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left_value".into(), }; let right = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "right_value".into(), }; @@ -368,6 +401,8 @@ mod tests { &Matchings::empty()), CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "<<<<<<< ours\nleft_value||||||| original\nvalue=======\nright_value>>>>>>> theirs\n".into() } ) @@ -377,10 +412,14 @@ mod tests { fn if_there_is_a_change_only_in_one_parent_it_returns_the_changes_from_this_parent() { let base_and_left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; let changed_parent = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_right".into(), }; @@ -398,14 +437,20 @@ mod tests { ordered_merge( &CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }, &CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }, &Matchings::empty(), @@ -418,13 +463,19 @@ mod tests { fn it_merges_non_terminals_if_there_are_non_changes() { let tree = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -442,17 +493,25 @@ mod tests { fn it_merges_non_terminals_if_both_left_and_right_add_the_same_things() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -471,26 +530,38 @@ mod tests { { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let initially_empty_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent_that_added = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -507,29 +578,43 @@ mod tests { fn it_merges_non_terminals_if_only_one_parent_adds_a_node_in_non_empty_children_list() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_that_added = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -537,13 +622,19 @@ mod tests { let merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -561,32 +652,48 @@ mod tests { fn it_merges_when_one_parent_adds_a_node_and_removes_one_that_was_not_edited_in_the_other() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let changed_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -603,10 +710,16 @@ mod tests { fn it_merges_when_one_parent_adds_a_node_and_removes_from_another_that_was_changed() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }], @@ -614,10 +727,16 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }], @@ -625,10 +744,16 @@ mod tests { let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -658,11 +783,17 @@ mod tests { assert_eq!( CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, @@ -670,8 +801,12 @@ mod tests { left: None, right: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }), @@ -684,19 +819,29 @@ mod tests { assert_eq!( CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }), @@ -712,21 +857,31 @@ mod tests { fn if_both_parents_add_different_nodes_then_we_have_a_conflict() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -737,13 +892,19 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: Some(&CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }), right: Some(&CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }), }], @@ -755,13 +916,19 @@ mod tests { fn it_merges_when_one_parent_removes_a_node_that_was_not_changed_in_another_parent() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -769,13 +936,19 @@ mod tests { let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -783,8 +956,12 @@ mod tests { let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -795,8 +972,12 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, @@ -807,16 +988,24 @@ mod tests { fn it_detects_a_conflit_when_one_parent_removes_a_node_that_was_changed_in_another_parent() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -824,16 +1013,24 @@ mod tests { let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -841,8 +1038,12 @@ mod tests { let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -853,12 +1054,18 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -867,6 +1074,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -879,13 +1088,19 @@ mod tests { &left, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -893,6 +1108,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -904,13 +1121,19 @@ mod tests { fn it_merges_when_a_parent_adds_a_node() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -918,13 +1141,19 @@ mod tests { let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -932,17 +1161,25 @@ mod tests { let changed_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -950,17 +1187,25 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -978,29 +1223,43 @@ mod tests { fn it_merges_when_one_parent_removes_and_add_a_node() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1008,8 +1267,12 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -1026,10 +1289,16 @@ mod tests { fn it_conflicts_when_one_parent_removes_and_add_a_node() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }], @@ -1037,24 +1306,36 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1066,13 +1347,19 @@ mod tests { &parent_b, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1080,6 +1367,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1091,12 +1380,18 @@ mod tests { &parent_a, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1105,6 +1400,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1116,26 +1413,38 @@ mod tests { fn it_merges_when_a_parent_adds_one_node() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1143,13 +1452,19 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1167,13 +1482,19 @@ mod tests { fn it_does_not_detect_a_conflict_if_am_merging_two_subtrees_that_have_not_changed_mutually() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -1181,22 +1502,32 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }; let expected_merge = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; @@ -1213,18 +1544,28 @@ mod tests { ) { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, @@ -1233,10 +1574,16 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -1244,10 +1591,16 @@ mod tests { let parent_b = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -1259,11 +1612,17 @@ mod tests { &parent_b, &CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1278,12 +1637,18 @@ mod tests { &parent_a, &CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) diff --git a/model/Cargo.toml b/model/Cargo.toml index 1d77457..31444d5 100644 --- a/model/Cargo.toml +++ b/model/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tree-sitter = "0.20.9" diff --git a/model/src/cst_node.rs b/model/src/cst_node.rs index 9aadebe..222b406 100644 --- a/model/src/cst_node.rs +++ b/model/src/cst_node.rs @@ -3,10 +3,14 @@ pub enum CSTNode<'a> { Terminal { kind: &'a str, value: String, + start_position: tree_sitter::Point, + end_position: tree_sitter::Point, }, NonTerminal { kind: &'a str, children: Vec>, + start_position: tree_sitter::Point, + end_position: tree_sitter::Point, }, Conflict { left: Option<&'a CSTNode<'a>>, @@ -14,6 +18,16 @@ pub enum CSTNode<'a> { }, } +impl CSTNode<'_> { + pub fn can_be_matching_unordered(&self) -> bool { + match self { + CSTNode::Terminal { .. } => false, + CSTNode::NonTerminal { kind, .. } => vec!["interface_body"].contains(kind), + CSTNode::Conflict { .. } => false, + } + } +} + impl ToString for CSTNode<'_> { fn to_string(&self) -> String { match self { diff --git a/model/src/lib.rs b/model/src/lib.rs index c58b4dc..6b7d048 100644 --- a/model/src/lib.rs +++ b/model/src/lib.rs @@ -3,3 +3,5 @@ pub mod language; pub use cst_node::CSTNode; pub use language::Language; + +pub type Point = tree_sitter::Point; diff --git a/parsing/src/parse.rs b/parsing/src/parse.rs index 1e060e1..e690a59 100644 --- a/parsing/src/parse.rs +++ b/parsing/src/parse.rs @@ -6,12 +6,16 @@ fn explore_node<'a>(node: Node, src: &'a str, config: &'a ParserConfiguration) - if node.child_count() == 0 || config.stop_compilation_at.contains(node.kind()) { CSTNode::Terminal { kind: node.kind().into(), + start_position: node.start_position(), + end_position: node.end_position(), value: src[node.byte_range()].into(), } } else { let mut cursor = node.walk(); CSTNode::NonTerminal { kind: node.kind().into(), + start_position: node.start_position(), + end_position: node.end_position(), children: node .children(&mut cursor) .map(|child| explore_node(child, src, config)) @@ -38,6 +42,9 @@ pub fn parse_string<'a>( #[cfg(test)] mod tests { + use model::CSTNode::{NonTerminal, Terminal}; + use model::Point; + use super::*; #[test] @@ -52,90 +59,136 @@ mod tests { stop_compilation_at: [].into_iter().collect(), }; let result = parse_string(code, &parser_configuration); - let expected = CSTNode::NonTerminal { - kind: "program".into(), - children: vec![CSTNode::NonTerminal { - kind: "interface_declaration".into(), + let expected = NonTerminal { + kind: "program", + children: vec![NonTerminal { + kind: "interface_declaration", children: vec![ - CSTNode::NonTerminal { - kind: "modifiers".into(), + NonTerminal { + kind: "modifiers", children: vec![ - CSTNode::Terminal { - kind: "public".into(), + Terminal { + kind: "public", value: "public".into(), + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 1, column: 18 }, }, - CSTNode::Terminal { - kind: "static".into(), + Terminal { + kind: "static", value: "static".into(), + start_position: Point { row: 1, column: 19 }, + end_position: Point { row: 1, column: 25 }, }, ], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 1, column: 25 }, }, - CSTNode::Terminal { - kind: "interface".into(), + Terminal { + kind: "interface", value: "interface".into(), + start_position: Point { row: 1, column: 26 }, + end_position: Point { row: 1, column: 35 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "HelloWorld".into(), + start_position: Point { row: 1, column: 36 }, + end_position: Point { row: 1, column: 46 }, }, - CSTNode::NonTerminal { - kind: "interface_body".into(), + NonTerminal { + kind: "interface_body", children: vec![ - CSTNode::Terminal { - kind: "{".into(), + Terminal { + kind: "{", value: "{".into(), + start_position: Point { row: 1, column: 47 }, + end_position: Point { row: 1, column: 48 }, }, - CSTNode::NonTerminal { - kind: "method_declaration".into(), + NonTerminal { + kind: "method_declaration", children: vec![ - CSTNode::Terminal { - kind: "void_type".into(), + Terminal { + kind: "void_type", value: "void".into(), + start_position: Point { row: 2, column: 16 }, + end_position: Point { row: 2, column: 20 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "sayHello".into(), + start_position: Point { row: 2, column: 21 }, + end_position: Point { row: 2, column: 29 }, }, - CSTNode::NonTerminal { - kind: "formal_parameters".into(), + NonTerminal { + kind: "formal_parameters", children: vec![ - CSTNode::Terminal { - kind: "(".into(), + Terminal { + kind: "(", value: "(".into(), + start_position: Point { row: 2, column: 29 }, + end_position: Point { row: 2, column: 30 }, }, - CSTNode::NonTerminal { - kind: "formal_parameter".into(), + NonTerminal { + kind: "formal_parameter", children: vec![ - CSTNode::Terminal { - kind: "type_identifier".into(), + Terminal { + kind: "type_identifier", value: "String".into(), + start_position: Point { + row: 2, + column: 30, + }, + end_position: Point { row: 2, column: 36 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "name".into(), + start_position: Point { + row: 2, + column: 37, + }, + end_position: Point { row: 2, column: 41 }, }, ], + start_position: Point { row: 2, column: 30 }, + end_position: Point { row: 2, column: 41 }, }, - CSTNode::Terminal { - kind: ")".into(), + Terminal { + kind: ")", value: ")".into(), + start_position: Point { row: 2, column: 41 }, + end_position: Point { row: 2, column: 42 }, }, ], + start_position: Point { row: 2, column: 29 }, + end_position: Point { row: 2, column: 42 }, }, - CSTNode::Terminal { - kind: ";".into(), + Terminal { + kind: ";", value: ";".into(), + start_position: Point { row: 2, column: 42 }, + end_position: Point { row: 2, column: 43 }, }, ], + start_position: Point { row: 2, column: 16 }, + end_position: Point { row: 2, column: 43 }, }, - CSTNode::Terminal { - kind: "}".into(), + Terminal { + kind: "}", value: "}".into(), + start_position: Point { row: 3, column: 12 }, + end_position: Point { row: 3, column: 13 }, }, ], + start_position: Point { row: 1, column: 47 }, + end_position: Point { row: 3, column: 13 }, }, ], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 3, column: 13 }, }], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 4, column: 8 }, }; assert_eq!(expected, result.unwrap()) } @@ -149,38 +202,54 @@ mod tests { }; let result = parse_string(code, &parser_configuration); - let expected = CSTNode::NonTerminal { - kind: "program".into(), - children: vec![CSTNode::NonTerminal { - kind: "interface_declaration".into(), + let expected = NonTerminal { + kind: "program", + children: vec![NonTerminal { + kind: "interface_declaration", children: vec![ - CSTNode::NonTerminal { - kind: "modifiers".into(), + NonTerminal { + kind: "modifiers", children: vec![ - CSTNode::Terminal { - kind: "public".into(), + Terminal { + kind: "public", value: "public".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 6 }, }, - CSTNode::Terminal { - kind: "static".into(), + Terminal { + kind: "static", value: "static".into(), + start_position: Point { row: 0, column: 7 }, + end_position: Point { row: 0, column: 13 }, }, ], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 13 }, }, - CSTNode::Terminal { - kind: "interface".into(), + Terminal { + kind: "interface", value: "interface".into(), + start_position: Point { row: 0, column: 14 }, + end_position: Point { row: 0, column: 23 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "HelloWorld".into(), + start_position: Point { row: 0, column: 24 }, + end_position: Point { row: 0, column: 34 }, }, - CSTNode::Terminal { - kind: "interface_body".into(), + Terminal { + kind: "interface_body", value: "{void sayHello(String name);}".into(), + start_position: Point { row: 0, column: 35 }, + end_position: Point { row: 0, column: 64 }, }, ], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 64 }, }], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 64 }, }; assert_eq!(expected, result.unwrap()) }