From 6b244e2fefc33ce4e66c8cbd21e2b15f7ba6f179 Mon Sep 17 00:00:00 2001 From: Matthias Deiml Date: Wed, 3 Aug 2022 12:18:17 +0200 Subject: [PATCH] Exclude only named children without injection.include-children (#3129) * Exclude only named children without injection.include-children * Add injection.include-unnamed-children parameter --- helix-core/src/syntax.rs | 59 ++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 9011f835b..79570faa8 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -752,7 +752,7 @@ fn point_sub(a: Point, b: Point) -> Point { ); let mut injections = Vec::new(); for mat in matches { - let (language_name, content_node, include_children) = injection_for_match( + let (language_name, content_node, included_children) = injection_for_match( &layer.config, &layer.config.injections_query, &mat, @@ -769,7 +769,7 @@ fn point_sub(a: Point, b: Point) -> Point { { if let Some(config) = (injection_callback)(&language_name) { let ranges = - intersect_ranges(&layer.ranges, &[content_node], include_children); + intersect_ranges(&layer.ranges, &[content_node], included_children); if !ranges.is_empty() { injections.push((config, ranges)); @@ -781,7 +781,10 @@ fn point_sub(a: Point, b: Point) -> Point { // Process combined injections. if let Some(combined_injections_query) = &layer.config.combined_injections_query { let mut injections_by_pattern_index = - vec![(None, Vec::new(), false); combined_injections_query.pattern_count()]; + vec![ + (None, Vec::new(), IncludedChildren::default()); + combined_injections_query.pattern_count() + ]; let matches = cursor.matches( combined_injections_query, layer.tree().root_node(), @@ -789,7 +792,7 @@ fn point_sub(a: Point, b: Point) -> Point { ); for mat in matches { let entry = &mut injections_by_pattern_index[mat.pattern_index]; - let (language_name, content_node, include_children) = injection_for_match( + let (language_name, content_node, included_children) = injection_for_match( &layer.config, combined_injections_query, &mat, @@ -801,16 +804,16 @@ fn point_sub(a: Point, b: Point) -> Point { if let Some(content_node) = content_node { entry.1.push(content_node); } - entry.2 = include_children; + entry.2 = included_children; } - for (lang_name, content_nodes, includes_children) in injections_by_pattern_index + for (lang_name, content_nodes, included_children) in injections_by_pattern_index { if let (Some(lang_name), false) = (lang_name, content_nodes.is_empty()) { if let Some(config) = (injection_callback)(&lang_name) { let ranges = intersect_ranges( &layer.ranges, &content_nodes, - includes_children, + included_children, ); if !ranges.is_empty() { injections.push((config, ranges)); @@ -1408,6 +1411,19 @@ fn sort_key(&mut self) -> Option<(usize, bool, isize)> { } } +#[derive(Clone)] +enum IncludedChildren { + None, + All, + Unnamed, +} + +impl Default for IncludedChildren { + fn default() -> Self { + Self::None + } +} + // Compute the ranges that should be included when parsing an injection. // This takes into account three things: // * `parent_ranges` - The ranges must all fall within the *current* layer's ranges. @@ -1420,7 +1436,7 @@ fn sort_key(&mut self) -> Option<(usize, bool, isize)> { fn intersect_ranges( parent_ranges: &[Range], nodes: &[Node], - includes_children: bool, + included_children: IncludedChildren, ) -> Vec { let mut cursor = nodes[0].walk(); let mut result = Vec::new(); @@ -1444,11 +1460,15 @@ fn intersect_ranges( for excluded_range in node .children(&mut cursor) - .filter_map(|child| { - if includes_children { - None - } else { - Some(child.range()) + .filter_map(|child| match included_children { + IncludedChildren::None => Some(child.range()), + IncludedChildren::All => None, + IncludedChildren::Unnamed => { + if child.is_named() { + Some(child.range()) + } else { + None + } } }) .chain([following_range].iter().cloned()) @@ -1777,7 +1797,7 @@ fn injection_for_match<'a>( query: &'a Query, query_match: &QueryMatch<'a, 'a>, source: RopeSlice<'a>, -) -> (Option>, Option>, bool) { +) -> (Option>, Option>, IncludedChildren) { let content_capture_index = config.injection_content_capture_index; let language_capture_index = config.injection_language_capture_index; @@ -1793,7 +1813,7 @@ fn injection_for_match<'a>( } } - let mut include_children = false; + let mut included_children = IncludedChildren::default(); for prop in query.property_settings(query_match.pattern_index) { match prop.key.as_ref() { // In addition to specifying the language name via the text of a @@ -1809,12 +1829,17 @@ fn injection_for_match<'a>( // `injection.content` node - only the ranges that belong to the // node itself. This can be changed using a `#set!` predicate that // sets the `injection.include-children` key. - "injection.include-children" => include_children = true, + "injection.include-children" => included_children = IncludedChildren::All, + + // Some queries might only exclude named children but include unnamed + // children in their `injection.content` node. This can be enabled using + // a `#set!` predicate that sets the `injection.include-unnamed-children` key. + "injection.include-unnamed-children" => included_children = IncludedChildren::Unnamed, _ => {} } } - (language_name, content_node, include_children) + (language_name, content_node, included_children) } pub struct Merge {