web-tree-sitter
Version:
Tree-sitter bindings for the web
315 lines (280 loc) • 10.4 kB
text/typescript
import { INTERNAL, Internal, assertInternal, Point, SIZE_OF_NODE, SIZE_OF_CURSOR, C } from './constants';
import { marshalNode, marshalPoint, marshalTreeCursor, unmarshalNode, unmarshalPoint, unmarshalTreeCursor } from './marshal';
import { Node } from './node';
import { TRANSFER_BUFFER } from './parser';
import { getText, Tree } from './tree';
/** A stateful object for walking a syntax {@link Tree} efficiently. */
export class TreeCursor {
/** @internal */
private [0] = 0; // Internal handle for WASM
/** @internal */
private [1] = 0; // Internal handle for WASM
/** @internal */
private [2] = 0; // Internal handle for WASM
/** @internal */
private [3] = 0; // Internal handle for WASM
/** @internal */
private tree: Tree;
/** @internal */
constructor(internal: Internal, tree: Tree) {
assertInternal(internal);
this.tree = tree;
unmarshalTreeCursor(this);
}
/** Creates a deep copy of the tree cursor. This allocates new memory. */
copy(): TreeCursor {
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(): void {
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(): Node {
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(): number {
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(): string | null {
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(): number {
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(): number {
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(): string {
return this.tree.language.types[this.nodeTypeId] || 'ERROR';
}
/** Get the type id of the cursor's current node. */
get nodeTypeId(): number {
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(): number {
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(): number {
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(): boolean {
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(): boolean {
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(): string {
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(): Point {
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(): Point {
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(): number {
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(): number {
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(): boolean {
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(): boolean {
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(): boolean {
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(): boolean {
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(): boolean {
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: number): void {
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: number): boolean {
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: Point): boolean {
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: Node): void {
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: TreeCursor): void {
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);
}
}