A comprehensive reference for writing S-expression queries against every supported language. Each pattern is tested against the real Tree-sitter grammars used by doora.
- Query Syntax Primer
- How to Discover Node Types
- Rust
- Python
- JavaScript
- TypeScript
- Go
- C
- C++
- Cross-Language Patterns
- Predicate Reference
- Advanced Patterns
- Troubleshooting
An S-expression query mirrors the shape of the syntax tree. It matches any node of the given type that satisfies all of its children constraints.
(node_type field_name: (child_type) @capture_name)
node_type— the Tree-sitter node kind (e.g.function_item,identifier)field_name:— optional named field that constrains which child to match@capture_name— tags the matched node for extraction in output- Predicates like
(#eq? @cap "value")filter captures by text content
Run a query:
doora -q 'YOUR_QUERY_HERE' -p ./src --lang rustRun multiple queries in one pass:
doora \
-q '(function_item name: (identifier) @fn_name)' \
-q '(struct_item name: (type_identifier) @struct_name)' \
-p ./srcUse the interactive TUI to explore the CST of any file:
doora -q '(function_item)' -p ./src --tuiPress Tab to focus the AST pane. The right pane shows the full CST with node kinds, field names, and byte positions. Expand and collapse nodes with Enter.
Alternatively, the Tree-sitter playground lets you paste source code and see its parse tree interactively.
All function definitions:
doora -q '(function_item name: (identifier) @fn_name)' -p ./srcA specific function by exact name:
doora \
-q '(function_item name: (identifier) @fn (#eq? @fn "authenticate"))' \
-p ./srcFunctions matching a naming pattern:
# All functions starting with handle_
doora \
-q '(function_item name: (identifier) @fn (#match? @fn "^handle_"))' \
-p ./src
# All test functions
doora \
-q '(function_item name: (identifier) @fn (#match? @fn "^test_"))' \
-p ./src
# All get/set/update functions
doora \
-q '(function_item name: (identifier) @fn (#match? @fn "^(get|set|update)_"))' \
-p ./srcPublic functions only:
doora \
-q '(function_item
(visibility_modifier) @vis
name: (identifier) @fn_name)' \
-p ./srcAsync functions:
doora \
-q '(function_item
(function_modifiers "async")
name: (identifier) @fn_name)' \
-p ./srcUnsafe functions:
doora \
-q '(function_item
(function_modifiers "unsafe")
name: (identifier) @fn_name)' \
-p ./srcFunctions with a specific return type:
# Functions returning Result
doora \
-q '(function_item
name: (identifier) @fn_name
return_type: (generic_type type: (type_identifier) @ret (#eq? @ret "Result")))' \
-p ./src
# Functions returning bool
doora \
-q '(function_item
name: (identifier) @fn_name
return_type: (primitive_type) @ret (#eq? @ret "bool"))' \
-p ./srcFunctions with exactly zero parameters:
doora \
-q '(function_item
name: (identifier) @fn_name
parameters: (parameters . ")"))' \
-p ./srcFunctions with a specific argument count:
Tree-sitter does not expose a built-in child-count predicate. The reliable approach is to query for the parameter pattern explicitly:
# Functions with exactly one parameter (besides self)
doora \
-q '(function_item
name: (identifier) @fn_name
parameters: (parameters
. (parameter) .
")"))' \
-p ./src
# Functions with exactly two parameters
doora \
-q '(function_item
name: (identifier) @fn_name
parameters: (parameters
. (parameter) . (parameter) .
")"))' \
-p ./srcThe . node . anchor syntax means "exactly this node with nothing between the neighbors."
Methods (functions inside impl blocks):
# All methods in any impl block
doora \
-q '(impl_item
body: (declaration_list
(function_item name: (identifier) @method_name)))' \
-p ./src
# Methods on a specific type
doora \
-q '(impl_item
type: (type_identifier) @t (#eq? @t "Config")
body: (declaration_list
(function_item name: (identifier) @method_name)))' \
-p ./srcAll structs:
doora -q '(struct_item name: (type_identifier) @struct_name)' -p ./srcA specific struct:
doora \
-q '(struct_item name: (type_identifier) @s (#eq? @s "AppConfig"))' \
-p ./srcStructs with a specific field:
doora \
-q '(struct_item
name: (type_identifier) @struct_name
body: (field_declaration_list
(field_declaration
name: (field_identifier) @field (#eq? @field "timeout"))))' \
-p ./srcStruct fields of a specific type:
# Fields of type String
doora \
-q '(field_declaration
name: (field_identifier) @field_name
type: (type_identifier) @t (#eq? @t "String"))' \
-p ./src
# Fields of type Option<T>
doora \
-q '(field_declaration
name: (field_identifier) @field_name
type: (generic_type
type: (type_identifier) @t (#eq? @t "Option")))' \
-p ./src
# Fields of any Vec type
doora \
-q '(field_declaration
name: (field_identifier) @field_name
type: (generic_type
type: (type_identifier) @t (#eq? @t "Vec")))' \
-p ./srcAll enums:
doora -q '(enum_item name: (type_identifier) @enum_name)' -p ./srcEnum variants:
doora \
-q '(enum_item
name: (type_identifier) @enum_name
body: (enum_variant_list
(enum_variant name: (identifier) @variant_name)))' \
-p ./srcAll trait definitions:
doora -q '(trait_item name: (type_identifier) @trait_name)' -p ./srcAll trait implementations (impl Trait for Type):
doora \
-q '(impl_item
trait: (type_identifier) @trait_name
type: (type_identifier) @type_name)' \
-p ./srcImplementations of a specific trait:
doora \
-q '(impl_item
trait: (type_identifier) @t (#eq? @t "Display")
type: (type_identifier) @type_name)' \
-p ./srcAll type aliases:
doora -q '(type_item name: (type_identifier) @alias_name)' -p ./srcA specific type alias:
doora \
-q '(type_item name: (type_identifier) @t (#eq? @t "Result"))' \
-p ./srcAll constants:
doora -q '(const_item name: (identifier) @const_name)' -p ./srcAll static items:
doora -q '(static_item name: (identifier) @static_name)' -p ./srcAll macro invocations:
doora -q '(macro_invocation macro: (identifier) @macro_name)' -p ./srcA specific macro:
# All println! calls
doora \
-q '(macro_invocation macro: (identifier) @m (#eq? @m "println"))' \
-p ./src
# All todo! calls
doora \
-q '(macro_invocation macro: (identifier) @m (#eq? @m "todo"))' \
-p ./src
# All panic! calls
doora \
-q '(macro_invocation macro: (identifier) @m (#eq? @m "panic"))' \
-p ./src
# All unimplemented! calls
doora \
-q '(macro_invocation macro: (identifier) @m (#eq? @m "unimplemented"))' \
-p ./srcAll derive attributes:
doora -q '(attribute (identifier) @attr (#eq? @attr "derive"))' -p ./srcItems with a specific attribute:
# All #[test] functions
doora \
-q '(attribute_item (attribute (identifier) @attr (#eq? @attr "test")))' \
-p ./src
# All #[derive(Debug)] items
doora \
-q '(attribute_item
(attribute
(identifier) @attr (#eq? @attr "derive")
arguments: (token_tree (identifier) @derived (#eq? @derived "Debug"))))' \
-p ./srcAll .unwrap() calls:
doora \
-q '(call_expression
function: (field_expression
field: (field_identifier) @m (#eq? @m "unwrap")))' \
-p ./srcAll .expect() calls:
doora \
-q '(call_expression
function: (field_expression
field: (field_identifier) @m (#eq? @m "expect")))' \
-p ./srcAll .clone() calls:
doora \
-q '(call_expression
function: (field_expression
field: (field_identifier) @m (#eq? @m "clone")))' \
-p ./srcAll function calls by name:
doora \
-q '(call_expression
function: (identifier) @fn (#eq? @fn "parse_file"))' \
-p ./srcTree-sitter's Rust grammar parses // line comments as line_comment nodes and /* */ block comments as block_comment nodes. The full comment text (including the // prefix) is the node's text.
# All TODO comments
doora \
-q '(line_comment) @c (#match? @c "TODO")' \
-p ./src
# All FIXME comments
doora \
-q '(line_comment) @c (#match? @c "FIXME")' \
-p ./src
# All HACK and FIXME and TODO in one pass
doora \
-q '(line_comment) @c (#match? @c "(TODO|FIXME|HACK|XXX)")' \
-p ./src
# Block comments containing TODO
doora \
-q '(block_comment) @c (#match? @c "TODO")' \
-p ./srcAll use declarations:
doora -q '(use_declaration) @import' -p ./srcImports from a specific crate:
doora \
-q '(use_declaration (scoped_identifier path: (identifier) @crate (#eq? @crate "std")))' \
-p ./srcAll let bindings:
doora -q '(let_declaration pattern: (identifier) @var_name)' -p ./srcLet bindings with a specific type annotation:
doora \
-q '(let_declaration
pattern: (identifier) @var_name
type: (generic_type
type: (type_identifier) @t (#eq? @t "Vec")))' \
-p ./srcAll function definitions:
doora -q '(function_definition name: (identifier) @fn_name)' -p . --lang pythonTest functions:
doora \
-q '(function_definition name: (identifier) @fn (#match? @fn "^test_"))' \
-p . --lang pythonAsync functions:
doora \
-q '(function_definition
"async"
name: (identifier) @fn_name)' \
-p . --lang pythonFunctions with a specific argument count:
# Functions with exactly one parameter
doora \
-q '(function_definition
name: (identifier) @fn_name
parameters: (parameters
. (identifier) . ")"))' \
-p . --lang python
# Functions with self as the first parameter (methods)
doora \
-q '(function_definition
name: (identifier) @fn_name
parameters: (parameters
. (identifier) @first (#eq? @first "self")))' \
-p . --lang pythonFunctions with type annotations:
doora \
-q '(function_definition
name: (identifier) @fn_name
return_type: (_) @return_type)' \
-p . --lang pythonAll class definitions:
doora -q '(class_definition name: (identifier) @class_name)' -p . --lang pythonClasses inheriting from a specific base:
doora \
-q '(class_definition
name: (identifier) @class_name
superclasses: (argument_list
(identifier) @base (#eq? @base "Exception")))' \
-p . --lang pythonMethods inside classes:
doora \
-q '(class_definition
body: (block
(function_definition name: (identifier) @method_name)))' \
-p . --lang pythonClass attributes (instance variables):
doora \
-q '(class_definition
body: (block
(expression_statement
(assignment left: (identifier) @attr_name))))' \
-p . --lang pythonAll decorated definitions:
doora -q '(decorated_definition decorator: (decorator) @decorator)' -p . --lang pythonSpecific decorator:
# Functions decorated with @property
doora \
-q '(decorated_definition
decorator: (decorator (identifier) @d (#eq? @d "property"))
definition: (function_definition name: (identifier) @fn_name))' \
-p . --lang python
# Flask routes (@app.route)
doora \
-q '(decorated_definition
decorator: (decorator) @dec (#match? @dec "route")
definition: (function_definition name: (identifier) @fn_name))' \
-p . --lang pythonAll import statements:
doora -q '(import_statement) @import' -p . --lang python
doora -q '(import_from_statement) @from_import' -p . --lang pythonImport from a specific module:
doora \
-q '(import_from_statement
module_name: (dotted_name) @mod (#eq? @mod "os"))' \
-p . --lang pythondoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang pythonAll string literals:
doora -q '(string) @str' -p . --lang pythonDocstrings (first expression in a function body):
doora \
-q '(function_definition
name: (identifier) @fn_name
body: (block . (expression_statement (string) @docstring)))' \
-p . --lang pythonFunction declarations:
doora -q '(function_declaration name: (identifier) @fn_name)' -p . --lang jsArrow functions assigned to variables:
doora \
-q '(lexical_declaration
(variable_declarator
name: (identifier) @fn_name
value: (arrow_function)))' \
-p . --lang jsArrow functions with specific parameter count:
# Arrow functions with exactly one parameter
doora \
-q '(arrow_function
parameter: (identifier) @param_name)' \
-p . --lang jsAsync functions:
doora \
-q '(function_declaration
"async"
name: (identifier) @fn_name)' \
-p . --lang jsGenerator functions:
doora \
-q '(generator_function
name: (identifier) @fn_name)' \
-p . --lang jsAll class declarations:
doora -q '(class_declaration name: (identifier) @class_name)' -p . --lang jsClasses extending a specific base:
doora \
-q '(class_declaration
name: (identifier) @class_name
(class_heritage
(identifier) @base (#eq? @base "React.Component")))' \
-p . --lang jsMethod definitions:
# All methods
doora -q '(method_definition name: (property_identifier) @method_name)' -p . --lang js
# Async methods
doora \
-q '(method_definition
"async"
name: (property_identifier) @method_name)' \
-p . --lang js
# Static methods
doora \
-q '(method_definition
"static"
name: (property_identifier) @method_name)' \
-p . --lang js
# Getter methods
doora \
-q '(method_definition
"get"
name: (property_identifier) @getter_name)' \
-p . --lang jsES module imports:
doora -q '(import_declaration) @import' -p . --lang jsImport from a specific module:
doora \
-q '(import_declaration
source: (string) @src (#match? @src "react"))' \
-p . --lang jsAll exports:
doora -q '(export_statement) @export' -p . --lang jsDefault exports:
doora -q '(export_statement "default") @export' -p . --lang jsAll function calls:
doora \
-q '(call_expression function: (identifier) @fn_name)' \
-p . --lang jsConsole.log calls:
doora \
-q '(call_expression
function: (member_expression
object: (identifier) @obj (#eq? @obj "console")
property: (property_identifier) @m (#eq? @m "log")))' \
-p . --lang jsdoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang jsAll function declarations:
doora -q '(function_declaration name: (identifier) @fn_name)' -p . --lang tsGeneric functions:
doora \
-q '(function_declaration
name: (identifier) @fn_name
type_parameters: (type_parameters))' \
-p . --lang tsAsync functions:
doora \
-q '(function_declaration
"async"
name: (identifier) @fn_name)' \
-p . --lang tsAll interfaces:
doora -q '(interface_declaration name: (type_identifier) @interface_name)' -p . --lang tsA specific interface:
doora \
-q '(interface_declaration name: (type_identifier) @n (#eq? @n "Repository"))' \
-p . --lang tsInterfaces extending another:
doora \
-q '(interface_declaration
name: (type_identifier) @interface_name
(extends_type_clause
(type_identifier) @extends (#eq? @extends "BaseEntity")))' \
-p . --lang tsMethod signatures inside interfaces:
doora \
-q '(interface_declaration
name: (type_identifier) @interface_name
body: (object_type
(method_signature
name: (property_identifier) @method_name)))' \
-p . --lang tsAll type aliases:
doora -q '(type_alias_declaration name: (type_identifier) @type_name)' -p . --lang tsUnion types:
doora \
-q '(type_alias_declaration
name: (type_identifier) @type_name
value: (union_type))' \
-p . --lang tsAll enums:
doora -q '(enum_declaration name: (identifier) @enum_name)' -p . --lang tsConst enums:
doora \
-q '(enum_declaration
"const"
name: (identifier) @enum_name)' \
-p . --lang tsAll classes:
doora -q '(class_declaration name: (identifier) @class_name)' -p . --lang tsAbstract classes:
doora \
-q '(class_declaration
"abstract"
name: (identifier) @class_name)' \
-p . --lang tsClasses implementing an interface:
doora \
-q '(class_declaration
name: (identifier) @class_name
(implements_clause
(type_identifier) @interface (#eq? @interface "Repository")))' \
-p . --lang tsProperty declarations with specific types:
# Properties typed as string
doora \
-q '(public_field_definition
name: (property_identifier) @prop_name
type: (type_annotation (predefined_type) @t (#eq? @t "string")))' \
-p . --lang ts
# Optional properties (prop?: Type)
doora \
-q '(public_field_definition
name: (property_identifier) @prop_name
"?")' \
-p . --lang tsAll decorated classes:
doora \
-q '(decorator (identifier) @d)' \
-p . --lang tsSpecific decorator:
doora \
-q '(class_declaration
(decorator (identifier) @d (#eq? @d "Injectable")))' \
-p . --lang tsdoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang tsAll function declarations (not methods):
doora -q '(function_declaration name: (identifier) @fn_name)' -p . --lang goA specific function:
doora \
-q '(function_declaration name: (identifier) @fn (#eq? @fn "main"))' \
-p . --lang goFunctions with a specific naming pattern:
doora \
-q '(function_declaration name: (identifier) @fn (#match? @fn "^New"))' \
-p . --lang goFunctions with specific parameter count:
# Functions with exactly one parameter
doora \
-q '(function_declaration
name: (identifier) @fn_name
parameters: (parameter_list
. (parameter_declaration) . ")"))' \
-p . --lang go
# Functions with no parameters
doora \
-q '(function_declaration
name: (identifier) @fn_name
parameters: (parameter_list . ")"))' \
-p . --lang goFunctions returning error:
doora \
-q '(function_declaration
name: (identifier) @fn_name
result: (parameter_list
(type_identifier) @t (#eq? @t "error")))' \
-p . --lang goFunctions returning multiple values:
doora \
-q '(function_declaration
name: (identifier) @fn_name
result: (parameter_list))' \
-p . --lang goAll methods (with receivers):
doora -q '(method_declaration name: (field_identifier) @method_name)' -p . --lang goMethods on a specific type:
doora \
-q '(method_declaration
receiver: (parameter_list
(parameter_declaration
type: (type_identifier) @recv (#eq? @recv "Config")))
name: (field_identifier) @method_name)' \
-p . --lang goPointer receiver methods:
doora \
-q '(method_declaration
receiver: (parameter_list
(parameter_declaration
type: (pointer_type
(type_identifier) @recv)))
name: (field_identifier) @method_name)' \
-p . --lang goAll type declarations:
doora \
-q '(type_declaration (type_spec name: (type_identifier) @type_name))' \
-p . --lang goStruct type declarations:
doora \
-q '(type_declaration
(type_spec
name: (type_identifier) @struct_name
type: (struct_type)))' \
-p . --lang goInterface type declarations:
doora \
-q '(type_declaration
(type_spec
name: (type_identifier) @interface_name
type: (interface_type)))' \
-p . --lang goStruct fields of a specific type:
# Fields of type string
doora \
-q '(field_declaration
name: (field_identifier) @field_name
type: (type_identifier) @t (#eq? @t "string"))' \
-p . --lang go
# Fields of pointer type
doora \
-q '(field_declaration
name: (field_identifier) @field_name
type: (pointer_type))' \
-p . --lang goAll import declarations:
doora -q '(import_declaration) @import' -p . --lang goImport of a specific package:
doora \
-q '(import_spec
path: (interpreted_string_literal) @path (#match? @path "context"))' \
-p . --lang goAll if err != nil blocks:
doora \
-q '(if_statement
condition: (binary_expression
left: (identifier) @err (#eq? @err "err")
"!="
right: (nil)))' \
-p . --lang goAll error return statements:
doora \
-q '(return_statement
(expression_list
(identifier) @err (#eq? @err "err")))' \
-p . --lang godoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang goAll function definitions:
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (identifier) @fn_name))' \
-p . --lang cA specific function:
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (identifier) @fn (#eq? @fn "main")))' \
-p . --lang cFunctions matching a pattern:
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (identifier) @fn (#match? @fn "^handle_")))' \
-p . --lang cStatic functions:
doora \
-q '(function_definition
(storage_class_specifier) @s (#eq? @s "static")
declarator: (function_declarator
declarator: (identifier) @fn_name))' \
-p . --lang cFunctions with a specific return type:
# Functions returning int
doora \
-q '(function_definition
type: (primitive_type) @t (#eq? @t "int")
declarator: (function_declarator
declarator: (identifier) @fn_name))' \
-p . --lang c
# Functions returning void
doora \
-q '(function_definition
type: (primitive_type) @t (#eq? @t "void")
declarator: (function_declarator
declarator: (identifier) @fn_name))' \
-p . --lang cFunctions with specific argument count:
# Functions with exactly two parameters
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (identifier) @fn_name
parameters: (parameter_list
. (parameter_declaration) . (parameter_declaration) . ")")))' \
-p . --lang cAll struct specifiers with a name:
doora -q '(struct_specifier name: (type_identifier) @struct_name)' -p . --lang cTypedef structs:
doora \
-q '(type_definition
type: (struct_specifier)
declarator: (type_identifier) @typedef_name)' \
-p . --lang cStruct fields of a specific type:
# char* fields (strings)
doora \
-q '(field_declaration
type: (type_identifier) @t (#eq? @t "char")
declarator: (pointer_declarator
declarator: (field_identifier) @field_name))' \
-p . --lang c
# int fields
doora \
-q '(field_declaration
type: (primitive_type) @t (#eq? @t "int")
declarator: (field_identifier) @field_name)' \
-p . --lang cAll enums:
doora -q '(enum_specifier name: (type_identifier) @enum_name)' -p . --lang cAll includes:
doora -q '(preproc_include) @include' -p . --lang cInclude of a specific header:
doora \
-q '(preproc_include path: (string_literal) @path (#match? @path "stdio"))' \
-p . --lang cAll macro definitions:
doora -q '(preproc_def name: (identifier) @macro_name)' -p . --lang cMacro definitions matching a pattern:
doora \
-q '(preproc_def name: (identifier) @m (#match? @m "^MAX_"))' \
-p . --lang cFunction-like macro definitions:
doora -q '(preproc_function_def name: (identifier) @macro_name)' -p . --lang cdoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang cFree function definitions:
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (identifier) @fn_name))' \
-p . --lang cppVirtual functions:
doora \
-q '(virtual_function_definition
declarator: (function_declarator
declarator: (field_identifier) @fn_name))' \
-p . --lang cppPure virtual functions (= 0):
doora \
-q '(field_declaration
(virtual)
declarator: (function_declarator
declarator: (field_identifier) @fn_name)
(pure_virtual_clause))' \
-p . --lang cppOverride methods:
doora \
-q '(function_definition
declarator: (function_declarator
declarator: (field_identifier) @fn_name
(type_qualifier) @q (#eq? @q "override")))' \
-p . --lang cppTemplate functions:
doora \
-q '(template_declaration
(function_definition
declarator: (function_declarator
declarator: (identifier) @fn_name)))' \
-p . --lang cppAll class declarations:
doora -q '(class_specifier name: (type_identifier) @class_name)' -p . --lang cppClasses inheriting from a specific base:
doora \
-q '(class_specifier
name: (type_identifier) @class_name
(base_class_clause
(type_identifier) @base (#eq? @base "Animal")))' \
-p . --lang cppAbstract classes (contain pure virtual functions):
doora \
-q '(class_specifier
name: (type_identifier) @class_name
body: (field_declaration_list
(field_declaration
(pure_virtual_clause))))' \
-p . --lang cppAll structs:
doora -q '(struct_specifier name: (type_identifier) @struct_name)' -p . --lang cppStruct fields of a specific type:
# std::string fields
doora \
-q '(field_declaration
type: (qualified_identifier
scope: (namespace_identifier) @ns (#eq? @ns "std")
name: (type_identifier) @t (#eq? @t "string"))
declarator: (field_identifier) @field_name)' \
-p . --lang cppAll namespace declarations:
doora -q '(namespace_definition name: (namespace_identifier) @ns_name)' -p . --lang cppA specific namespace:
doora \
-q '(namespace_definition name: (namespace_identifier) @ns (#eq? @ns "detail"))' \
-p . --lang cppAll template declarations:
doora -q '(template_declaration) @template' -p . --lang cppTemplate class specializations:
doora \
-q '(template_declaration
(class_specifier name: (type_identifier) @class_name))' \
-p . --lang cppAll includes:
doora -q '(preproc_include) @include' -p . --lang cppSystem headers:
doora \
-q '(preproc_include path: (system_lib_string) @header)' \
-p . --lang cppdoora \
-q '(comment) @c (#match? @c "(TODO|FIXME|HACK)")' \
-p . --lang cppThese patterns work across multiple languages simultaneously using --lang auto (the default).
function_item compiles only for Rust. function_definition compiles only for Python. function_declaration compiles for JS, TS, Go, C, and C++. In auto mode, each query is compiled against every grammar and languages where it fails are skipped:
# Search only Rust files (function_item is Rust-specific)
doora -q '(function_item name: (identifier) @fn_name)' -p .
# Search JS, TS, Go, C, C++ files (function_declaration exists in all of them)
doora -q '(function_declaration name: (identifier) @fn_name)' -p .
# Search only Python files (function_definition is Python-specific)
doora -q '(function_definition name: (identifier) @fn_name)' -p .doora -q '(comment) @c (#match? @c "TODO")' -p .Note: Rust uses line_comment and block_comment node types. If targeting Rust specifically, use those instead:
# For Rust (includes both // and /* */ comments in one query)
doora -q '(line_comment) @c (#match? @c "TODO")' -p . --lang rust
doora -q '(block_comment) @c (#match? @c "TODO")' -p . --lang rust# Every identifier that contains "auth" anywhere in the name
doora -q '(identifier) @id (#match? @id "auth")' -p .All predicates appear inside an S-expression after the structural pattern they filter.
(function_item name: (identifier) @fn (#eq? @fn "connect"))Case-sensitive. The entire captured text must equal the string exactly.
(function_item name: (identifier) @fn (#match? @fn "^handle_"))The regex is compiled once at query compile time (not per file). Uses Rust's regex crate syntax. Anchors work as expected: ^ = start of captured text, $ = end.
Common regex patterns:
| Pattern | Matches |
|---|---|
^handle_ |
Names starting with handle_ |
_error$ |
Names ending with _error |
^(get|set|del)_ |
Names starting with get_, set_, or del_ |
[Tt]est |
Names containing Test or test |
\d+ |
Names containing digits |
^[A-Z] |
Names starting with uppercase (PascalCase) |
^[a-z] |
Names starting with lowercase |
(function_item name: (identifier) @fn (#not-eq? @fn "main"))Excludes matches where the captured text equals the string.
(function_item
name: (identifier) @fn
(#any-of? @fn "get" "set" "delete" "create" "update"))More readable than a long #match? alternation for short exact lists.
The . operator anchors position within a list. (node . child) means child is the first child. (node child .) means child is the last child.
; The first statement in a block
(block . (expression_statement) @first)
; The last statement in a block
(block (expression_statement) @last .)
; A function with exactly two parameters
(function_item
parameters: (parameters . (parameter) . (parameter) . ")")); Capture both the function item AND its name
(function_item @whole_fn
name: (identifier) @fn_name)Both @whole_fn and @fn_name appear in output. Each produces a separate result line.
Multiple predicates on the same query all must be satisfied:
; Async function AND name starts with handle_
(function_item
(function_modifiers "async")
name: (identifier) @fn
(#match? @fn "^handle_"))Multiple queries are OR'd — a file is searched with each query independently:
# Finds functions named "foo" OR structs named "Bar"
doora \
-q '(function_item name: (identifier) @fn (#eq? @fn "foo"))' \
-q '(struct_item name: (type_identifier) @s (#eq? @s "Bar"))' \
-p ./srcMatches any single node regardless of type:
; Any node named "connect" (works across different node types)
((_) @node (#eq? @node "connect")); obj.method().unwrap() — unwrap after a method call
(call_expression
function: (field_expression
value: (call_expression)
field: (field_identifier) @m (#eq? @m "unwrap"))); Rust string literal containing a specific URL
(string_literal) @s (#match? @s "https://")
; Python f-string
(interpolated_string) @fstrThe node type in your query does not exist in any supported grammar. Common causes:
- Used
function_itemin auto mode — it only exists in Rust. Add--lang rust. - Typo in node type name — node types are case-sensitive and use underscores.
- Using the wrong language flag —
function_definitionis Python, not Rust.
Debug: Use the TUI's AST pane (--tui) to see the exact node kinds for your code.
Add predicates to narrow the match:
# Too broad — matches all identifiers named "connect"
doora -q '(identifier) @id (#eq? @id "connect")' -p ./src
# Narrowed — only function definitions named "connect"
doora \
-q '(function_item name: (identifier) @fn (#eq? @fn "connect"))' \
-p ./src- Verify the node type: open the TUI and inspect the CST of a file that should match.
- Check field names:
name:is a named field infunction_itembut may differ by language. - Remove predicates one by one to see which constraint is over-filtering.
- Use
(source_file) @rootto verify the parser is processing your files at all.
- Add string literal predicates (
#eq?or#match?) so the Bloom filter index can reject non-matching files before parsing. - Build the index first:
doora index ./src - Use
--statsto see how many files the sieve is rejecting.
All column numbers are byte offsets, not character counts. For ASCII-only source this is identical to character position. For multi-byte UTF-8 source (emoji, accented characters, CJK), the column offset reflects bytes, matching how Tree-sitter and most editors with LSP integration count positions.
Find function definitions
Rust: (function_item name: (identifier) @fn_name)
Python: (function_definition name: (identifier) @fn_name)
JS/TS/Go: (function_declaration name: (identifier) @fn_name)
C/C++: (function_definition declarator: (function_declarator declarator: (identifier) @fn_name))
Find class/struct definitions
Rust: (struct_item name: (type_identifier) @name)
Python: (class_definition name: (identifier) @name)
JS/TS: (class_declaration name: (identifier) @name)
Go: (type_declaration (type_spec name: (type_identifier) @name type: (struct_type)))
C/C++: (struct_specifier name: (type_identifier) @name)
C++: (class_specifier name: (type_identifier) @name)
Find TODO comments (all languages)
(comment) @c (#match? @c "TODO")
Rust only: (line_comment) @c (#match? @c "TODO")
Find unwrap() calls (Rust)
(call_expression function: (field_expression field: (field_identifier) @m (#eq? @m "unwrap")))
Filter by name (add to any query)
Exact: (#eq? @capture "name")
Regex: (#match? @capture "^pattern")
Exclude: (#not-eq? @capture "excluded")
List: (#any-of? @capture "a" "b" "c")