diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index f1398b80..7caf5dcb 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -1238,6 +1238,45 @@ fn test_query_comments() { }); } +#[test] +fn test_query_disable_pattern() { + allocations::record(|| { + let language = get_language("javascript"); + let mut query = Query::new( + language, + " + (function_declaration + name: (identifier) @name) + (function_declaration + body: (statement_block) @body) + (class_declaration + name: (identifier) @name) + (class_declaration + body: (class_body) @body) + ", + ) + .unwrap(); + + // disable the patterns that match names + query.disable_pattern(0); + query.disable_pattern(2); + + let source = "class A { constructor() {} } function b() { return 1; }"; + let mut parser = Parser::new(); + parser.set_language(language).unwrap(); + let tree = parser.parse(source, None).unwrap(); + let mut cursor = QueryCursor::new(); + let matches = cursor.matches(&query, tree.root_node(), to_callback(source)); + assert_eq!( + collect_matches(matches, &query, source), + &[ + (3, vec![("body", "{ constructor() {} }")]), + (1, vec![("body", "{ return 1; }")]), + ], + ); + }); +} + fn collect_matches<'a>( matches: impl Iterator>, query: &'a Query, diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index f6515608..dfc280fa 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -668,6 +668,12 @@ extern "C" { arg3: u32, ); } +extern "C" { + #[doc = " Disable a certain pattern within a query. This prevents the pattern"] + #[doc = " from matching and removes most of the overhead associated with the"] + #[doc = " pattern."] + pub fn ts_query_disable_pattern(arg1: *mut TSQuery, arg2: u32); +} extern "C" { #[doc = " Create a new cursor for executing a given query."] #[doc = ""] diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 94332667..0f308d45 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -1322,6 +1322,14 @@ impl Query { } } + /// Disable a certain pattern within a query. + /// + /// This prevents the pattern from matching, and also avoids any resource usage + /// associated with the pattern. + pub fn disable_pattern(&mut self, index: usize) { + unsafe { ffi::ts_query_disable_pattern(self.ptr.as_ptr(), index as u32) } + } + fn parse_property( function_name: &str, capture_names: &[String], diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index 40187e3d..17b6d0c4 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -732,12 +732,22 @@ const char *ts_query_string_value_for_id( ); /** - * Disable a certain capture within a query. This prevents the capture - * from being returned in matches, and also avoids any resource usage - * associated with recording the capture. + * Disable a certain capture within a query. + * + * This prevents the capture from being returned in matches, and also avoids + * any resource usage associated with recording the capture. Currently, there + * is no way to undo this. */ void ts_query_disable_capture(TSQuery *, const char *, uint32_t); +/** + * Disable a certain pattern within a query. + * + * This prevents the pattern from matching and removes most of the overhead + * associated with the pattern. Currently, there is no way to undo this. + */ +void ts_query_disable_pattern(TSQuery *, uint32_t); + /** * Create a new cursor for executing a given query. * diff --git a/lib/src/query.c b/lib/src/query.c index 415c9e31..21e8bd9a 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -893,6 +893,8 @@ void ts_query_disable_capture( const char *name, uint32_t length ) { + // Remove capture information for any pattern step that previously + // captured with the given name. int id = symbol_table_id_for_name(&self->captures, name, length); if (id != -1) { for (unsigned i = 0; i < self->steps.size; i++) { @@ -901,8 +903,23 @@ void ts_query_disable_capture( step->capture_id = NONE; } } + ts_query__finalize_steps(self); + } +} + +void ts_query_disable_pattern( + TSQuery *self, + uint32_t pattern_index +) { + // Remove the given pattern from the pattern map. Its steps will still + // be in the `steps` array, but they will never be read. + for (unsigned i = 0; i < self->pattern_map.size; i++) { + PatternEntry *pattern = &self->pattern_map.contents[i]; + if (pattern->pattern_index == pattern_index) { + array_erase(&self->pattern_map, i); + i--; + } } - ts_query__finalize_steps(self); } /***************