Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude only named children without injection.include-children #3129

Merged
merged 2 commits into from
Aug 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ impl Syntax {
);
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,
Expand All @@ -783,7 +783,7 @@ impl Syntax {
{
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));
Expand All @@ -795,15 +795,18 @@ impl Syntax {
// 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(),
RopeProvider(source_slice),
);
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,
Expand All @@ -815,16 +818,16 @@ impl Syntax {
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));
Expand Down Expand Up @@ -1422,6 +1425,19 @@ impl<'a> HighlightIterLayer<'a> {
}
}

#[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.
Expand All @@ -1434,7 +1450,7 @@ impl<'a> HighlightIterLayer<'a> {
fn intersect_ranges(
parent_ranges: &[Range],
nodes: &[Node],
includes_children: bool,
included_children: IncludedChildren,
) -> Vec<Range> {
let mut cursor = nodes[0].walk();
let mut result = Vec::new();
Expand All @@ -1458,11 +1474,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())
Expand Down Expand Up @@ -1791,7 +1811,7 @@ fn injection_for_match<'a>(
query: &'a Query,
query_match: &QueryMatch<'a, 'a>,
source: RopeSlice<'a>,
) -> (Option<Cow<'a, str>>, Option<Node<'a>>, bool) {
) -> (Option<Cow<'a, str>>, Option<Node<'a>>, IncludedChildren) {
let content_capture_index = config.injection_content_capture_index;
let language_capture_index = config.injection_language_capture_index;

Expand All @@ -1807,7 +1827,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
Expand All @@ -1823,12 +1843,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<I> {
Expand Down