Implement include-children directive in injection queries
This commit is contained in:
parent
b3809274f0
commit
060e00463d
6 changed files with 86 additions and 43 deletions
|
|
@ -124,9 +124,6 @@ fn test_highlighting_injected_html_in_javascript() {
|
|||
fn test_highlighting_injected_javascript_in_html_mini() {
|
||||
let source = "<script>const x = new Thing();</script>";
|
||||
|
||||
eprintln!("HTML {:?}", HTML_HIGHLIGHT.language);
|
||||
eprintln!("JavaScript {:?}", JS_HIGHLIGHT.language);
|
||||
|
||||
assert_eq!(
|
||||
&to_token_vector(source, &HTML_HIGHLIGHT).unwrap(),
|
||||
&[vec![
|
||||
|
|
@ -377,7 +374,10 @@ fn test_highlighting_with_content_children_included() {
|
|||
("(", vec!["punctuation.bracket"]),
|
||||
(")", vec!["punctuation.bracket"]),
|
||||
],
|
||||
vec![(")", vec!["punctuation.bracket"]), (";", vec![]),]
|
||||
vec![
|
||||
(")", vec!["punctuation.bracket"]),
|
||||
(";", vec!["punctuation.delimiter"]),
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{thread, time};
|
|||
use tree_sitter::{InputEdit, LogType, Parser, Point, Range};
|
||||
|
||||
#[test]
|
||||
fn test_basic_parsing() {
|
||||
fn test_parsing_simple_string() {
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(get_language("rust")).unwrap();
|
||||
|
||||
|
|
@ -26,7 +26,11 @@ fn test_basic_parsing() {
|
|||
|
||||
assert_eq!(
|
||||
root_node.to_sexp(),
|
||||
"(source_file (struct_item (type_identifier) (field_declaration_list)) (function_item (identifier) (parameters) (block)))"
|
||||
concat!(
|
||||
"(source_file ",
|
||||
"(struct_item name: (type_identifier) body: (field_declaration_list)) ",
|
||||
"(function_item name: (identifier) parameters: (parameters) body: (block)))"
|
||||
)
|
||||
);
|
||||
|
||||
let struct_node = root_node.child(0).unwrap();
|
||||
|
|
@ -118,7 +122,17 @@ fn test_parsing_with_custom_utf8_input() {
|
|||
.unwrap();
|
||||
|
||||
let root = tree.root_node();
|
||||
assert_eq!(root.to_sexp(), "(source_file (function_item (visibility_modifier) (identifier) (parameters) (block (integer_literal))))");
|
||||
assert_eq!(
|
||||
root.to_sexp(),
|
||||
concat!(
|
||||
"(source_file ",
|
||||
"(function_item ",
|
||||
"(visibility_modifier) ",
|
||||
"name: (identifier) ",
|
||||
"parameters: (parameters) ",
|
||||
"body: (block (integer_literal))))"
|
||||
)
|
||||
);
|
||||
assert_eq!(root.kind(), "source_file");
|
||||
assert_eq!(root.has_error(), false);
|
||||
assert_eq!(root.child(0).unwrap().kind(), "function_item");
|
||||
|
|
@ -154,7 +168,10 @@ fn test_parsing_with_custom_utf16_input() {
|
|||
.unwrap();
|
||||
|
||||
let root = tree.root_node();
|
||||
assert_eq!(root.to_sexp(), "(source_file (function_item (visibility_modifier) (identifier) (parameters) (block (integer_literal))))");
|
||||
assert_eq!(
|
||||
root.to_sexp(),
|
||||
"(source_file (function_item (visibility_modifier) name: (identifier) parameters: (parameters) body: (block (integer_literal))))"
|
||||
);
|
||||
assert_eq!(root.kind(), "source_file");
|
||||
assert_eq!(root.has_error(), false);
|
||||
assert_eq!(root.child(0).unwrap().kind(), "function_item");
|
||||
|
|
@ -175,7 +192,10 @@ fn test_parsing_with_callback_returning_owned_strings() {
|
|||
.unwrap();
|
||||
|
||||
let root = tree.root_node();
|
||||
assert_eq!(root.to_sexp(), "(source_file (function_item (visibility_modifier) (identifier) (parameters) (block (integer_literal))))");
|
||||
assert_eq!(
|
||||
root.to_sexp(),
|
||||
"(source_file (function_item (visibility_modifier) name: (identifier) parameters: (parameters) body: (block (integer_literal))))"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -192,7 +212,7 @@ fn test_parsing_text_with_byte_order_mark() {
|
|||
.unwrap();
|
||||
assert_eq!(
|
||||
tree.root_node().to_sexp(),
|
||||
"(source_file (function_item (identifier) (parameters) (block)))"
|
||||
"(source_file (function_item name: (identifier) parameters: (parameters) body: (block)))"
|
||||
);
|
||||
assert_eq!(tree.root_node().start_byte(), 2);
|
||||
|
||||
|
|
@ -200,7 +220,7 @@ fn test_parsing_text_with_byte_order_mark() {
|
|||
let mut tree = parser.parse("\u{FEFF}fn a() {}", None).unwrap();
|
||||
assert_eq!(
|
||||
tree.root_node().to_sexp(),
|
||||
"(source_file (function_item (identifier) (parameters) (block)))"
|
||||
"(source_file (function_item name: (identifier) parameters: (parameters) body: (block)))"
|
||||
);
|
||||
assert_eq!(tree.root_node().start_byte(), 3);
|
||||
|
||||
|
|
@ -216,7 +236,7 @@ fn test_parsing_text_with_byte_order_mark() {
|
|||
let mut tree = parser.parse(" \u{FEFF}fn a() {}", Some(&tree)).unwrap();
|
||||
assert_eq!(
|
||||
tree.root_node().to_sexp(),
|
||||
"(source_file (ERROR (UNEXPECTED 65279)) (function_item (identifier) (parameters) (block)))"
|
||||
"(source_file (ERROR (UNEXPECTED 65279)) (function_item name: (identifier) parameters: (parameters) body: (block)))"
|
||||
);
|
||||
assert_eq!(tree.root_node().start_byte(), 1);
|
||||
|
||||
|
|
@ -232,7 +252,7 @@ fn test_parsing_text_with_byte_order_mark() {
|
|||
let tree = parser.parse("\u{FEFF}fn a() {}", Some(&tree)).unwrap();
|
||||
assert_eq!(
|
||||
tree.root_node().to_sexp(),
|
||||
"(source_file (function_item (identifier) (parameters) (block)))"
|
||||
"(source_file (function_item name: (identifier) parameters: (parameters) body: (block)))"
|
||||
);
|
||||
assert_eq!(tree.root_node().start_byte(), 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,13 @@ fn test_query_errors_on_invalid_syntax() {
|
|||
// Mismatched parens
|
||||
assert_eq!(
|
||||
Query::new(language, "(if_statement"),
|
||||
Err(QueryError::Syntax("Unexpected EOF".to_string()))
|
||||
Err(QueryError::Syntax(
|
||||
[
|
||||
"(if_statement", //
|
||||
" ^",
|
||||
]
|
||||
.join("\n")
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
Query::new(language, "; comment 1\n; comment 2\n (if_statement))"),
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ where
|
|||
layers: Vec<HighlightIterLayer<'a>>,
|
||||
iter_count: usize,
|
||||
next_event: Option<HighlightEvent>,
|
||||
last_highlight_range: Option<(usize, usize, usize)>,
|
||||
}
|
||||
|
||||
struct HighlightIterLayer<'a> {
|
||||
|
|
@ -252,6 +253,7 @@ impl Highlighter {
|
|||
iter_count: 0,
|
||||
layers: vec![layer],
|
||||
next_event: None,
|
||||
last_highlight_range: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -397,7 +399,8 @@ impl<'a> HighlightIterLayer<'a> {
|
|||
// First, sort scope boundaries by their byte offset in the document. At a
|
||||
// given position, emit scope endings before scope beginnings. Finally, emit
|
||||
// scope boundaries from outer layers first.
|
||||
fn sort_key(&mut self) -> Option<(usize, bool, usize)> {
|
||||
fn sort_key(&mut self) -> Option<(usize, bool, isize)> {
|
||||
let depth = -(self.depth as isize);
|
||||
let next_start = self
|
||||
.captures
|
||||
.peek()
|
||||
|
|
@ -406,13 +409,13 @@ impl<'a> HighlightIterLayer<'a> {
|
|||
match (next_start, next_end) {
|
||||
(Some(start), Some(end)) => {
|
||||
if start < end {
|
||||
Some((start, true, self.depth))
|
||||
Some((start, true, depth))
|
||||
} else {
|
||||
Some((end, false, self.depth))
|
||||
Some((end, false, depth))
|
||||
}
|
||||
}
|
||||
(Some(i), None) => Some((i, true, self.depth)),
|
||||
(None, Some(j)) => Some((j, false, self.depth)),
|
||||
(Some(i), None) => Some((i, true, depth)),
|
||||
(None, Some(j)) => Some((j, false, depth)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -589,7 +592,7 @@ where
|
|||
// captures being intermixed with other captures related to local variables
|
||||
// and syntax highlighting.
|
||||
let source = self.source;
|
||||
let mut injections = Vec::<(usize, Option<&str>, Vec<Node>)>::new();
|
||||
let mut injections = Vec::<(usize, Option<&str>, Vec<Node>, bool)>::new();
|
||||
for mat in self.injections_cursor.matches(
|
||||
&layer.config.injections_query,
|
||||
site_node,
|
||||
|
|
@ -600,7 +603,7 @@ where
|
|||
{
|
||||
entry
|
||||
} else {
|
||||
injections.push((mat.pattern_index, None, Vec::new()));
|
||||
injections.push((mat.pattern_index, None, Vec::new(), false));
|
||||
injections.last_mut().unwrap()
|
||||
};
|
||||
|
||||
|
|
@ -618,27 +621,29 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
for (pattern_index, language, _) in injections.iter_mut() {
|
||||
// In addition to specifying the language name via the text of a captured node,
|
||||
// it can also be hard-coded via a `(set! injection.language <language>)`
|
||||
// predicate.
|
||||
if language.is_none() {
|
||||
*language = layer
|
||||
.config
|
||||
.query
|
||||
.property_settings(*pattern_index)
|
||||
.iter()
|
||||
.find_map(|prop| {
|
||||
if prop.key.as_ref() == "injection.language" {
|
||||
prop.value.as_ref().map(|s| s.as_ref())
|
||||
} else {
|
||||
None
|
||||
for (pattern_index, language, _, include_children) in injections.iter_mut() {
|
||||
for prop in layer.config.query.property_settings(*pattern_index) {
|
||||
match prop.key.as_ref() {
|
||||
// In addition to specifying the language name via the text of a
|
||||
// captured node, it can also be hard-coded via a `set!` predicate
|
||||
// that sets the injection.language key.
|
||||
"injection.language" => {
|
||||
if language.is_none() {
|
||||
*language = prop.value.as_ref().map(|s| s.as_ref())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// By default, injections do not include the *children* of an
|
||||
// `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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (_, language, content_nodes) in injections {
|
||||
for (_, language, content_nodes, include_children) in injections {
|
||||
// If a language is found with the given name, then add a new language layer
|
||||
// to the highlighted document.
|
||||
if let Some(config) = language.and_then(&self.injection_callback) {
|
||||
|
|
@ -649,7 +654,8 @@ where
|
|||
self.context,
|
||||
self.cancellation_flag,
|
||||
self.layers[0].depth + 1,
|
||||
self.layers[0].intersect_ranges(&content_nodes, false),
|
||||
self.layers[0]
|
||||
.intersect_ranges(&content_nodes, include_children),
|
||||
) {
|
||||
Ok(layer) => self.insert_layer(layer),
|
||||
Err(e) => return Some(Err(e)),
|
||||
|
|
@ -736,10 +742,17 @@ where
|
|||
break;
|
||||
}
|
||||
|
||||
let mut has_highlight = true;
|
||||
if let Some((last_start, last_end, last_depth)) = self.last_highlight_range {
|
||||
if range.start == last_start && range.end == last_end && layer.depth < last_depth {
|
||||
has_highlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current node was found to be a local variable, then skip over any
|
||||
// highlighting patterns that are disabled for local variables.
|
||||
let mut has_highlight = true;
|
||||
while (definition_highlight.is_some() || reference_highlight.is_some())
|
||||
while has_highlight
|
||||
&& (definition_highlight.is_some() || reference_highlight.is_some())
|
||||
&& layer.config.non_local_variable_patterns[pattern_index]
|
||||
{
|
||||
has_highlight = false;
|
||||
|
|
@ -780,6 +793,7 @@ where
|
|||
|
||||
// Emit a scope start event and push the node's end position to the stack.
|
||||
if let Some(highlight) = reference_highlight.or(current_highlight) {
|
||||
self.last_highlight_range = Some((range.start, range.end, layer.depth));
|
||||
layer.highlight_end_stack.push(range.end);
|
||||
return self
|
||||
.emit_event(range.start, Some(HighlightEvent::HighlightStart(highlight)));
|
||||
|
|
|
|||
|
|
@ -185,7 +185,10 @@ describe("Parser", () => {
|
|||
tree = parser.parse("const x: &'static str = r###\"hello\"###;");
|
||||
assert.equal(
|
||||
tree.rootNode.toString(),
|
||||
'(source_file (const_item (identifier) (reference_type (lifetime (identifier)) (primitive_type)) (raw_string_literal)))'
|
||||
'(source_file (const_item ' +
|
||||
'name: (identifier) ' +
|
||||
'type: (reference_type (lifetime (identifier)) type: (primitive_type)) ' +
|
||||
'value: (raw_string_literal)))'
|
||||
);
|
||||
}).timeout(5000);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,5 +31,5 @@ fetch_grammar javascript highlight-queries
|
|||
fetch_grammar json master
|
||||
fetch_grammar python master
|
||||
fetch_grammar ruby master
|
||||
fetch_grammar rust master
|
||||
fetch_grammar rust highlight-queries
|
||||
fetch_grammar typescript master
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue