web-tree-sitter
Version:
Tree-sitter bindings for the web
1,487 lines (1,480 loc) • 185 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/constants.ts
var SIZE_OF_SHORT = 2;
var SIZE_OF_INT = 4;
var SIZE_OF_CURSOR = 4 * SIZE_OF_INT;
var SIZE_OF_NODE = 5 * SIZE_OF_INT;
var SIZE_OF_POINT = 2 * SIZE_OF_INT;
var SIZE_OF_RANGE = 2 * SIZE_OF_INT + 2 * SIZE_OF_POINT;
var ZERO_POINT = { row: 0, column: 0 };
var INTERNAL = Symbol("INTERNAL");
function assertInternal(x) {
if (x !== INTERNAL) throw new Error("Illegal constructor");
}
__name(assertInternal, "assertInternal");
function isPoint(point) {
return !!point && typeof point.row === "number" && typeof point.column === "number";
}
__name(isPoint, "isPoint");
function setModule(module2) {
C = module2;
}
__name(setModule, "setModule");
var C;
// src/lookahead_iterator.ts
var LookaheadIterator = class {
static {
__name(this, "LookaheadIterator");
}
/** @internal */
[0] = 0;
// Internal handle for WASM
/** @internal */
language;
/** @internal */
constructor(internal, address, language) {
assertInternal(internal);
this[0] = address;
this.language = language;
}
/** Get the current symbol of the lookahead iterator. */
get currentTypeId() {
return C._ts_lookahead_iterator_current_symbol(this[0]);
}
/** Get the current symbol name of the lookahead iterator. */
get currentType() {
return this.language.types[this.currentTypeId] || "ERROR";
}
/** Delete the lookahead iterator, freeing its resources. */
delete() {
C._ts_lookahead_iterator_delete(this[0]);
this[0] = 0;
}
/**
* Reset the lookahead iterator.
*
* This returns `true` if the language was set successfully and `false`
* otherwise.
*/
reset(language, stateId) {
if (C._ts_lookahead_iterator_reset(this[0], language[0], stateId)) {
this.language = language;
return true;
}
return false;
}
/**
* Reset the lookahead iterator to another state.
*
* This returns `true` if the iterator was reset to the given state and
* `false` otherwise.
*/
resetState(stateId) {
return Boolean(C._ts_lookahead_iterator_reset_state(this[0], stateId));
}
/**
* Returns an iterator that iterates over the symbols of the lookahead iterator.
*
* The iterator will yield the current symbol name as a string for each step
* until there are no more symbols to iterate over.
*/
[Symbol.iterator]() {
return {
next: /* @__PURE__ */ __name(() => {
if (C._ts_lookahead_iterator_next(this[0])) {
return { done: false, value: this.currentType };
}
return { done: true, value: "" };
}, "next")
};
}
};
// src/tree.ts
function getText(tree, startIndex, endIndex, startPosition) {
const length = endIndex - startIndex;
let result = tree.textCallback(startIndex, startPosition);
if (result) {
startIndex += result.length;
while (startIndex < endIndex) {
const string = tree.textCallback(startIndex, startPosition);
if (string && string.length > 0) {
startIndex += string.length;
result += string;
} else {
break;
}
}
if (startIndex > endIndex) {
result = result.slice(0, length);
}
}
return result ?? "";
}
__name(getText, "getText");
var Tree = class _Tree {
static {
__name(this, "Tree");
}
/** @internal */
[0] = 0;
// Internal handle for WASM
/** @internal */
textCallback;
/** The language that was used to parse the syntax tree. */
language;
/** @internal */
constructor(internal, address, language, textCallback) {
assertInternal(internal);
this[0] = address;
this.language = language;
this.textCallback = textCallback;
}
/** Create a shallow copy of the syntax tree. This is very fast. */
copy() {
const address = C._ts_tree_copy(this[0]);
return new _Tree(INTERNAL, address, this.language, this.textCallback);
}
/** Delete the syntax tree, freeing its resources. */
delete() {
C._ts_tree_delete(this[0]);
this[0] = 0;
}
/** Get the root node of the syntax tree. */
get rootNode() {
C._ts_tree_root_node_wasm(this[0]);
return unmarshalNode(this);
}
/**
* Get the root node of the syntax tree, but with its position shifted
* forward by the given offset.
*/
rootNodeWithOffset(offsetBytes, offsetExtent) {
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
C.setValue(address, offsetBytes, "i32");
marshalPoint(address + SIZE_OF_INT, offsetExtent);
C._ts_tree_root_node_with_offset_wasm(this[0]);
return unmarshalNode(this);
}
/**
* Edit the syntax tree to keep it in sync with source code that has been
* edited.
*
* You must describe the edit both in terms of byte offsets and in terms of
* row/column coordinates.
*/
edit(edit) {
marshalEdit(edit);
C._ts_tree_edit_wasm(this[0]);
}
/** Create a new {@link TreeCursor} starting from the root of the tree. */
walk() {
return this.rootNode.walk();
}
/**
* Compare this old edited syntax tree to a new syntax tree representing
* the same document, returning a sequence of ranges whose syntactic
* structure has changed.
*
* For this to work correctly, this syntax tree must have been edited such
* that its ranges match up to the new tree. Generally, you'll want to
* call this method right after calling one of the [`Parser::parse`]
* functions. Call it on the old tree that was passed to parse, and
* pass the new tree that was returned from `parse`.
*/
getChangedRanges(other) {
if (!(other instanceof _Tree)) {
throw new TypeError("Argument must be a Tree");
}
C._ts_tree_get_changed_ranges_wasm(this[0], other[0]);
const count = C.getValue(TRANSFER_BUFFER, "i32");
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
const result = new Array(count);
if (count > 0) {
let address = buffer;
for (let i2 = 0; i2 < count; i2++) {
result[i2] = unmarshalRange(address);
address += SIZE_OF_RANGE;
}
C._free(buffer);
}
return result;
}
/** Get the included ranges that were used to parse the syntax tree. */
getIncludedRanges() {
C._ts_tree_included_ranges_wasm(this[0]);
const count = C.getValue(TRANSFER_BUFFER, "i32");
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
const result = new Array(count);
if (count > 0) {
let address = buffer;
for (let i2 = 0; i2 < count; i2++) {
result[i2] = unmarshalRange(address);
address += SIZE_OF_RANGE;
}
C._free(buffer);
}
return result;
}
};
// src/tree_cursor.ts
var TreeCursor = class _TreeCursor {
static {
__name(this, "TreeCursor");
}
/** @internal */
// @ts-expect-error: never read
[0] = 0;
// Internal handle for Wasm
/** @internal */
// @ts-expect-error: never read
[1] = 0;
// Internal handle for Wasm
/** @internal */
// @ts-expect-error: never read
[2] = 0;
// Internal handle for Wasm
/** @internal */
// @ts-expect-error: never read
[3] = 0;
// Internal handle for Wasm
/** @internal */
tree;
/** @internal */
constructor(internal, tree) {
assertInternal(internal);
this.tree = tree;
unmarshalTreeCursor(this);
}
/** Creates a deep copy of the tree cursor. This allocates new memory. */
copy() {
const copy = new _TreeCursor(INTERNAL, this.tree);
C._ts_tree_cursor_copy_wasm(this.tree[0]);
unmarshalTreeCursor(copy);
return copy;
}
/** Delete the tree cursor, freeing its resources. */
delete() {
marshalTreeCursor(this);
C._ts_tree_cursor_delete_wasm(this.tree[0]);
this[0] = this[1] = this[2] = 0;
}
/** Get the tree cursor's current {@link Node}. */
get currentNode() {
marshalTreeCursor(this);
C._ts_tree_cursor_current_node_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/**
* Get the numerical field id of this tree cursor's current node.
*
* See also {@link TreeCursor#currentFieldName}.
*/
get currentFieldId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_field_id_wasm(this.tree[0]);
}
/** Get the field name of this tree cursor's current node. */
get currentFieldName() {
return this.tree.language.fields[this.currentFieldId];
}
/**
* Get the depth of the cursor's current node relative to the original
* node that the cursor was constructed with.
*/
get currentDepth() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_depth_wasm(this.tree[0]);
}
/**
* Get the index of the cursor's current node out of all of the
* descendants of the original node that the cursor was constructed with.
*/
get currentDescendantIndex() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_descendant_index_wasm(this.tree[0]);
}
/** Get the type of the cursor's current node. */
get nodeType() {
return this.tree.language.types[this.nodeTypeId] || "ERROR";
}
/** Get the type id of the cursor's current node. */
get nodeTypeId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_type_id_wasm(this.tree[0]);
}
/** Get the state id of the cursor's current node. */
get nodeStateId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_state_id_wasm(this.tree[0]);
}
/** Get the id of the cursor's current node. */
get nodeId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_id_wasm(this.tree[0]);
}
/**
* Check if the cursor's current node is *named*.
*
* Named nodes correspond to named rules in the grammar, whereas
* *anonymous* nodes correspond to string literals in the grammar.
*/
get nodeIsNamed() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_is_named_wasm(this.tree[0]) === 1;
}
/**
* Check if the cursor's current node is *missing*.
*
* Missing nodes are inserted by the parser in order to recover from
* certain kinds of syntax errors.
*/
get nodeIsMissing() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_is_missing_wasm(this.tree[0]) === 1;
}
/** Get the string content of the cursor's current node. */
get nodeText() {
marshalTreeCursor(this);
const startIndex = C._ts_tree_cursor_start_index_wasm(this.tree[0]);
const endIndex = C._ts_tree_cursor_end_index_wasm(this.tree[0]);
C._ts_tree_cursor_start_position_wasm(this.tree[0]);
const startPosition = unmarshalPoint(TRANSFER_BUFFER);
return getText(this.tree, startIndex, endIndex, startPosition);
}
/** Get the start position of the cursor's current node. */
get startPosition() {
marshalTreeCursor(this);
C._ts_tree_cursor_start_position_wasm(this.tree[0]);
return unmarshalPoint(TRANSFER_BUFFER);
}
/** Get the end position of the cursor's current node. */
get endPosition() {
marshalTreeCursor(this);
C._ts_tree_cursor_end_position_wasm(this.tree[0]);
return unmarshalPoint(TRANSFER_BUFFER);
}
/** Get the start index of the cursor's current node. */
get startIndex() {
marshalTreeCursor(this);
return C._ts_tree_cursor_start_index_wasm(this.tree[0]);
}
/** Get the end index of the cursor's current node. */
get endIndex() {
marshalTreeCursor(this);
return C._ts_tree_cursor_end_index_wasm(this.tree[0]);
}
/**
* Move this cursor to the first child of its current node.
*
* This returns `true` if the cursor successfully moved, and returns
* `false` if there were no children.
*/
gotoFirstChild() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_first_child_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move this cursor to the last child of its current node.
*
* This returns `true` if the cursor successfully moved, and returns
* `false` if there were no children.
*
* Note that this function may be slower than
* {@link TreeCursor#gotoFirstChild} because it needs to
* iterate through all the children to compute the child's position.
*/
gotoLastChild() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_last_child_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move this cursor to the parent of its current node.
*
* This returns `true` if the cursor successfully moved, and returns
* `false` if there was no parent node (the cursor was already on the
* root node).
*
* Note that the node the cursor was constructed with is considered the root
* of the cursor, and the cursor cannot walk outside this node.
*/
gotoParent() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_parent_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move this cursor to the next sibling of its current node.
*
* This returns `true` if the cursor successfully moved, and returns
* `false` if there was no next sibling node.
*
* Note that the node the cursor was constructed with is considered the root
* of the cursor, and the cursor cannot walk outside this node.
*/
gotoNextSibling() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_next_sibling_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move this cursor to the previous sibling of its current node.
*
* This returns `true` if the cursor successfully moved, and returns
* `false` if there was no previous sibling node.
*
* Note that this function may be slower than
* {@link TreeCursor#gotoNextSibling} due to how node
* positions are stored. In the worst case, this will need to iterate
* through all the children up to the previous sibling node to recalculate
* its position. Also note that the node the cursor was constructed with is
* considered the root of the cursor, and the cursor cannot walk outside this node.
*/
gotoPreviousSibling() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_previous_sibling_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move the cursor to the node that is the nth descendant of
* the original node that the cursor was constructed with, where
* zero represents the original node itself.
*/
gotoDescendant(goalDescendantIndex) {
marshalTreeCursor(this);
C._ts_tree_cursor_goto_descendant_wasm(this.tree[0], goalDescendantIndex);
unmarshalTreeCursor(this);
}
/**
* Move this cursor to the first child of its current node that contains or
* starts after the given byte offset.
*
* This returns `true` if the cursor successfully moved to a child node, and returns
* `false` if no such child was found.
*/
gotoFirstChildForIndex(goalIndex) {
marshalTreeCursor(this);
C.setValue(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalIndex, "i32");
const result = C._ts_tree_cursor_goto_first_child_for_index_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Move this cursor to the first child of its current node that contains or
* starts after the given byte offset.
*
* This returns the index of the child node if one was found, and returns
* `null` if no such child was found.
*/
gotoFirstChildForPosition(goalPosition) {
marshalTreeCursor(this);
marshalPoint(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalPosition);
const result = C._ts_tree_cursor_goto_first_child_for_position_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
/**
* Re-initialize this tree cursor to start at the original node that the
* cursor was constructed with.
*/
reset(node) {
marshalNode(node);
marshalTreeCursor(this, TRANSFER_BUFFER + SIZE_OF_NODE);
C._ts_tree_cursor_reset_wasm(this.tree[0]);
unmarshalTreeCursor(this);
}
/**
* Re-initialize a tree cursor to the same position as another cursor.
*
* Unlike {@link TreeCursor#reset}, this will not lose parent
* information and allows reusing already created cursors.
*/
resetTo(cursor) {
marshalTreeCursor(this, TRANSFER_BUFFER);
marshalTreeCursor(cursor, TRANSFER_BUFFER + SIZE_OF_CURSOR);
C._ts_tree_cursor_reset_to_wasm(this.tree[0], cursor.tree[0]);
unmarshalTreeCursor(this);
}
};
// src/node.ts
var Node = class {
static {
__name(this, "Node");
}
/** @internal */
// @ts-expect-error: never read
[0] = 0;
// Internal handle for Wasm
/** @internal */
_children;
/** @internal */
_namedChildren;
/** @internal */
constructor(internal, {
id,
tree,
startIndex,
startPosition,
other
}) {
assertInternal(internal);
this[0] = other;
this.id = id;
this.tree = tree;
this.startIndex = startIndex;
this.startPosition = startPosition;
}
/**
* The numeric id for this node that is unique.
*
* Within a given syntax tree, no two nodes have the same id. However:
*
* * If a new tree is created based on an older tree, and a node from the old tree is reused in
* the process, then that node will have the same id in both trees.
*
* * A node not marked as having changes does not guarantee it was reused.
*
* * If a node is marked as having changed in the old tree, it will not be reused.
*/
id;
/** The byte index where this node starts. */
startIndex;
/** The position where this node starts. */
startPosition;
/** The tree that this node belongs to. */
tree;
/** Get this node's type as a numerical id. */
get typeId() {
marshalNode(this);
return C._ts_node_symbol_wasm(this.tree[0]);
}
/**
* Get the node's type as a numerical id as it appears in the grammar,
* ignoring aliases.
*/
get grammarId() {
marshalNode(this);
return C._ts_node_grammar_symbol_wasm(this.tree[0]);
}
/** Get this node's type as a string. */
get type() {
return this.tree.language.types[this.typeId] || "ERROR";
}
/**
* Get this node's symbol name as it appears in the grammar, ignoring
* aliases as a string.
*/
get grammarType() {
return this.tree.language.types[this.grammarId] || "ERROR";
}
/**
* Check if this node is *named*.
*
* Named nodes correspond to named rules in the grammar, whereas
* *anonymous* nodes correspond to string literals in the grammar.
*/
get isNamed() {
marshalNode(this);
return C._ts_node_is_named_wasm(this.tree[0]) === 1;
}
/**
* Check if this node is *extra*.
*
* Extra nodes represent things like comments, which are not required
* by the grammar, but can appear anywhere.
*/
get isExtra() {
marshalNode(this);
return C._ts_node_is_extra_wasm(this.tree[0]) === 1;
}
/**
* Check if this node represents a syntax error.
*
* Syntax errors represent parts of the code that could not be incorporated
* into a valid syntax tree.
*/
get isError() {
marshalNode(this);
return C._ts_node_is_error_wasm(this.tree[0]) === 1;
}
/**
* Check if this node is *missing*.
*
* Missing nodes are inserted by the parser in order to recover from
* certain kinds of syntax errors.
*/
get isMissing() {
marshalNode(this);
return C._ts_node_is_missing_wasm(this.tree[0]) === 1;
}
/** Check if this node has been edited. */
get hasChanges() {
marshalNode(this);
return C._ts_node_has_changes_wasm(this.tree[0]) === 1;
}
/**
* Check if this node represents a syntax error or contains any syntax
* errors anywhere within it.
*/
get hasError() {
marshalNode(this);
return C._ts_node_has_error_wasm(this.tree[0]) === 1;
}
/** Get the byte index where this node ends. */
get endIndex() {
marshalNode(this);
return C._ts_node_end_index_wasm(this.tree[0]);
}
/** Get the position where this node ends. */
get endPosition() {
marshalNode(this);
C._ts_node_end_point_wasm(this.tree[0]);
return unmarshalPoint(TRANSFER_BUFFER);
}
/** Get the string content of this node. */
get text() {
return getText(this.tree, this.startIndex, this.endIndex, this.startPosition);
}
/** Get this node's parse state. */
get parseState() {
marshalNode(this);
return C._ts_node_parse_state_wasm(this.tree[0]);
}
/** Get the parse state after this node. */
get nextParseState() {
marshalNode(this);
return C._ts_node_next_parse_state_wasm(this.tree[0]);
}
/** Check if this node is equal to another node. */
equals(other) {
return this.tree === other.tree && this.id === other.id;
}
/**
* Get the node's child at the given index, where zero represents the first child.
*
* This method is fairly fast, but its cost is technically log(n), so if
* you might be iterating over a long list of children, you should use
* {@link Node#children} instead.
*/
child(index) {
marshalNode(this);
C._ts_node_child_wasm(this.tree[0], index);
return unmarshalNode(this.tree);
}
/**
* Get this node's *named* child at the given index.
*
* See also {@link Node#isNamed}.
* This method is fairly fast, but its cost is technically log(n), so if
* you might be iterating over a long list of children, you should use
* {@link Node#namedChildren} instead.
*/
namedChild(index) {
marshalNode(this);
C._ts_node_named_child_wasm(this.tree[0], index);
return unmarshalNode(this.tree);
}
/**
* Get this node's child with the given numerical field id.
*
* See also {@link Node#childForFieldName}. You can
* convert a field name to an id using {@link Language#fieldIdForName}.
*/
childForFieldId(fieldId) {
marshalNode(this);
C._ts_node_child_by_field_id_wasm(this.tree[0], fieldId);
return unmarshalNode(this.tree);
}
/**
* Get the first child with the given field name.
*
* If multiple children may have the same field name, access them using
* {@link Node#childrenForFieldName}.
*/
childForFieldName(fieldName) {
const fieldId = this.tree.language.fields.indexOf(fieldName);
if (fieldId !== -1) return this.childForFieldId(fieldId);
return null;
}
/** Get the field name of this node's child at the given index. */
fieldNameForChild(index) {
marshalNode(this);
const address = C._ts_node_field_name_for_child_wasm(this.tree[0], index);
if (!address) return null;
return C.AsciiToString(address);
}
/** Get the field name of this node's named child at the given index. */
fieldNameForNamedChild(index) {
marshalNode(this);
const address = C._ts_node_field_name_for_named_child_wasm(this.tree[0], index);
if (!address) return null;
return C.AsciiToString(address);
}
/**
* Get an array of this node's children with a given field name.
*
* See also {@link Node#children}.
*/
childrenForFieldName(fieldName) {
const fieldId = this.tree.language.fields.indexOf(fieldName);
if (fieldId !== -1 && fieldId !== 0) return this.childrenForFieldId(fieldId);
return [];
}
/**
* Get an array of this node's children with a given field id.
*
* See also {@link Node#childrenForFieldName}.
*/
childrenForFieldId(fieldId) {
marshalNode(this);
C._ts_node_children_by_field_id_wasm(this.tree[0], fieldId);
const count = C.getValue(TRANSFER_BUFFER, "i32");
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
const result = new Array(count);
if (count > 0) {
let address = buffer;
for (let i2 = 0; i2 < count; i2++) {
result[i2] = unmarshalNode(this.tree, address);
address += SIZE_OF_NODE;
}
C._free(buffer);
}
return result;
}
/** Get the node's first child that contains or starts after the given byte offset. */
firstChildForIndex(index) {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
C.setValue(address, index, "i32");
C._ts_node_first_child_for_byte_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the node's first named child that contains or starts after the given byte offset. */
firstNamedChildForIndex(index) {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
C.setValue(address, index, "i32");
C._ts_node_first_named_child_for_byte_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get this node's number of children. */
get childCount() {
marshalNode(this);
return C._ts_node_child_count_wasm(this.tree[0]);
}
/**
* Get this node's number of *named* children.
*
* See also {@link Node#isNamed}.
*/
get namedChildCount() {
marshalNode(this);
return C._ts_node_named_child_count_wasm(this.tree[0]);
}
/** Get this node's first child. */
get firstChild() {
return this.child(0);
}
/**
* Get this node's first named child.
*
* See also {@link Node#isNamed}.
*/
get firstNamedChild() {
return this.namedChild(0);
}
/** Get this node's last child. */
get lastChild() {
return this.child(this.childCount - 1);
}
/**
* Get this node's last named child.
*
* See also {@link Node#isNamed}.
*/
get lastNamedChild() {
return this.namedChild(this.namedChildCount - 1);
}
/**
* Iterate over this node's children.
*
* If you're walking the tree recursively, you may want to use the
* {@link TreeCursor} APIs directly instead.
*/
get children() {
if (!this._children) {
marshalNode(this);
C._ts_node_children_wasm(this.tree[0]);
const count = C.getValue(TRANSFER_BUFFER, "i32");
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
this._children = new Array(count);
if (count > 0) {
let address = buffer;
for (let i2 = 0; i2 < count; i2++) {
this._children[i2] = unmarshalNode(this.tree, address);
address += SIZE_OF_NODE;
}
C._free(buffer);
}
}
return this._children;
}
/**
* Iterate over this node's named children.
*
* See also {@link Node#children}.
*/
get namedChildren() {
if (!this._namedChildren) {
marshalNode(this);
C._ts_node_named_children_wasm(this.tree[0]);
const count = C.getValue(TRANSFER_BUFFER, "i32");
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
this._namedChildren = new Array(count);
if (count > 0) {
let address = buffer;
for (let i2 = 0; i2 < count; i2++) {
this._namedChildren[i2] = unmarshalNode(this.tree, address);
address += SIZE_OF_NODE;
}
C._free(buffer);
}
}
return this._namedChildren;
}
/**
* Get the descendants of this node that are the given type, or in the given types array.
*
* The types array should contain node type strings, which can be retrieved from {@link Language#types}.
*
* Additionally, a `startPosition` and `endPosition` can be passed in to restrict the search to a byte range.
*/
descendantsOfType(types, startPosition = ZERO_POINT, endPosition = ZERO_POINT) {
if (!Array.isArray(types)) types = [types];
const symbols = [];
const typesBySymbol = this.tree.language.types;
for (const node_type of types) {
if (node_type == "ERROR") {
symbols.push(65535);
}
}
for (let i2 = 0, n = typesBySymbol.length; i2 < n; i2++) {
if (types.includes(typesBySymbol[i2])) {
symbols.push(i2);
}
}
const symbolsAddress = C._malloc(SIZE_OF_INT * symbols.length);
for (let i2 = 0, n = symbols.length; i2 < n; i2++) {
C.setValue(symbolsAddress + i2 * SIZE_OF_INT, symbols[i2], "i32");
}
marshalNode(this);
C._ts_node_descendants_of_type_wasm(
this.tree[0],
symbolsAddress,
symbols.length,
startPosition.row,
startPosition.column,
endPosition.row,
endPosition.column
);
const descendantCount = C.getValue(TRANSFER_BUFFER, "i32");
const descendantAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
const result = new Array(descendantCount);
if (descendantCount > 0) {
let address = descendantAddress;
for (let i2 = 0; i2 < descendantCount; i2++) {
result[i2] = unmarshalNode(this.tree, address);
address += SIZE_OF_NODE;
}
}
C._free(descendantAddress);
C._free(symbolsAddress);
return result;
}
/** Get this node's next sibling. */
get nextSibling() {
marshalNode(this);
C._ts_node_next_sibling_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get this node's previous sibling. */
get previousSibling() {
marshalNode(this);
C._ts_node_prev_sibling_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/**
* Get this node's next *named* sibling.
*
* See also {@link Node#isNamed}.
*/
get nextNamedSibling() {
marshalNode(this);
C._ts_node_next_named_sibling_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/**
* Get this node's previous *named* sibling.
*
* See also {@link Node#isNamed}.
*/
get previousNamedSibling() {
marshalNode(this);
C._ts_node_prev_named_sibling_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the node's number of descendants, including one for the node itself. */
get descendantCount() {
marshalNode(this);
return C._ts_node_descendant_count_wasm(this.tree[0]);
}
/**
* Get this node's immediate parent.
* Prefer {@link Node#childWithDescendant} for iterating over this node's ancestors.
*/
get parent() {
marshalNode(this);
C._ts_node_parent_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/**
* Get the node that contains `descendant`.
*
* Note that this can return `descendant` itself.
*/
childWithDescendant(descendant) {
marshalNode(this);
marshalNode(descendant, 1);
C._ts_node_child_with_descendant_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the smallest node within this node that spans the given byte range. */
descendantForIndex(start2, end = start2) {
if (typeof start2 !== "number" || typeof end !== "number") {
throw new Error("Arguments must be numbers");
}
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
C.setValue(address, start2, "i32");
C.setValue(address + SIZE_OF_INT, end, "i32");
C._ts_node_descendant_for_index_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the smallest named node within this node that spans the given byte range. */
namedDescendantForIndex(start2, end = start2) {
if (typeof start2 !== "number" || typeof end !== "number") {
throw new Error("Arguments must be numbers");
}
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
C.setValue(address, start2, "i32");
C.setValue(address + SIZE_OF_INT, end, "i32");
C._ts_node_named_descendant_for_index_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the smallest node within this node that spans the given point range. */
descendantForPosition(start2, end = start2) {
if (!isPoint(start2) || !isPoint(end)) {
throw new Error("Arguments must be {row, column} objects");
}
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
marshalPoint(address, start2);
marshalPoint(address + SIZE_OF_POINT, end);
C._ts_node_descendant_for_position_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/** Get the smallest named node within this node that spans the given point range. */
namedDescendantForPosition(start2, end = start2) {
if (!isPoint(start2) || !isPoint(end)) {
throw new Error("Arguments must be {row, column} objects");
}
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
marshalPoint(address, start2);
marshalPoint(address + SIZE_OF_POINT, end);
C._ts_node_named_descendant_for_position_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
/**
* Create a new {@link TreeCursor} starting from this node.
*
* Note that the given node is considered the root of the cursor,
* and the cursor cannot walk outside this node.
*/
walk() {
marshalNode(this);
C._ts_tree_cursor_new_wasm(this.tree[0]);
return new TreeCursor(INTERNAL, this.tree);
}
/**
* Edit this node to keep it in-sync with source code that has been edited.
*
* This function is only rarely needed. When you edit a syntax tree with
* the {@link Tree#edit} method, all of the nodes that you retrieve from
* the tree afterward will already reflect the edit. You only need to
* use {@link Node#edit} when you have a specific {@link Node} instance that
* you want to keep and continue to use after an edit.
*/
edit(edit) {
if (this.startIndex >= edit.oldEndIndex) {
this.startIndex = edit.newEndIndex + (this.startIndex - edit.oldEndIndex);
let subbedPointRow;
let subbedPointColumn;
if (this.startPosition.row > edit.oldEndPosition.row) {
subbedPointRow = this.startPosition.row - edit.oldEndPosition.row;
subbedPointColumn = this.startPosition.column;
} else {
subbedPointRow = 0;
subbedPointColumn = this.startPosition.column;
if (this.startPosition.column >= edit.oldEndPosition.column) {
subbedPointColumn = this.startPosition.column - edit.oldEndPosition.column;
}
}
if (subbedPointRow > 0) {
this.startPosition.row += subbedPointRow;
this.startPosition.column = subbedPointColumn;
} else {
this.startPosition.column += subbedPointColumn;
}
} else if (this.startIndex > edit.startIndex) {
this.startIndex = edit.newEndIndex;
this.startPosition.row = edit.newEndPosition.row;
this.startPosition.column = edit.newEndPosition.column;
}
}
/** Get the S-expression representation of this node. */
toString() {
marshalNode(this);
const address = C._ts_node_to_string_wasm(this.tree[0]);
const result = C.AsciiToString(address);
C._free(address);
return result;
}
};
// src/marshal.ts
function unmarshalCaptures(query, tree, address, patternIndex, result) {
for (let i2 = 0, n = result.length; i2 < n; i2++) {
const captureIndex = C.getValue(address, "i32");
address += SIZE_OF_INT;
const node = unmarshalNode(tree, address);
address += SIZE_OF_NODE;
result[i2] = { patternIndex, name: query.captureNames[captureIndex], node };
}
return address;
}
__name(unmarshalCaptures, "unmarshalCaptures");
function marshalNode(node, index = 0) {
let address = TRANSFER_BUFFER + index * SIZE_OF_NODE;
C.setValue(address, node.id, "i32");
address += SIZE_OF_INT;
C.setValue(address, node.startIndex, "i32");
address += SIZE_OF_INT;
C.setValue(address, node.startPosition.row, "i32");
address += SIZE_OF_INT;
C.setValue(address, node.startPosition.column, "i32");
address += SIZE_OF_INT;
C.setValue(address, node[0], "i32");
}
__name(marshalNode, "marshalNode");
function unmarshalNode(tree, address = TRANSFER_BUFFER) {
const id = C.getValue(address, "i32");
address += SIZE_OF_INT;
if (id === 0) return null;
const index = C.getValue(address, "i32");
address += SIZE_OF_INT;
const row = C.getValue(address, "i32");
address += SIZE_OF_INT;
const column = C.getValue(address, "i32");
address += SIZE_OF_INT;
const other = C.getValue(address, "i32");
const result = new Node(INTERNAL, {
id,
tree,
startIndex: index,
startPosition: { row, column },
other
});
return result;
}
__name(unmarshalNode, "unmarshalNode");
function marshalTreeCursor(cursor, address = TRANSFER_BUFFER) {
C.setValue(address + 0 * SIZE_OF_INT, cursor[0], "i32");
C.setValue(address + 1 * SIZE_OF_INT, cursor[1], "i32");
C.setValue(address + 2 * SIZE_OF_INT, cursor[2], "i32");
C.setValue(address + 3 * SIZE_OF_INT, cursor[3], "i32");
}
__name(marshalTreeCursor, "marshalTreeCursor");
function unmarshalTreeCursor(cursor) {
cursor[0] = C.getValue(TRANSFER_BUFFER + 0 * SIZE_OF_INT, "i32");
cursor[1] = C.getValue(TRANSFER_BUFFER + 1 * SIZE_OF_INT, "i32");
cursor[2] = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, "i32");
cursor[3] = C.getValue(TRANSFER_BUFFER + 3 * SIZE_OF_INT, "i32");
}
__name(unmarshalTreeCursor, "unmarshalTreeCursor");
function marshalPoint(address, point) {
C.setValue(address, point.row, "i32");
C.setValue(address + SIZE_OF_INT, point.column, "i32");
}
__name(marshalPoint, "marshalPoint");
function unmarshalPoint(address) {
const result = {
row: C.getValue(address, "i32") >>> 0,
column: C.getValue(address + SIZE_OF_INT, "i32") >>> 0
};
return result;
}
__name(unmarshalPoint, "unmarshalPoint");
function marshalRange(address, range) {
marshalPoint(address, range.startPosition);
address += SIZE_OF_POINT;
marshalPoint(address, range.endPosition);
address += SIZE_OF_POINT;
C.setValue(address, range.startIndex, "i32");
address += SIZE_OF_INT;
C.setValue(address, range.endIndex, "i32");
address += SIZE_OF_INT;
}
__name(marshalRange, "marshalRange");
function unmarshalRange(address) {
const result = {};
result.startPosition = unmarshalPoint(address);
address += SIZE_OF_POINT;
result.endPosition = unmarshalPoint(address);
address += SIZE_OF_POINT;
result.startIndex = C.getValue(address, "i32") >>> 0;
address += SIZE_OF_INT;
result.endIndex = C.getValue(address, "i32") >>> 0;
return result;
}
__name(unmarshalRange, "unmarshalRange");
function marshalEdit(edit, address = TRANSFER_BUFFER) {
marshalPoint(address, edit.startPosition);
address += SIZE_OF_POINT;
marshalPoint(address, edit.oldEndPosition);
address += SIZE_OF_POINT;
marshalPoint(address, edit.newEndPosition);
address += SIZE_OF_POINT;
C.setValue(address, edit.startIndex, "i32");
address += SIZE_OF_INT;
C.setValue(address, edit.oldEndIndex, "i32");
address += SIZE_OF_INT;
C.setValue(address, edit.newEndIndex, "i32");
address += SIZE_OF_INT;
}
__name(marshalEdit, "marshalEdit");
function unmarshalLanguageMetadata(address) {
const major_version = C.getValue(address, "i32");
const minor_version = C.getValue(address += SIZE_OF_INT, "i32");
const patch_version = C.getValue(address += SIZE_OF_INT, "i32");
return { major_version, minor_version, patch_version };
}
__name(unmarshalLanguageMetadata, "unmarshalLanguageMetadata");
// src/query.ts
var PREDICATE_STEP_TYPE_CAPTURE = 1;
var PREDICATE_STEP_TYPE_STRING = 2;
var QUERY_WORD_REGEX = /[\w-]+/g;
var CaptureQuantifier = {
Zero: 0,
ZeroOrOne: 1,
ZeroOrMore: 2,
One: 3,
OneOrMore: 4
};
var isCaptureStep = /* @__PURE__ */ __name((step) => step.type === "capture", "isCaptureStep");
var isStringStep = /* @__PURE__ */ __name((step) => step.type === "string", "isStringStep");
var QueryErrorKind = {
Syntax: 1,
NodeName: 2,
FieldName: 3,
CaptureName: 4,
PatternStructure: 5
};
var QueryError = class _QueryError extends Error {
constructor(kind, info2, index, length) {
super(_QueryError.formatMessage(kind, info2));
this.kind = kind;
this.info = info2;
this.index = index;
this.length = length;
this.name = "QueryError";
}
static {
__name(this, "QueryError");
}
/** Formats an error message based on the error kind and info */
static formatMessage(kind, info2) {
switch (kind) {
case QueryErrorKind.NodeName:
return `Bad node name '${info2.word}'`;
case QueryErrorKind.FieldName:
return `Bad field name '${info2.word}'`;
case QueryErrorKind.CaptureName:
return `Bad capture name @${info2.word}`;
case QueryErrorKind.PatternStructure:
return `Bad pattern structure at offset ${info2.suffix}`;
case QueryErrorKind.Syntax:
return `Bad syntax at offset ${info2.suffix}`;
}
}
};
function parseAnyPredicate(steps, index, operator, textPredicates) {
if (steps.length !== 3) {
throw new Error(
`Wrong number of arguments to \`#${operator}\` predicate. Expected 2, got ${steps.length - 1}`
);
}
if (!isCaptureStep(steps[1])) {
throw new Error(
`First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}"`
);
}
const isPositive = operator === "eq?" || operator === "any-eq?";
const matchAll = !operator.startsWith("any-");
if (isCaptureStep(steps[2])) {
const captureName1 = steps[1].name;
const captureName2 = steps[2].name;
textPredicates[index].push((captures) => {
const nodes1 = [];
const nodes2 = [];
for (const c of captures) {
if (c.name === captureName1) nodes1.push(c.node);
if (c.name === captureName2) nodes2.push(c.node);
}
const compare = /* @__PURE__ */ __name((n1, n2, positive) => {
return positive ? n1.text === n2.text : n1.text !== n2.text;
}, "compare");
return matchAll ? nodes1.every((n1) => nodes2.some((n2) => compare(n1, n2, isPositive))) : nodes1.some((n1) => nodes2.some((n2) => compare(n1, n2, isPositive)));
});
} else {
const captureName = steps[1].name;
const stringValue = steps[2].value;
const matches = /* @__PURE__ */ __name((n) => n.text === stringValue, "matches");
const doesNotMatch = /* @__PURE__ */ __name((n) => n.text !== stringValue, "doesNotMatch");
textPredicates[index].push((captures) => {
const nodes = [];
for (const c of captures) {
if (c.name === captureName) nodes.push(c.node);
}
const test = isPositive ? matches : doesNotMatch;
return matchAll ? nodes.every(test) : nodes.some(test);
});
}
}
__name(parseAnyPredicate, "parseAnyPredicate");
function parseMatchPredicate(steps, index, operator, textPredicates) {
if (steps.length !== 3) {
throw new Error(
`Wrong number of arguments to \`#${operator}\` predicate. Expected 2, got ${steps.length - 1}.`
);
}
if (steps[1].type !== "capture") {
throw new Error(
`First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
);
}
if (steps[2].type !== "string") {
throw new Error(
`Second argument of \`#${operator}\` predicate must be a string. Got @${steps[2].name}.`
);
}
const isPositive = operator === "match?" || operator === "any-match?";
const matchAll = !operator.startsWith("any-");
const captureName = steps[1].name;
const regex = new RegExp(steps[2].value);
textPredicates[index].push((captures) => {
const nodes = [];
for (const c of captures) {
if (c.name === captureName) nodes.push(c.node.text);
}
const test = /* @__PURE__ */ __name((text, positive) => {
return positive ? regex.test(text) : !regex.test(text);
}, "test");
if (nodes.length === 0) return !isPositive;
return matchAll ? nodes.every((text) => test(text, isPositive)) : nodes.some((text) => test(text, isPositive));
});
}
__name(parseMatchPredicate, "parseMatchPredicate");
function parseAnyOfPredicate(steps, index, operator, textPredicates) {
if (steps.length < 2) {
throw new Error(
`Wrong number of arguments to \`#${operator}\` predicate. Expected at least 1. Got ${steps.length - 1}.`
);
}
if (steps[1].type !== "capture") {
throw new Error(
`First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
);
}
const isPositive = operator === "any-of?";
const captureName = steps[1].name;
const stringSteps = steps.slice(2);
if (!stringSteps.every(isStringStep)) {
throw new Error(
`Arguments to \`#${operator}\` predicate must be strings.".`
);
}
const values = stringSteps.map((s) => s.value);
textPredicates[index].push((captures) => {
const nodes = [];
for (const c of captures) {
if (c.name === captureName) nodes.push(c.node.text);
}
if (nodes.length === 0) return !isPositive;
return nodes.every((text) => values.includes(text)) === isPositive;
});
}
__name(parseAnyOfPredicate, "parseAnyOfPredicate");
function parseIsPredicate(steps, index, operator, assertedProperties, refutedProperties) {
if (steps.length < 2 || steps.length > 3) {
throw new Error(
`Wrong number of arguments to \`#${operator}\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
);
}
if (!steps.every(isStringStep)) {
throw new Error(
`Arguments to \`#${operator}\` predicate must be strings.".`
);
}
const properties = operator === "is?" ? assertedProperties : refutedProperties;
if (!properties[index]) properties[index] = {};
properties[index][steps[1].value] = steps[2]?.value ?? null;
}
__name(parseIsPredicate, "parseIsPredicate");
function parseSetDirective(steps, index, setProperties) {
if (steps.length < 2 || steps.length > 3) {
throw new Error(`Wrong number of arguments to \`#set!\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`);
}
if (!steps.every(isStringStep)) {
throw new Error(`Arguments to \`#set!\` predicate must be strings.".`);
}
if (!setProperties[index]) setProperties[index] = {};
setProperties[index][steps[1].value] = steps[2]?.value ?? null;
}
__name(parseSetDirective, "parseSetDirective");
function parsePattern(index, stepType, stepValueId, captureNames, stringValues, steps, textPredicates, predicates, setProperties, assertedProperties, refutedProperties) {
if (stepType === PREDICATE_STEP_TYPE_CAPTURE) {
const name2 = captureNames[stepValueId];
steps.push({ type: "capture", name: name2 });
} else if (stepType === PREDICATE_STEP_TYPE_STRING) {
steps.push({ type: "string", value: stringValues[stepValueId] });
} else if (steps.length > 0) {
if (steps[0].type !== "string") {
throw new Error("Predicates must begin with a literal value");
}
const operator = steps[0].value;
switch (operator) {
case "any-not-eq?":
case "not-eq?":
case "any-eq?":
case "eq?":
parseAnyPredicate(steps, index, operator, textPredicates);
break;
case "any-not-match?":
case "not-match?":
case "any-match?":
case "match?":
parseMatchPredicate(steps, index, operator, textPredicates);
break;
case "not-any-of?":
case "any-of?":
parseAnyOfPredicate(steps, index, operator, textPredicates);
break;
case "is?":
case "is-not?":
parseIsPredicate(steps, index, operator, assertedProperties, refutedProperties);
break;
case "set!":
parseSetDirective(steps, index, setProperties);
break;
default:
predicates[index].push({ operator, operands: steps.slice(1) });
}
steps.length = 0;
}
}
__name(parsePattern, "parsePattern");
var Query = class {
static {
__name(this, "Query");
}
/** @internal */
[0] = 0;
// Internal handle for WASM
/** @internal */
exceededMatchLimit;
/** @internal */
textPredicates;
/** The names of the captures used in the query. */
captureNames;
/** The quantifiers of the captures used in the query. */
captureQuantifiers;
/**
* The other user-defined predicates associated with the given index.
*
* This includes predicates with operators other than:
* - `match?`
* - `eq?` and `not-eq?`
* - `any-of?` and `not-any-of?`
* - `is?` and `is-not?`
* - `set!`
*/
predicates;
/** The properties for predicates with the operator `set!`. */
setProperties;
/** The properties for predicates with the operator `is?`. */
assertedProperties;
/** The properties for predicates with the operator `is-not?`. */
refutedProperties;
/** The maximum number of in-progress matches for this cursor. */
matchLimit;
/**
* Create a new query from a string containing one or more S-expression
* patterns.
*
* The query is associated with a particular language, and can only be run
* on syntax nodes parsed with that language. References to Queries can be
* shared between multiple threads.
*
* @link {@see https://tree-sitter.github.io/tree-sitter/using-parsers/queries}
*/
constructor(language, source) {
const sourceLength = C.lengthBytesUTF8(source);
const sourceAddress = C._malloc(sourceLength + 1);
C.stringToUTF8(source, sourceAddress, sourceLength + 1);
const address = C._ts_query_new(
language[0],
sourceAddress,
sourceLength,
TRANSFER_BUFFER,
TRANSFER_BUFFER + SIZE_OF_INT
);
if (!address) {
const errorId = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
const errorByte = C.getValue(TRANSFER_BUFFER, "i32");
const errorIndex = C.UTF8ToString(sourceAddress, errorByte).length;
const suffix = source.slice(errorIndex, errorIndex + 100).split("\n")[0];
const word = suffix.match(QUERY_WORD_REGEX)?.[0] ?? "";
C._free(sourceAddress);
switch (errorId) {
case QueryErrorKind.Syntax:
throw new QueryError(QueryErrorKind.Syntax, { suffix: `${errorIndex}: '${suffix}'...` }, errorIndex, 0);
case QueryErrorKind.NodeName:
throw new QueryError(errorId, { word }, errorIndex, word.length);
case QueryErrorKind.FieldName:
throw new QueryError(errorId, { word }, errorIndex, word.length);
case QueryErrorKind.CaptureName:
throw new QueryError(errorId, { word }, errorIndex, word.length);
case QueryErrorKind.PatternStructure:
throw new QueryError(errorId, { suffix: `${errorIndex}: '${suffix}'...` }, errorIndex, 0);
}
}
const stringCount = C._ts_query_string_count(address);
const captureCount = C._ts_query_cap