Documentation Index Fetch the complete documentation index at: https://mintlify.com/tree-sitter/tree-sitter/llms.txt
Use this file to discover all available pages before exploring further.
Introduction
Code analysis often requires finding specific patterns in source code. Tree-sitter provides a simple yet powerful pattern-matching language for this purpose, similar to what’s used in its unit test system .
This allows you to express and search for code structures without writing complex parsing logic.
Creating a Query
Create a query by specifying a string containing one or more patterns:
TSQuery * ts_query_new (
const TSLanguage * language ,
const char * source ,
uint32_t source_len ,
uint32_t * error_offset ,
TSQueryError * error_type
);
language
const TSLanguage*
required
The language that the query is for
A string containing one or more S-expression patterns
The length of the source string in bytes
If there’s an error, this will be set to the byte offset of the error
If there’s an error, this will be set to the error type
Error Types
typedef enum {
TSQueryErrorNone = 0 ,
TSQueryErrorSyntax,
TSQueryErrorNodeType,
TSQueryErrorField,
TSQueryErrorCapture,
TSQueryErrorStructure,
TSQueryErrorLanguage,
} TSQueryError;
#include <tree_sitter/api.h>
const TSLanguage * tree_sitter_javascript ( void );
int main () {
const TSLanguage * language = tree_sitter_javascript ();
uint32_t error_offset;
TSQueryError error_type;
TSQuery * query = ts_query_new (
language,
"(function_declaration name: (identifier) @fn-name)" ,
strlen ( "(function_declaration name: (identifier) @fn-name)" ),
& error_offset,
& error_type
);
if ( ! query) {
fprintf (stderr, "Error at offset %u : %d \n " , error_offset, error_type);
return 1 ;
}
// Use the query...
ts_query_delete (query);
return 0 ;
}
Query Syntax Basics
Queries are written using S-expressions that match the structure of syntax trees:
Simple Pattern
Matches all function declarations.
Pattern with Fields
(function_declaration
name: (identifier) @function-name
body: (statement_block) @function-body)
Matches function declarations and captures the name and body with labels.
Pattern with Predicates
((identifier) @constant
(#match? @constant "^[A-Z_]+$" ))
Matches identifiers that look like constants (all uppercase).
Executing Queries
Creating a Query Cursor
The TSQuery value is immutable and can be safely shared between threads. To execute the query, create a TSQueryCursor, which carries the state needed for processing:
TSQueryCursor * ts_query_cursor_new ( void );
The query cursor should not be shared between threads, but can be reused for many query executions.
Executing the Query
Execute the query on a given syntax node:
void ts_query_cursor_exec (TSQueryCursor * , const TSQuery * , TSNode);
Iterating Over Matches
typedef struct {
TSNode node;
uint32_t index;
} TSQueryCapture;
typedef struct {
uint32_t id;
uint16_t pattern_index;
uint16_t capture_count;
const TSQueryCapture * captures;
} TSQueryMatch;
bool ts_query_cursor_next_match (TSQueryCursor * , TSQueryMatch * match );
This function returns false when there are no more matches. Otherwise, it populates the match with data about which pattern matched and which nodes were captured.
Complete Example
#include <stdio.h>
#include <string.h>
#include <tree_sitter/api.h>
const TSLanguage * tree_sitter_javascript ( void );
int main () {
// Setup parser
TSParser * parser = ts_parser_new ();
ts_parser_set_language (parser, tree_sitter_javascript ());
// Parse code
const char * source_code = "function one() { two(); function three() {} }" ;
TSTree * tree = ts_parser_parse_string (
parser, NULL , source_code, strlen (source_code)
);
// Create query
const char * query_string =
"(function_declaration name: (identifier) @fn-def) \n "
"(call_expression function: (identifier) @fn-ref)" ;
uint32_t error_offset;
TSQueryError error_type;
TSQuery * query = ts_query_new (
tree_sitter_javascript (),
query_string,
strlen (query_string),
& error_offset,
& error_type
);
// Execute query
TSQueryCursor * cursor = ts_query_cursor_new ();
ts_query_cursor_exec (cursor, query, ts_tree_root_node (tree));
// Iterate matches
TSQueryMatch match;
while ( ts_query_cursor_next_match (cursor, & match)) {
printf ( "Match pattern %d : \n " , match . pattern_index );
for ( uint32_t i = 0 ; i < match . capture_count ; i ++ ) {
TSQueryCapture capture = match . captures [i];
uint32_t length;
const char * name = ts_query_capture_name_for_id (
query, capture . index , & length
);
uint32_t start = ts_node_start_byte ( capture . node );
uint32_t end = ts_node_end_byte ( capture . node );
printf ( " @ %.*s : %.*s \n " ,
length, name,
end - start, source_code + start);
}
}
// Cleanup
ts_query_cursor_delete (cursor);
ts_query_delete (query);
ts_tree_delete (tree);
ts_parser_delete (parser);
return 0 ;
}
Iterating Over Captures
Instead of iterating over complete matches, you can iterate over individual captures in order:
bool ts_query_cursor_next_capture (
TSQueryCursor * self ,
TSQueryMatch * match ,
uint32_t * capture_index
);
This is useful when you don’t care about which pattern matched and just want a single ordered sequence of captures.
TSQueryMatch match;
uint32_t capture_index;
while ( ts_query_cursor_next_capture (cursor, & match , & capture_index )) {
TSQueryCapture capture = match . captures [capture_index];
uint32_t length;
const char * name = ts_query_capture_name_for_id (
query, capture . index , & length
);
printf ( "Capture @ %.*s \n " , length, name);
}
Setting Query Ranges
You can limit the range in which queries execute:
bool ts_query_cursor_set_byte_range (
TSQueryCursor * self ,
uint32_t start_byte ,
uint32_t end_byte
);
bool ts_query_cursor_set_point_range (
TSQueryCursor * self ,
TSPoint start_point ,
TSPoint end_point
);
// Only search within bytes 100-500
ts_query_cursor_set_byte_range (cursor, 100 , 500 );
ts_query_cursor_exec (cursor, query, ts_tree_root_node (tree));
The query cursor will return matches that intersect with the given range. A match may be returned even if some of its captures fall outside the specified range, as long as at least part of the match overlaps with the range.
Query Properties and Predicates
Queries support predicates for filtering matches:
Text Predicates
; Match identifiers equal to "foo"
((identifier) @id (#eq? @id "foo" ))
; Match identifiers matching a regex
((identifier) @const (#match? @const "^[A-Z_]+$" ))
; Match identifiers NOT equal to "bar"
((identifier) @id (#not-eq? @id "bar" ))
Property Settings
; Set properties on matches
((comment) @comment.documentation
(#match? @comment.documentation "^///" ))
const TSQueryPredicateStep * ts_query_predicates_for_pattern (
const TSQuery * self ,
uint32_t pattern_index ,
uint32_t * step_count
);
Advanced Query Features
Query Introspection
// Get counts
uint32_t ts_query_pattern_count ( const TSQuery * self );
uint32_t ts_query_capture_count ( const TSQuery * self );
uint32_t ts_query_string_count ( const TSQuery * self );
// Get pattern info
uint32_t ts_query_start_byte_for_pattern ( const TSQuery * self , uint32_t pattern_index );
uint32_t ts_query_end_byte_for_pattern ( const TSQuery * self , uint32_t pattern_index );
// Check pattern properties
bool ts_query_is_pattern_rooted ( const TSQuery * self , uint32_t pattern_index );
bool ts_query_is_pattern_non_local ( const TSQuery * self , uint32_t pattern_index );
Disabling Patterns and Captures
void ts_query_disable_capture (TSQuery * self , const char * name , uint32_t length );
void ts_query_disable_pattern (TSQuery * self , uint32_t pattern_index );
This prevents patterns or captures from matching and reduces resource usage.
Match Limits
void ts_query_cursor_set_match_limit (TSQueryCursor * self , uint32_t limit );
uint32_t ts_query_cursor_match_limit ( const TSQueryCursor * self );
bool ts_query_cursor_did_exceed_match_limit ( const TSQueryCursor * self );
Set a maximum number of in-progress matches to prevent excessive memory usage.
Next Steps
Query Syntax Learn the complete query syntax
Query Predicates Master predicates and directives
Static Node Types Generate type information for queries