UNPKG

web-tree-sitter

Version:
315 lines (280 loc) 10.4 kB
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); } }