diff --git a/cli/src/tests/highlight_test.rs b/cli/src/tests/highlight_test.rs index 759ee9a3..a5579c65 100644 --- a/cli/src/tests/highlight_test.rs +++ b/cli/src/tests/highlight_test.rs @@ -124,9 +124,6 @@ fn test_highlighting_injected_html_in_javascript() { fn test_highlighting_injected_javascript_in_html_mini() { let source = ""; - 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"]), + ] ], ); } diff --git a/cli/src/tests/parser_test.rs b/cli/src/tests/parser_test.rs index 882f5963..dc25e081 100644 --- a/cli/src/tests/parser_test.rs +++ b/cli/src/tests/parser_test.rs @@ -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); } diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index 1b147cf9..1d7554c0 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -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))"), diff --git a/highlight/src/lib.rs b/highlight/src/lib.rs index 4f4a930d..ec2cdafb 100644 --- a/highlight/src/lib.rs +++ b/highlight/src/lib.rs @@ -73,6 +73,7 @@ where layers: Vec>, iter_count: usize, next_event: Option, + 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)>::new(); + let mut injections = Vec::<(usize, Option<&str>, Vec, 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 )` - // 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))); diff --git a/lib/binding_web/test/parser-test.js b/lib/binding_web/test/parser-test.js index d6851539..90fdaf7b 100644 --- a/lib/binding_web/test/parser-test.js +++ b/lib/binding_web/test/parser-test.js @@ -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); diff --git a/script/fetch-fixtures b/script/fetch-fixtures index 7baf4032..1f3d9000 100755 --- a/script/fetch-fixtures +++ b/script/fetch-fixtures @@ -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