diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index f72c4c8f..f41cdf3f 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -1425,6 +1425,7 @@ fn test_query_matches_with_too_many_permutations_to_track() { collect_matches(matches, &query, source.as_str())[0], (0, vec![("pre", "hello"), ("post", "hello")]), ); + assert_eq!(cursor.did_exceed_match_limit(), true); }); } @@ -1462,6 +1463,7 @@ fn test_query_matches_with_alternatives_and_too_many_permutations_to_track() { collect_matches(matches, &query, source.as_str()), vec![(1, vec![("method", "b")]); 50], ); + assert_eq!(cursor.did_exceed_match_limit(), true); }); } @@ -3119,6 +3121,7 @@ fn assert_query_matches( let mut cursor = QueryCursor::new(); let matches = cursor.matches(&query, tree.root_node(), to_callback(source)); assert_eq!(collect_matches(matches, &query, source), expected); + assert_eq!(cursor.did_exceed_match_limit(), false); } fn collect_matches<'a>( diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index 154ef826..a749ef98 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -720,6 +720,16 @@ extern "C" { #[doc = " Start running a given query on a given node."] pub fn ts_query_cursor_exec(arg1: *mut TSQueryCursor, arg2: *const TSQuery, arg3: TSNode); } +extern "C" { + #[doc = " Check if this cursor has exceeded its maximum number of in-progress"] + #[doc = " matches."] + #[doc = ""] + #[doc = " Currently, query cursors have a fixed capacity for storing lists"] + #[doc = " of in-progress captures. If this capacity is exceeded, then the"] + #[doc = " earliest-starting match will silently be dropped to make room for"] + #[doc = " further matches."] + pub fn ts_query_cursor_did_exceed_match_limit(arg1: *const TSQueryCursor) -> bool; +} extern "C" { #[doc = " Set the range of bytes or (row, column) positions in which the query"] #[doc = " will be executed."] diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 62a67f33..f7422c67 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -1595,6 +1595,12 @@ impl QueryCursor { QueryCursor(unsafe { NonNull::new_unchecked(ffi::ts_query_cursor_new()) }) } + /// Check if, on its last execution, this cursor exceeded its maximum number of + /// in-progress matches. + pub fn did_exceed_match_limit(&self) -> bool { + unsafe { ffi::ts_query_cursor_did_exceed_match_limit(self.0.as_ptr()) } + } + /// Iterate over all of the matches in the order that they were found. /// /// Each match contains the index of the pattern that matched, and a list of captures. diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index 3299fd20..22837035 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -791,6 +791,17 @@ void ts_query_cursor_delete(TSQueryCursor *); */ void ts_query_cursor_exec(TSQueryCursor *, const TSQuery *, TSNode); +/** + * Check if this cursor has exceeded its maximum number of in-progress + * matches. + * + * Currently, query cursors have a fixed capacity for storing lists + * of in-progress captures. If this capacity is exceeded, then the + * earliest-starting match will silently be dropped to make room for + * further matches. + */ +bool ts_query_cursor_did_exceed_match_limit(const TSQueryCursor *); + /** * Set the range of bytes or (row, column) positions in which the query * will be executed. diff --git a/lib/src/query.c b/lib/src/query.c index d28e1b92..5a20603d 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -236,6 +236,7 @@ struct TSQueryCursor { TSPoint end_point; bool ascending; bool halted; + bool did_exceed_match_limit; }; static const TSQueryError PARENT_DONE = -1; @@ -2103,6 +2104,7 @@ void ts_query_disable_pattern( TSQueryCursor *ts_query_cursor_new(void) { TSQueryCursor *self = ts_malloc(sizeof(TSQueryCursor)); *self = (TSQueryCursor) { + .did_exceed_match_limit = false, .ascending = false, .halted = false, .states = array_new(), @@ -2126,6 +2128,10 @@ void ts_query_cursor_delete(TSQueryCursor *self) { ts_free(self); } +bool ts_query_cursor_did_exceed_match_limit(const TSQueryCursor *self) { + return self->did_exceed_match_limit; +} + void ts_query_cursor_exec( TSQueryCursor *self, const TSQuery *query, @@ -2140,6 +2146,7 @@ void ts_query_cursor_exec( self->ascending = false; self->halted = false; self->query = query; + self->did_exceed_match_limit = false; } void ts_query_cursor_set_byte_range( @@ -2359,6 +2366,7 @@ static CaptureList *ts_query_cursor__prepare_to_capture( // state has captured the earliest node in the document, and steal its // capture list. if (state->capture_list_id == NONE) { + self->did_exceed_match_limit = true; uint32_t state_index, byte_offset, pattern_index; if ( ts_query_cursor__first_in_progress_capture(