diff --git a/cli/src/tags.rs b/cli/src/tags.rs index 5ea00f39..122b58d2 100644 --- a/cli/src/tags.rs +++ b/cli/src/tags.rs @@ -53,7 +53,7 @@ pub fn generate_tags( let source = fs::read(path)?; let t0 = Instant::now(); - for tag in context.generate_tags(tags_config, &source, Some(&cancellation_flag))? { + for tag in context.generate_tags(tags_config, &source, Some(&cancellation_flag))?.0 { let tag = tag?; if !quiet { write!( diff --git a/cli/src/tests/tags_test.rs b/cli/src/tests/tags_test.rs index 3ff1c92b..2b058c0b 100644 --- a/cli/src/tests/tags_test.rs +++ b/cli/src/tests/tags_test.rs @@ -102,6 +102,7 @@ fn test_tags_python() { let tags = tag_context .generate_tags(&tags_config, source, None) .unwrap() + .0 .collect::, _>>() .unwrap(); @@ -153,6 +154,7 @@ fn test_tags_javascript() { let tags = tag_context .generate_tags(&tags_config, source, None) .unwrap() + .0 .collect::, _>>() .unwrap(); @@ -189,6 +191,7 @@ fn test_tags_columns_measured_in_utf16_code_units() { let tag = tag_context .generate_tags(&tags_config, source, None) .unwrap() + .0 .next() .unwrap() .unwrap(); @@ -229,6 +232,7 @@ fn test_tags_ruby() { let tags = tag_context .generate_tags(&tags_config, source.as_bytes(), None) .unwrap() + .0 .collect::, _>>() .unwrap(); @@ -271,7 +275,7 @@ fn test_tags_cancellation() { .generate_tags(&tags_config, source.as_bytes(), Some(&cancellation_flag)) .unwrap(); - for (i, tag) in tags.enumerate() { + for (i, tag) in tags.0.enumerate() { if i == 150 { cancellation_flag.store(1, Ordering::SeqCst); } @@ -293,6 +297,39 @@ fn test_invalid_capture() { assert_eq!(e, Error::InvalidCapture("method".to_string())); } +#[test] +fn test_tags_with_parse_error() { + let language = get_language("python"); + let tags_config = TagsConfiguration::new(language, PYTHON_TAG_QUERY, "").unwrap(); + let mut tag_context = TagsContext::new(); + + let source = br#" + class Fine: pass + class Bad + "#; + + let (tags, failed) = tag_context + .generate_tags(&tags_config, source, None) + .unwrap(); + + let newtags = tags.collect::, _>>().unwrap(); + + assert!(failed, "syntax error should have been detected"); + + assert_eq!( + newtags.iter() + .map(|t| ( + substr(source, &t.name_range), + tags_config.syntax_type_name(t.syntax_type_id) + )) + .collect::>(), + &[ + ("Fine", "class"), + ] + ); +} + + #[test] fn test_tags_via_c_api() { allocations::record(|| { diff --git a/tags/include/tree_sitter/tags.h b/tags/include/tree_sitter/tags.h index f2b17075..4784abbb 100644 --- a/tags/include/tree_sitter/tags.h +++ b/tags/include/tree_sitter/tags.h @@ -88,6 +88,9 @@ uint32_t ts_tags_buffer_docs_len(const TSTagsBuffer *); // Get the syntax kinds for a scope. const char **ts_tagger_syntax_kinds_for_scope_name(const TSTagger *, const char *scope_name, uint32_t *len); +// Determine whether a parse error was encountered while tagging. +bool ts_tags_buffer_found_parse_error(const TSTagsBuffer*); + #ifdef __cplusplus } #endif diff --git a/tags/src/c_lib.rs b/tags/src/c_lib.rs index b0786580..85de1ff6 100644 --- a/tags/src/c_lib.rs +++ b/tags/src/c_lib.rs @@ -55,6 +55,7 @@ pub struct TSTagsBuffer { context: TagsContext, tags: Vec, docs: Vec, + errors_present: bool, } #[no_mangle] @@ -129,7 +130,10 @@ pub extern "C" fn ts_tagger_tag( .context .generate_tags(config, source_code, cancellation_flag) { - Ok(tags) => tags, + Ok((tags, found_error)) => { + buffer.errors_present = found_error; + tags + } Err(e) => { return match e { Error::InvalidLanguage => TSTagsError::InvalidLanguage, @@ -188,6 +192,7 @@ pub extern "C" fn ts_tags_buffer_new() -> *mut TSTagsBuffer { context: TagsContext::new(), tags: Vec::with_capacity(BUFFER_TAGS_RESERVE_CAPACITY), docs: Vec::with_capacity(BUFFER_DOCS_RESERVE_CAPACITY), + errors_present: false, })) } @@ -220,6 +225,12 @@ pub extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 { buffer.docs.len() as u32 } +#[no_mangle] +pub extern "C" fn ts_tags_buffer_found_parse_error(this: *const TSTagsBuffer) -> bool { + let buffer = unwrap_ptr(this); + buffer.errors_present +} + #[no_mangle] pub extern "C" fn ts_tagger_syntax_kinds_for_scope_name( this: *mut TSTagger, diff --git a/tags/src/lib.rs b/tags/src/lib.rs index c247c13e..dd55d4be 100644 --- a/tags/src/lib.rs +++ b/tags/src/lib.rs @@ -255,7 +255,7 @@ impl TagsContext { config: &'a TagsConfiguration, source: &'a [u8], cancellation_flag: Option<&'a AtomicUsize>, - ) -> Result> + 'a, Error> { + ) -> Result<(impl Iterator> + 'a, bool), Error> { self.parser .set_language(config.language) .map_err(|_| Error::InvalidLanguage)?; @@ -271,7 +271,7 @@ impl TagsContext { .matches(&config.query, tree_ref.root_node(), move |node| { &source[node.byte_range()] }); - Ok(TagsIter { + Ok((TagsIter { _tree: tree, matches, source, @@ -285,7 +285,7 @@ impl TagsContext { inherits: false, local_defs: Vec::new(), }], - }) + }, tree_ref.root_node().has_error())) } }