diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0fff462a6d5a..3b87649f942f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -11,6 +11,7 @@ on: - 'crates/**_parser/**/*.rs' - 'crates/**_formatter/**/*.rs' - 'crates/**_analyze/**/*.rs' + - 'crates/biome_grit_patterns/**/*.rs' push: branches: - main @@ -19,6 +20,7 @@ on: - 'crates/**_parser/**/*.rs' - 'crates/**_formatter/**/*.rs' - 'crates/**_analyze/**/*.rs' + - 'crates/biome_grit_patterns/**/*.rs' env: RUST_LOG: info diff --git a/crates/biome_grit_patterns/src/grit_target_language.rs b/crates/biome_grit_patterns/src/grit_target_language.rs index 00e89cfa9761..371f630d9db3 100644 --- a/crates/biome_grit_patterns/src/grit_target_language.rs +++ b/crates/biome_grit_patterns/src/grit_target_language.rs @@ -187,19 +187,32 @@ impl GritTargetLanguage { pub fn parse_snippet_contexts(&self, source: &str) -> Vec> { let source = self.substitute_metavariable_prefix(source); - self.snippet_context_strings() - .iter() - .map(|(pre, post)| self.get_parser().parse_snippet(pre, &source, post)) - .filter(|result| { - result - .tree + + let mut snippet_trees: Vec> = Vec::new(); + for (pre, post) in self.snippet_context_strings() { + let parse_result = self.get_parser().parse_snippet(pre, &source, post); + + let has_errors = parse_result + .tree + .root_node() + .descendants() + .map_or(false, |mut descendants| { + descendants.any(|descendant| descendant.kind().is_bogus()) + }); + if has_errors { + continue; + } + + if !snippet_trees.iter().any(|tree| { + tree.tree .root_node() - .descendants() - .map_or(false, |mut descendants| { - !descendants.any(|descendant| descendant.kind().is_bogus()) - }) - }) - .collect() + .matches_kinds_recursively_with(&parse_result.tree.root_node()) + }) { + snippet_trees.push(parse_result); + } + } + + snippet_trees } } diff --git a/crates/biome_grit_patterns/src/grit_target_node.rs b/crates/biome_grit_patterns/src/grit_target_node.rs index 70c051860633..fbbc60b26b3e 100644 --- a/crates/biome_grit_patterns/src/grit_target_node.rs +++ b/crates/biome_grit_patterns/src/grit_target_node.rs @@ -261,6 +261,52 @@ impl<'a> GritTargetNode<'a> { let trimmed_range = self.text_trimmed_range(); &self.source()[trimmed_range.start().into()..trimmed_range.end().into()] } + + /// Matches the `kind` of this node, and those of all its children, with + /// those of another node. + /// + /// This is a relatively cheap way to discover whether two parsed snippets + /// are identical when they were parsed from the same source string, but + /// with different contexts. In that use case, we already know the snippet + /// is essentially the same, but we only detect a meaningful difference in + /// context by looking for a variance in node kinds. + pub fn matches_kinds_recursively_with(&self, other: &Self) -> bool { + let mut cursor_a = self.walk(); + let mut cursor_b = other.walk(); + + // Are we navigating back up? If so, we shouldn't try to visit any + // children until we've visited another sibling, or we'd run in circles. + let mut up = false; + + loop { + if cursor_a.node().kind() != cursor_b.node().kind() { + break false; + } + + if !up && cursor_a.goto_first_child() { + if !cursor_b.goto_first_child() { + break false; + } + } else if cursor_a.goto_next_sibling() { + if cursor_b.goto_first_child() || !cursor_b.goto_next_sibling() { + break false; + } + + up = false; + } else if cursor_a.goto_parent() { + if (!up && cursor_b.goto_first_child()) + || cursor_b.goto_next_sibling() + || !cursor_b.goto_parent() + { + break false; + } + + up = true; + } else { + break true; + } + } + } } impl<'a> Debug for GritTargetNode<'a> {