///|
priv type TSQueryCursor
///|
struct QueryCursor {
cursor : TSQueryCursor
mut query : Query
mut tree : TSTree
mut text : StringView
}
///|
extern "c" fn ts_query_cursor_new() -> TSQueryCursor = "moonbit_ts_query_cursor_new"
///|
/// Create a new cursor for executing a given query.
///
/// The cursor stores the state that is needed to iteratively search for
/// matches. To use the query cursor, first call `QueryCursor::exec`
/// to start running a given query on a given syntax node. Then, there are
/// two options for consuming the results of the query:
/// 1. Repeatedly call `QueryCursor::next_match` to iterate over all of the
/// *matches* in the order that they were found. Each match contains the
/// index of the pattern that matched, and an array of captures. Because
/// multiple patterns can match the same set of nodes, one match may contain
/// captures that appear *before* some of the captures from a previous match.
/// 2. Repeatedly call `QueryCursor::next_capture` to iterate over all of the
/// individual *captures* in the order that they appear. This is useful if
/// don't care about which pattern matched, and just want a single ordered
/// sequence of captures.
///
/// If you don't care about consuming all of the results, you can stop calling
/// `QueryCursor::next_match` or `QueryCursor::next_capture` at any point.
/// You can then start executing another query on another node by calling
/// `QueryCursor::exec` again.
pub fn QueryCursor::new() -> QueryCursor {
let cursor = ts_query_cursor_new()
let cursor = QueryCursor::{
cursor,
query: ts_query_null(),
tree: ts_tree_null(),
text: "",
}
cursor
}
///|
#borrow(cursor, query, node, tree)
extern "c" fn ts_query_cursor_exec(
cursor : TSQueryCursor,
query : Query,
node : TSNode,
tree : TSTree,
) = "moonbit_ts_query_cursor_exec"
///|
#borrow(cursor, query, node, tree)
extern "c" fn ts_query_cursor_exec_with_options(
cursor : TSQueryCursor,
query : Query,
node : TSNode,
tree : TSTree,
progress_callback : (UInt) -> Unit,
) = "moonbit_ts_query_cursor_exec_with_options"
///|
pub struct QueryCursorState {
current_byte_offset : Int
}
///|
struct QueryCursorOptions {
progress_callback : (QueryCursorState) -> Unit
}
///|
pub fn QueryCursorOptions::new(
progress_callback~ : (QueryCursorState) -> Unit,
) -> QueryCursorOptions {
QueryCursorOptions::{ progress_callback, }
}
///|
/// Start running a given query on a given node, with optional options.
pub fn QueryCursor::exec(
self : QueryCursor,
query : Query,
node : Node,
options? : QueryCursorOptions,
) -> Unit {
match options {
None => ts_query_cursor_exec(self.cursor, query, node.node, node.tree)
Some(options) =>
ts_query_cursor_exec_with_options(
self.cursor,
query,
node.node,
node.tree,
fn(current_byte_offset) {
let current_byte_offset = uint_to_int(current_byte_offset)
(options.progress_callback)(QueryCursorState::{ current_byte_offset, })
},
)
}
self.query = query
self.tree = node.tree
self.text = node.text
}
///|
#borrow(cursor)
extern "c" fn ts_query_cursor_did_exceed_match_limit(
cursor : TSQueryCursor,
) -> Bool = "moonbit_ts_query_cursor_did_exceed_match_limit"
///|
/// Check if the query cursor exceeded its match limit.
///
/// Query cursors have an optional maximum 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. This maximum capacity is optional — by default, query cursors allow
/// any number of pending matches, dynamically allocating new space for them as
/// needed as the query is executed.
pub fn QueryCursor::did_exceed_match_limit(self : QueryCursor) -> Bool {
ts_query_cursor_did_exceed_match_limit(self.cursor)
}
///|
#borrow(cursor)
extern "c" fn ts_query_cursor_match_limit(cursor : TSQueryCursor) -> UInt = "moonbit_ts_query_cursor_match_limit"
///|
/// Get the maximum number of in-progress matches allowed by this query cursor.
///
/// See `QueryCursor::did_exceed_match_limit`.
pub fn QueryCursor::match_limit(self : QueryCursor) -> Int {
ts_query_cursor_match_limit(self.cursor) |> uint_to_int()
}
///|
#borrow(cursor)
extern "c" fn ts_query_cursor_set_match_limit(
cursor : TSQueryCursor,
limit : UInt,
) = "moonbit_ts_query_cursor_set_match_limit"
///|
/// Set the maximum number of in-progress matches allowed by this query cursor.
///
/// See `QueryCursor::did_exceed_match_limit`.
pub fn QueryCursor::set_match_limit(self : QueryCursor, limit : Int) -> Unit {
ts_query_cursor_set_match_limit(self.cursor, int_to_uint(limit))
}
///|
#borrow(cursor)
extern "c" fn ts_query_cursor_set_byte_range(
cursor : TSQueryCursor,
start_byte : UInt,
end_byte : UInt,
) = "moonbit_ts_query_cursor_set_byte_range"
///|
/// Set the range of bytes in which the query will be executed.
///
/// The query cursor will return matches that intersect with the given byte range.
/// This means that 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.
pub fn QueryCursor::set_byte_range(
self : QueryCursor,
start_byte : Int,
end_byte : Int,
) -> Unit {
ts_query_cursor_set_byte_range(
self.cursor,
int_to_uint(start_byte),
int_to_uint(end_byte),
)
}
///|
#borrow(cursor, start_point, end_point)
extern "c" fn ts_query_cursor_set_point_range(
cursor : TSQueryCursor,
start_point : Point,
end_point : Point,
) = "moonbit_ts_query_cursor_set_point_range"
///|
/// Set the range of (row, column) positions in which the query will be executed.
///
/// The query cursor will return matches that intersect with the given point range.
/// This means that 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.
pub fn QueryCursor::set_point_range(
self : QueryCursor,
start_point : Point,
end_point : Point,
) -> Unit {
ts_query_cursor_set_point_range(self.cursor, start_point, end_point)
}
///|
struct QueryCapture {
query : Query
node : Node
index : Int
}
///|
pub impl Show for QueryCapture with output(self, logger) -> Unit {
logger.write_string(self.node.string().to_string())
logger.write_string(" @")
logger.write_object(self.name())
}
///|
pub impl ToJson for QueryCapture with to_json(self) -> Json {
Json::object({
"name": self.name().to_json(),
"node": self.node.to_string().to_json(),
"index": self.index.to_json(),
})
}
///|
pub fn QueryCapture::node(self : QueryCapture) -> Node {
self.node
}
///|
pub fn QueryCapture::index(self : QueryCapture) -> Int {
self.index
}
///|
pub fn QueryCapture::name(self : QueryCapture) -> String {
self.query.capture_name_for_id(self.index)
}
///|
struct QueryMatch {
query : Query
id : Int
pattern_index : Int
captures : FixedArray[QueryCapture]
}
///|
pub impl ToJson for QueryMatch with to_json(self) -> Json {
Json::object({
"id": self.id.to_json(),
"pattern_index": self.pattern_index.to_json(),
"captures": self.captures.to_json(),
})
}
///|
pub fn QueryMatch::id(self : QueryMatch) -> Int {
self.id
}
///|
pub fn QueryMatch::pattern_index(self : QueryMatch) -> Int {
self.pattern_index
}
///|
pub fn QueryMatch::captures(self : QueryMatch) -> Iter[QueryCapture] {
self.captures.iter()
}
///|
pub fn QueryMatch::predicates(self : QueryMatch) -> Array[QueryPredicate] {
self.query.predicates_for_pattern(self.pattern_index)
}
///|
priv type TSQueryMatch
///|
#borrow(query_match)
extern "c" fn ts_query_match_is_null(query_match : TSQueryMatch) -> Bool = "moonbit_c_is_null"
///|
impl Nullable for TSQueryMatch with is_null(self : TSQueryMatch) -> Bool {
return ts_query_match_is_null(self)
}
///|
#borrow(query)
extern "c" fn ts_query_match_id(query : TSQueryMatch) -> UInt = "moonbit_ts_query_match_id"
///|
#borrow(match_)
extern "c" fn ts_query_match_pattern_index(match_ : TSQueryMatch) -> UInt = "moonbit_ts_query_match_pattern_index"
///|
#borrow(query_match)
extern "c" fn ts_query_match_capture_count(
query_match : TSQueryMatch,
) -> UInt16 = "moonbit_ts_query_match_capture_count"
///|
#borrow(query_match)
extern "c" fn ts_query_match_captures_get_node(
query_match : TSQueryMatch,
index : UInt,
) -> TSNode = "moonbit_ts_query_match_captures_get_node"
///|
#borrow(query)
extern "c" fn ts_query_match_captures_get_index(
query : TSQueryMatch,
index : UInt,
) -> UInt = "moonbit_ts_query_match_captures_get_index"
///|
#borrow(cursor, query, tree)
extern "c" fn ts_query_cursor_next_match(
cursor : TSQueryCursor,
query : Query,
tree : TSTree,
) -> TSQueryMatch = "moonbit_ts_query_cursor_next_match"
///|
/// Advance to the next match of the currently running query.
///
/// If there is a match, returns Some(match).
/// Otherwise, returns None.
pub fn QueryCursor::next_match(self : QueryCursor) -> QueryMatch? {
let ts_match = ts_query_cursor_next_match(self.cursor, self.query, self.tree).to_option()
guard ts_match is Some(ts_match) else { return None }
let capture_count = ts_query_match_capture_count(ts_match)
let captures = FixedArray::makei(capture_count.to_int(), fn(i) {
let i = i.reinterpret_as_uint()
let node = ts_query_match_captures_get_node(ts_match, i)
let index = ts_query_match_captures_get_index(ts_match, i)
let index = uint_to_int(index)
QueryCapture::{
query: self.query,
node: { node, tree: self.tree, text: self.text },
index,
}
})
let id = uint_to_int(ts_query_match_id(ts_match))
let pattern_index = uint_to_int(ts_query_match_pattern_index(ts_match))
Some(QueryMatch::{ query: self.query, id, pattern_index, captures })
}
///|
pub fn QueryCursor::matches(self : QueryCursor) -> Iter[QueryMatch] {
Iter::new(fn() { self.next_match() })
}
///|
#borrow(cursor, query, tree)
extern "c" fn ts_query_cursor_remove_match(
cursor : TSQueryCursor,
query : Query,
tree : TSTree,
match_id : UInt,
) = "moonbit_ts_query_cursor_remove_match"
///|
/// Remove a match from the query cursor's results.
pub fn QueryCursor::remove_match(self : QueryCursor, match_id : Int) -> Unit {
ts_query_cursor_remove_match(
self.cursor,
self.query,
self.tree,
int_to_uint(match_id),
)
}
///|
#borrow(cursor, query, tree, match_id)
extern "c" fn ts_query_cursor_next_capture(
cursor : TSQueryCursor,
query : Query,
tree : TSTree,
match_id : FixedArray[UInt],
) -> TSQueryMatch = "moonbit_ts_query_cursor_next_capture"
///|
/// Advance to the next capture of the currently running query.
///
/// If there is a capture, returns Some(capture).
/// Otherwise, returns None.
pub fn QueryCursor::next_capture(self : QueryCursor) -> QueryCapture? {
let match_id = FixedArray::make(1, 0U)
let ts_match = ts_query_cursor_next_capture(
self.cursor,
self.query,
self.tree,
match_id,
).to_option()
guard ts_match is Some(ts_match) else { return None }
let match_id = match_id[0]
let node = ts_query_match_captures_get_node(ts_match, match_id)
let node = { node, tree: self.tree, text: self.text }
let index = ts_query_match_captures_get_index(ts_match, match_id)
let index = uint_to_int(index)
Some(QueryCapture::{ query: self.query, node, index })
}
///|
pub fn QueryCursor::captures(self : QueryCursor) -> Iter[QueryCapture] {
Iter::new(fn() { self.next_capture() })
}
///|
#borrow(cursor)
extern "c" fn ts_query_cursor_set_max_start_depth(
cursor : TSQueryCursor,
max_start_depth : UInt,
) = "moonbit_ts_query_cursor_set_max_start_depth"
///|
/// Set the maximum start depth for a query cursor.
///
/// This prevents cursors from exploring children nodes at a certain depth.
/// Note if a pattern includes many children, then they will still be checked.
///
/// The zero max start depth value can be used as a special behavior and
/// it helps to destructure a subtree by staying on a node and using captures
/// for interested parts. Note that the zero max start depth only limit a search
/// depth for a pattern's root node but other nodes that are parts of the pattern
/// may be searched at any depth what defined by the pattern structure.
///
/// Set to `@uint.max_value` to remove the maximum start depth.
pub fn QueryCursor::set_max_start_depth(
self : QueryCursor,
max_start_depth : Int,
) -> Unit {
ts_query_cursor_set_max_start_depth(self.cursor, int_to_uint(max_start_depth))
}