From 78fdeadfb02d34c3ba0e62869b14692d4437efbb Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 26 Apr 2025 17:45:38 +0200 Subject: [PATCH 1/3] Integrate web Tree sitter --- packages/common/package.json | 3 +- packages/common/src/types/TreeSitter.ts | 4 +- packages/cursorless-engine/package.json | 1 + .../DisabledLanguageDefinitions.ts | 7 +- .../disabledComponents/DisabledTreeSitter.ts | 4 +- .../src/languages/LanguageDefinition.ts | 12 +- .../src/languages/LanguageDefinitions.ts | 9 +- .../PredicateOperatorSchemaTypes.ts | 2 +- .../TreeSitterQuery/QueryPredicateOperator.ts | 6 +- .../constructZodErrorMessages.ts | 8 +- .../TreeSitterQuery/isContainedInErrorNode.ts | 6 +- .../operatorArgumentSchemaTypes.ts | 6 +- .../TreeSitterQuery/parsePredicates.test.ts | 4 +- .../TreeSitterQuery/parsePredicates.ts | 4 +- .../TreeSitterQuery/predicateToString.ts | 8 +- .../rewriteStartOfEndOf.test.ts | 4 +- .../src/languages/clojure.ts | 9 +- .../src/languages/elseIfExtractor.ts | 6 +- .../src/languages/getNodeMatcher.ts | 8 +- .../cursorless-engine/src/languages/latex.ts | 6 +- .../cursorless-engine/src/languages/ruby.ts | 4 +- .../cursorless-engine/src/languages/rust.ts | 8 +- .../LegacyContainingSyntaxScopeStage.ts | 6 +- .../cursorless-engine/src/typings/Types.ts | 15 +- .../cursorless-engine/src/util/nodeFinders.ts | 62 ++-- .../src/util/nodeMatchers.ts | 10 +- .../src/util/nodeSelectors.ts | 58 ++-- .../src/util/treeSitterUtils.ts | 27 +- packages/neovim-common/src/getExtensionApi.ts | 2 +- packages/vscode-common/src/getExtensionApi.ts | 4 +- pnpm-lock.yaml | 11 + typings/global.d.ts | 1 + typings/treeSitter.d.ts | 270 ------------------ 33 files changed, 161 insertions(+), 434 deletions(-) create mode 100644 typings/global.d.ts delete mode 100644 typings/treeSitter.d.ts diff --git a/packages/common/package.json b/packages/common/package.json index a072e9f09d..6e0ef5593a 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -26,7 +26,8 @@ "cross-spawn": "7.0.5", "fast-check": "3.22.0", "js-yaml": "^4.1.0", - "mocha": "^10.7.3" + "mocha": "^10.7.3", + "web-tree-sitter": "^0.25.3" }, "types": "./out/index.d.ts", "exports": { diff --git a/packages/common/src/types/TreeSitter.ts b/packages/common/src/types/TreeSitter.ts index db50589e43..c3b22e6d07 100644 --- a/packages/common/src/types/TreeSitter.ts +++ b/packages/common/src/types/TreeSitter.ts @@ -1,11 +1,11 @@ import type { Range, TextDocument } from "@cursorless/common"; -import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; +import type { Language, Node, Tree } from "web-tree-sitter"; export interface TreeSitter { /** * Function to access nodes in the tree sitter. */ - getNodeAtLocation(document: TextDocument, range: Range): SyntaxNode; + getNodeAtLocation(document: TextDocument, range: Range): Node; /** * Function to access the tree sitter tree. diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 503fdb5d8c..2ea200e905 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -32,6 +32,7 @@ "nearley": "2.20.1", "talon-snippets": "1.3.0", "uuid": "^10.0.0", + "web-tree-sitter": "^0.25.3", "zod": "3.23.8" }, "devDependencies": { diff --git a/packages/cursorless-engine/src/disabledComponents/DisabledLanguageDefinitions.ts b/packages/cursorless-engine/src/disabledComponents/DisabledLanguageDefinitions.ts index 20a3936c88..f66c20136e 100644 --- a/packages/cursorless-engine/src/disabledComponents/DisabledLanguageDefinitions.ts +++ b/packages/cursorless-engine/src/disabledComponents/DisabledLanguageDefinitions.ts @@ -1,5 +1,5 @@ import type { TextDocument, Range, Listener } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { LanguageDefinition } from "../languages/LanguageDefinition"; import type { LanguageDefinitions } from "../languages/LanguageDefinitions"; @@ -16,10 +16,7 @@ export class DisabledLanguageDefinitions implements LanguageDefinitions { return undefined; } - getNodeAtLocation( - _document: TextDocument, - _range: Range, - ): SyntaxNode | undefined { + getNodeAtLocation(_document: TextDocument, _range: Range): Node | undefined { return undefined; } diff --git a/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts b/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts index a12975541e..fdf3820a20 100644 --- a/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts +++ b/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts @@ -1,5 +1,5 @@ import type { Range, TextDocument, TreeSitter } from "@cursorless/common"; -import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; +import type { Language, Node, Tree } from "web-tree-sitter"; export class DisabledTreeSitter implements TreeSitter { getTree(_document: TextDocument): Tree { @@ -14,7 +14,7 @@ export class DisabledTreeSitter implements TreeSitter { throw new Error("Tree sitter not provided"); } - getNodeAtLocation(_document: TextDocument, _range: Range): SyntaxNode { + getNodeAtLocation(_document: TextDocument, _range: Range): Node { throw new Error("Tree sitter not provided"); } } diff --git a/packages/cursorless-engine/src/languages/LanguageDefinition.ts b/packages/cursorless-engine/src/languages/LanguageDefinition.ts index 93e93fb6d0..19d3ed275d 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinition.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinition.ts @@ -11,6 +11,7 @@ import { type IDE, type TextDocument, } from "@cursorless/common"; +import { Query } from "web-tree-sitter"; import { TreeSitterScopeHandler } from "../processTargets/modifiers/scopeHandlers"; import { TreeSitterQuery } from "./TreeSitterQuery"; import type { QueryCapture } from "./TreeSitterQuery/QueryCapture"; @@ -59,9 +60,14 @@ export class LanguageDefinition { return undefined; } - const rawQuery = treeSitter - .getLanguage(languageId)! - .query(rawLanguageQueryString); + const language = treeSitter.getLanguage(languageId); + + if (language == null) { + throw Error(`Could not get Tree sitter language for ${languageId}`); + } + + const rawQuery = new Query(language, rawLanguageQueryString); + const query = TreeSitterQuery.create(languageId, treeSitter, rawQuery); return new LanguageDefinition(query); diff --git a/packages/cursorless-engine/src/languages/LanguageDefinitions.ts b/packages/cursorless-engine/src/languages/LanguageDefinitions.ts index 1d5a5f4ca0..a0c255816a 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinitions.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinitions.ts @@ -8,7 +8,7 @@ import { type TreeSitter, } from "@cursorless/common"; import { toString } from "lodash-es"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import { LanguageDefinition } from "./LanguageDefinition"; import { treeSitterQueryCache } from "./TreeSitterQuery/treeSitterQueryCache"; @@ -36,10 +36,7 @@ export interface LanguageDefinitions { /** * @deprecated Only for use in legacy containing scope stage */ - getNodeAtLocation( - document: TextDocument, - range: Range, - ): SyntaxNode | undefined; + getNodeAtLocation(document: TextDocument, range: Range): Node | undefined; } /** @@ -163,7 +160,7 @@ export class LanguageDefinitionsImpl return definition === LANGUAGE_UNDEFINED ? undefined : definition; } - public getNodeAtLocation(document: TextDocument, range: Range): SyntaxNode { + public getNodeAtLocation(document: TextDocument, range: Range): Node { return this.treeSitter.getNodeAtLocation(document, range); } diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts index 0497810c51..6c6757df22 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts @@ -32,7 +32,7 @@ export type InferSchemaType = T["schema"]; * Maps from an operand schema output type to the type of the argument that * will be passed to the `accept` function of the predicate operator. For example: * - * - `{type: "capture", name: string}` -> `SyntaxNode` + * - `{type: "capture", name: string}` -> `Node` * - `{type: "integer", value: number}` -> `number` */ type PredicateParameterType = T extends { diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts index 5f35af593d..f3c876ee6c 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts @@ -1,4 +1,4 @@ -import type { PredicateOperand } from "web-tree-sitter"; +import type { PredicateStep } from "web-tree-sitter"; import type { z } from "zod"; import type { AcceptFunctionArgs, @@ -41,7 +41,7 @@ export abstract class QueryPredicateOperator { * * @param args The arguments to the operator, converted to the types specified * in the schema. For example, if the schema is `z.tuple([q.node, q.string])`, - * then `args` will be `SyntaxNode, string`. + * then `args` will be `Node, string`. */ protected abstract run( ...args: AcceptFunctionArgs>> @@ -71,7 +71,7 @@ export abstract class QueryPredicateOperator { * @returns Either a predicate function, or a list of error messages if the operands * were invalid. */ - createPredicate(inputOperands: PredicateOperand[]): PredicateResult { + createPredicate(inputOperands: PredicateStep[]): PredicateResult { const result = this.schema.safeParse(inputOperands); return result.success diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts index df881d7e16..147c2d8668 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts @@ -1,10 +1,10 @@ -import type { PredicateOperand } from "web-tree-sitter"; +import type { PredicateStep } from "web-tree-sitter"; import type { z } from "zod"; import { operandToString } from "./predicateToString"; export function constructZodErrorMessages( - inputOperands: PredicateOperand[], - error: z.ZodError, + inputOperands: PredicateStep[], + error: z.ZodError, ): string[] { return error.errors .filter( @@ -21,7 +21,7 @@ export function constructZodErrorMessages( .map((error) => getErrorMessage(inputOperands, error)); } -function getErrorMessage(inputOperands: PredicateOperand[], error: z.ZodIssue) { +function getErrorMessage(inputOperands: PredicateStep[], error: z.ZodIssue) { if (error.path.length === 0) { if (error.code === "too_small") { return "Too few arguments"; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/isContainedInErrorNode.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/isContainedInErrorNode.ts index 463d97e2e8..38f7cc4320 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/isContainedInErrorNode.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/isContainedInErrorNode.ts @@ -1,17 +1,17 @@ -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; /** * Determines whether the given node or one of its ancestors is an error node * @param node The node to check * @returns True if the given node is contained in an error node */ -export function isContainedInErrorNode(node: SyntaxNode) { +export function isContainedInErrorNode(node: Node) { // This node or one of it descendants is an error node if (node.hasError) { return true; } - let ancestorNode: SyntaxNode | null = node.parent; + let ancestorNode: Node | null = node.parent; while (ancestorNode != null) { // Ancestral node is an error node diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts index 099d197595..7295b185b1 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { assertTypesEqual } from "./assertTypesEqual"; -import type { PredicateOperand } from "web-tree-sitter"; +import type { PredicateStep } from "web-tree-sitter"; const string = z.object({ type: z.literal("string"), value: z.string() }); @@ -62,9 +62,9 @@ export type SchemaTypes = (typeof q)[keyof typeof q]; /** * The type of the input to the schema. This should always be - * `PredicateOperand`, as that is what we always get from tree-sitter + * `PredicateStep`, as that is what we always get from tree-sitter */ -export type SchemaInputType = PredicateOperand; +export type SchemaInputType = PredicateStep; // eslint-disable-next-line @typescript-eslint/no-unused-expressions assertTypesEqual, SchemaInputType>; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts index a19c66673b..0067a6856c 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts @@ -1,8 +1,8 @@ import assert from "assert"; import { parsePredicates } from "./parsePredicates"; -import type { PredicateResult } from "web-tree-sitter"; +import type { QueryPredicate } from "web-tree-sitter"; -const predicates: PredicateResult[][] = [ +const predicates: QueryPredicate[][] = [ [ // (#not-type? @statement comment) // Valid diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.ts index 65494ddc47..7a1318ad40 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.ts @@ -1,4 +1,4 @@ -import type { PredicateResult } from "web-tree-sitter"; +import type { QueryPredicate } from "web-tree-sitter"; import type { MutableQueryMatch } from "./QueryCapture"; import { queryPredicateOperators } from "./queryPredicateOperators"; @@ -14,7 +14,7 @@ import { queryPredicateOperators } from "./queryPredicateOperators"; * function that takes a match and returns true if the match matches the * predicate. */ -export function parsePredicates(predicateDescriptors: PredicateResult[][]) { +export function parsePredicates(predicateDescriptors: QueryPredicate[][]) { const errors: PredicateError[] = []; const predicates: ((match: MutableQueryMatch) => boolean)[][] = []; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/predicateToString.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/predicateToString.ts index c07a2e941d..71dd041ff1 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/predicateToString.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/predicateToString.ts @@ -1,8 +1,6 @@ -import type { PredicateOperand, PredicateResult } from "web-tree-sitter"; +import type { PredicateStep, QueryPredicate } from "web-tree-sitter"; -export function predicateToString( - predicateDescriptor: PredicateResult, -): string { +export function predicateToString(predicateDescriptor: QueryPredicate): string { const operandList = predicateDescriptor.operands .map(operandToString) .join(" "); @@ -10,6 +8,6 @@ export function predicateToString( return `(#${predicateDescriptor.operator} ${operandList})`; } -export function operandToString(value: PredicateOperand): string { +export function operandToString(value: PredicateStep): string { return value.type === "capture" ? `@${value.name}` : value.value; } diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/rewriteStartOfEndOf.test.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/rewriteStartOfEndOf.test.ts index 5d3376d9e0..078b46c4ac 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/rewriteStartOfEndOf.test.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/rewriteStartOfEndOf.test.ts @@ -1,6 +1,6 @@ import type { TextDocument } from "@cursorless/common"; import { Range } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { MutableQueryCapture } from "./QueryCapture"; import { rewriteStartOfEndOf } from "./rewriteStartOfEndOf"; import assert from "assert"; @@ -59,7 +59,7 @@ function fillOutCapture(capture: NameRange): MutableQueryCapture { allowMultiple: false, insertionDelimiter: undefined, document: null as unknown as TextDocument, - node: null as unknown as SyntaxNode, + node: null as unknown as Node, hasError, }; } diff --git a/packages/cursorless-engine/src/languages/clojure.ts b/packages/cursorless-engine/src/languages/clojure.ts index 4ecb9ef947..bfc4b33367 100644 --- a/packages/cursorless-engine/src/languages/clojure.ts +++ b/packages/cursorless-engine/src/languages/clojure.ts @@ -1,5 +1,5 @@ import type { SimpleScopeTypeType } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { NodeFinder, NodeMatcherAlternative } from "../typings/Types"; import { patternFinder } from "../util/nodeFinders"; import { @@ -44,7 +44,7 @@ function indexNodeFinder( parentFinder: NodeFinder, indexTransform: (index: number) => number, ) { - return (node: SyntaxNode) => { + return (node: Node) => { const parent = node.parent; if (parent == null || parentFinder(parent) == null) { @@ -76,8 +76,7 @@ function indexNodeFinder( * @param node The node whose children to get * @returns A list of the value node children of the given node */ -const getValueNodes = (node: SyntaxNode) => - getChildNodesForFieldName(node, "value"); +const getValueNodes = (node: Node) => getChildNodesForFieldName(node, "value"); // A function call is a list literal which is not quoted const functionCallPattern = "~quoting_lit.list_lit!"; @@ -89,7 +88,7 @@ const functionCallFinder = patternFinder(functionCallPattern); * @returns The function call node if the name matches otherwise null */ function functionNameBasedFinder(...names: string[]) { - return (node: SyntaxNode) => { + return (node: Node) => { const functionCallNode = functionCallFinder(node); if (functionCallNode == null) { return null; diff --git a/packages/cursorless-engine/src/languages/elseIfExtractor.ts b/packages/cursorless-engine/src/languages/elseIfExtractor.ts index 171a3189ed..cd16e383d9 100644 --- a/packages/cursorless-engine/src/languages/elseIfExtractor.ts +++ b/packages/cursorless-engine/src/languages/elseIfExtractor.ts @@ -1,6 +1,6 @@ import type { TextEditor } from "@cursorless/common"; import { Selection } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { SelectionExtractor, SelectionWithContext, @@ -29,7 +29,7 @@ export function elseIfExtractor(): SelectionExtractor { includeUnnamedChildren: true, }); - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { + return function (editor: TextEditor, node: Node): SelectionWithContext { const contentRange = contentRangeExtractor(editor, node); const parent = node.parent; @@ -84,7 +84,7 @@ export function elseIfExtractor(): SelectionExtractor { export function elseExtractor(ifNodeType: string): SelectionExtractor { const nestedElseIfExtractor = elseIfExtractor(); - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { + return function (editor: TextEditor, node: Node): SelectionWithContext { // If we are an `else if` statement, then we just run `elseIfExtractor` on // our nested `if` node. Otherwise we are a simple `else` branch and don't // need to do anything fancy. diff --git a/packages/cursorless-engine/src/languages/getNodeMatcher.ts b/packages/cursorless-engine/src/languages/getNodeMatcher.ts index a5cb458ccf..e7b2b5f40a 100644 --- a/packages/cursorless-engine/src/languages/getNodeMatcher.ts +++ b/packages/cursorless-engine/src/languages/getNodeMatcher.ts @@ -1,5 +1,5 @@ import { UnsupportedLanguageError } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { SimpleScopeTypeType } from "@cursorless/common"; import type { NodeMatcher, @@ -53,7 +53,7 @@ export const languageMatchers: Record< function matcherIncludeSiblings(matcher: NodeMatcher): NodeMatcher { return ( selection: SelectionWithEditor, - node: SyntaxNode, + node: Node, ): NodeMatcherValue[] | null => { let matches = matcher(selection, node); if (matches == null) { @@ -74,11 +74,11 @@ function matcherIncludeSiblings(matcher: NodeMatcher): NodeMatcher { } function iterateNearestIterableAncestor( - node: SyntaxNode, + node: Node, selection: SelectionWithEditor, nodeMatcher: NodeMatcher, ) { - let parent: SyntaxNode | null = node.parent; + let parent: Node | null = node.parent; while (parent != null) { const matches = parent.namedChildren .flatMap((sibling) => nodeMatcher(selection, sibling)) diff --git a/packages/cursorless-engine/src/languages/latex.ts b/packages/cursorless-engine/src/languages/latex.ts index 0203a67a0e..7ad103dbb6 100644 --- a/packages/cursorless-engine/src/languages/latex.ts +++ b/packages/cursorless-engine/src/languages/latex.ts @@ -1,6 +1,6 @@ import type { SimpleScopeTypeType, TextEditor } from "@cursorless/common"; import { Selection } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { NodeMatcherAlternative, SelectionWithContext, @@ -84,7 +84,7 @@ const sectioningCommand = SECTIONING.map((s) => `${s}[command]`); function unwrapGroupParens( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { return { selection: new Selection( @@ -102,7 +102,7 @@ function unwrapGroupParens( function extendToNamedSiblingIfExists( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { const startIndex = node.startIndex; let endIndex = node.endIndex; diff --git a/packages/cursorless-engine/src/languages/ruby.ts b/packages/cursorless-engine/src/languages/ruby.ts index 62ed5a1a92..0e0e166b64 100644 --- a/packages/cursorless-engine/src/languages/ruby.ts +++ b/packages/cursorless-engine/src/languages/ruby.ts @@ -1,5 +1,5 @@ import type { SimpleScopeTypeType } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { NodeMatcherAlternative } from "../typings/Types"; import { patternFinder } from "../util/nodeFinders"; import { @@ -131,7 +131,7 @@ const assignmentOperators = [ ]; const mapKeyValueSeparators = [":", "=>"]; -function blockFinder(node: SyntaxNode) { +function blockFinder(node: Node) { if (node.type !== "call") { return null; } diff --git a/packages/cursorless-engine/src/languages/rust.ts b/packages/cursorless-engine/src/languages/rust.ts index f7bf5eda7f..1604ea3613 100644 --- a/packages/cursorless-engine/src/languages/rust.ts +++ b/packages/cursorless-engine/src/languages/rust.ts @@ -1,5 +1,5 @@ import type { TextEditor } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { SimpleScopeTypeType } from "@cursorless/common"; import type { NodeMatcherAlternative, @@ -60,7 +60,7 @@ const STATEMENT_PARENT_TYPES = ["source_file", "block", "declaration_list"]; * @param node The node which we will start our search from * @returns node or null */ -function implItemTypeFinder(node: SyntaxNode) { +function implItemTypeFinder(node: Node) { if ( node.parent?.type === "impl_item" && node.parent?.childForFieldName("type")?.equals(node) @@ -72,7 +72,7 @@ function implItemTypeFinder(node: SyntaxNode) { function traitBoundExtractor( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { return { selection: makeNodePairSelection(node.children[1], node.lastNamedChild!), @@ -92,7 +92,7 @@ function traitBoundExtractor( * @param node The node which we might match * @returns The return value node */ -function returnValueFinder(node: SyntaxNode) { +function returnValueFinder(node: Node) { if (node.type !== "block") { return null; } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts index e3beaa6f02..39461acc41 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts @@ -4,7 +4,7 @@ import type { SimpleScopeType, } from "@cursorless/common"; import { NoContainingScopeError, Selection } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { LanguageDefinitions } from "../../../languages/LanguageDefinitions"; import { getNodeMatcher } from "../../../languages/getNodeMatcher"; import type { @@ -93,11 +93,11 @@ export class LegacyContainingSyntaxScopeStage implements ModifierStage { } function findNearestContainingAncestorNode( - startNode: SyntaxNode, + startNode: Node, nodeMatcher: NodeMatcher, selection: SelectionWithEditor, ): SelectionWithEditorWithContext[] | null { - let node: SyntaxNode | null = startNode; + let node: Node | null = startNode; while (node != null) { const matches = nodeMatcher(selection, node); if (matches != null) { diff --git a/packages/cursorless-engine/src/typings/Types.ts b/packages/cursorless-engine/src/typings/Types.ts index fd3e33e357..778862b91c 100644 --- a/packages/cursorless-engine/src/typings/Types.ts +++ b/packages/cursorless-engine/src/typings/Types.ts @@ -1,5 +1,5 @@ import type { Edit, Range, Selection, TextEditor } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; export interface SelectionWithEditor { selection: Selection; @@ -46,7 +46,7 @@ export interface SelectionWithContext { } export type NodeMatcherValue = { - node: SyntaxNode; + node: Node; selection: SelectionWithContext; }; @@ -54,22 +54,19 @@ export type NodeMatcherAlternative = NodeMatcher | string[] | string; export type NodeMatcher = ( selection: SelectionWithEditor, - node: SyntaxNode, + node: Node, ) => NodeMatcherValue[] | null; /** * Returns the desired relative of the provided node. * Returns null if matching node not found. **/ -export type NodeFinder = ( - node: SyntaxNode, - selection?: Selection, -) => SyntaxNode | null; +export type NodeFinder = (node: Node, selection?: Selection) => Node | null; -/** Returns one or more selections for a given SyntaxNode */ +/** Returns one or more selections for a given Node */ export type SelectionExtractor = ( editor: TextEditor, - nodes: SyntaxNode, + nodes: Node, ) => SelectionWithContext; export interface EditWithRangeUpdater extends Edit { diff --git a/packages/cursorless-engine/src/util/nodeFinders.ts b/packages/cursorless-engine/src/util/nodeFinders.ts index 12553bf75b..987eb5a908 100644 --- a/packages/cursorless-engine/src/util/nodeFinders.ts +++ b/packages/cursorless-engine/src/util/nodeFinders.ts @@ -1,12 +1,12 @@ import type { Selection } from "@cursorless/common"; import { Position } from "@cursorless/common"; -import type { Point, SyntaxNode } from "web-tree-sitter"; +import type { Point, Node } from "web-tree-sitter"; import type { NodeFinder } from "../typings/Types"; export const nodeFinder = ( - isTargetNode: (node: SyntaxNode) => boolean, + isTargetNode: (node: Node) => boolean, ): NodeFinder => { - return (node: SyntaxNode) => { + return (node: Node) => { return isTargetNode(node) ? node : null; }; }; @@ -19,8 +19,8 @@ export const nodeFinder = ( * @returns A node finder */ export function leadingSiblingNodeFinder(nodeFinder: NodeFinder) { - return (node: SyntaxNode) => { - let currentNode: SyntaxNode | null = node; + return (node: Node) => { + let currentNode: Node | null = node; while (currentNode != null) { const returnNode = nodeFinder(currentNode); @@ -44,8 +44,8 @@ export function leadingSiblingNodeFinder(nodeFinder: NodeFinder) { * @returns A node finder which is a chain of the input node finders */ export function chainedNodeFinder(...nodeFinders: NodeFinder[]) { - return (node: SyntaxNode) => { - let currentNode: SyntaxNode | null = node; + return (node: Node) => { + let currentNode: Node | null = node; for (const nodeFinder of nodeFinders) { currentNode = nodeFinder(currentNode); if (currentNode == null) { @@ -77,9 +77,9 @@ export function ancestorChainNodeFinder( nodeToReturn: number, ...nodeFinders: NodeFinder[] ) { - return (node: SyntaxNode) => { - let currentNode: SyntaxNode | null = node; - const nodeList: SyntaxNode[] = []; + return (node: Node) => { + let currentNode: Node | null = node; + const nodeList: Node[] = []; const nodeFindersReversed = [...nodeFinders].reverse(); for (const nodeFinder of nodeFindersReversed) { @@ -112,12 +112,11 @@ export const argumentNodeFinder = (...parentTypes: string[]): NodeFinder => { const left = ["(", "{", "[", "<"]; const right = [")", "}", "]", ">"]; const delimiters = left.concat(right); - const isType = (node: SyntaxNode | null, typeNames: string[]) => + const isType = (node: Node | null, typeNames: string[]) => node != null && typeNames.includes(node.type); - const isOk = (node: SyntaxNode | null) => - node != null && !isType(node, delimiters); - return (node: SyntaxNode, selection?: Selection) => { - let resultNode: SyntaxNode | null; + const isOk = (node: Node | null) => node != null && !isType(node, delimiters); + return (node: Node, selection?: Selection) => { + let resultNode: Node | null; const { start, end } = selection!; // Is already child if (isType(node.parent, parentTypes)) { @@ -171,9 +170,9 @@ export const argumentNodeFinder = (...parentTypes: string[]): NodeFinder => { export function findPossiblyWrappedNode( isWrapperNode: NodeFinder, isTargetNode: NodeFinder, - getWrappedNodes: (node: SyntaxNode) => (SyntaxNode | null)[], + getWrappedNodes: (node: Node) => (Node | null)[], ): NodeFinder { - return (node: SyntaxNode) => { + return (node: Node) => { if (node.parent != null && isWrapperNode(node.parent)) { // We don't want to return the target node if it is wrapped. We return // null, knowing that the ancestor walk will call us again with the @@ -197,7 +196,7 @@ export function findPossiblyWrappedNode( export function patternFinder(...patterns: string[]): NodeFinder { const parsedPatterns = parsePatternStrings(patterns); - return (node: SyntaxNode) => { + return (node: Node) => { for (const pattern of parsedPatterns) { const match = tryPatternMatch(node, pattern); if (match != null) { @@ -214,17 +213,14 @@ function parsePatternStrings(patternStrings: string[]) { ); } -function tryPatternMatch( - node: SyntaxNode, - patterns: Pattern[], -): SyntaxNode | null { +function tryPatternMatch(node: Node, patterns: Pattern[]): Node | null { let result = searchNodeAscending(node, patterns); if (!result && patterns.length > 1) { result = searchNodeDescending(node, patterns); } - let resultNode: SyntaxNode | null = null; + let resultNode: Node | null = null; let resultPattern; if (result != null) { @@ -248,14 +244,11 @@ function tryPatternMatch( return resultNode; } -type NodePattern = [SyntaxNode, Pattern] | null; +type NodePattern = [Node, Pattern] | null; -function searchNodeAscending( - node: SyntaxNode, - patterns: Pattern[], -): NodePattern { +function searchNodeAscending(node: Node, patterns: Pattern[]): NodePattern { let result: NodePattern = null; - let currentNode: SyntaxNode | null = node; + let currentNode: Node | null = node; for (let i = patterns.length - 1; i > -1; --i) { const pattern = patterns[i]; @@ -278,12 +271,9 @@ function searchNodeAscending( return result; } -function searchNodeDescending( - node: SyntaxNode, - patterns: Pattern[], -): NodePattern { +function searchNodeDescending(node: Node, patterns: Pattern[]): NodePattern { let result: NodePattern = null; - let currentNode: SyntaxNode | null = node; + let currentNode: Node | null = node; for (let i = 0; i < patterns.length; ++i) { const pattern = patterns[i]; @@ -301,7 +291,7 @@ function searchNodeDescending( } if (i + 1 < patterns.length) { - const children: SyntaxNode[] = currentNode.namedChildren.filter((node) => + const children: Node[] = currentNode.namedChildren.filter((node) => patterns[i + 1].typeEquals(node), ); currentNode = children.length === 1 ? children[0] : null; @@ -357,7 +347,7 @@ class Pattern { }); } - typeEquals(node: SyntaxNode) { + typeEquals(node: Node) { if (this.anyType) { return true; } diff --git a/packages/cursorless-engine/src/util/nodeMatchers.ts b/packages/cursorless-engine/src/util/nodeMatchers.ts index 115c943ab8..b0bf026d42 100644 --- a/packages/cursorless-engine/src/util/nodeMatchers.ts +++ b/packages/cursorless-engine/src/util/nodeMatchers.ts @@ -1,6 +1,6 @@ import type { SimpleScopeTypeType } from "@cursorless/common"; import { unsafeKeys } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; import type { NodeFinder, NodeMatcher, @@ -27,7 +27,7 @@ export function matcher( finder: NodeFinder, selector: SelectionExtractor = simpleSelectionExtractor, ): NodeMatcher { - return function (selection: SelectionWithEditor, node: SyntaxNode) { + return function (selection: SelectionWithEditor, node: Node) { const targetNode = finder(node, selection.selection); return targetNode != null ? [ @@ -54,7 +54,7 @@ export function chainedMatcher( ): NodeMatcher { const nodeFinder = chainedNodeFinder(...finders); - return function (selection: SelectionWithEditor, initialNode: SyntaxNode) { + return function (selection: SelectionWithEditor, initialNode: Node) { const returnNode = nodeFinder(initialNode); if (returnNode == null) { @@ -157,7 +157,7 @@ export function trailingMatcher( * @returns A NodeMatcher that tries the given matchers in sequence */ export function cascadingMatcher(...matchers: NodeMatcher[]): NodeMatcher { - return (selection: SelectionWithEditor, node: SyntaxNode) => { + return (selection: SelectionWithEditor, node: Node) => { for (const matcher of matchers) { const match = matcher(selection, node); if (match != null) { @@ -170,7 +170,7 @@ export function cascadingMatcher(...matchers: NodeMatcher[]): NodeMatcher { } export function notSupported(scopeTypeType: SimpleScopeTypeType): NodeMatcher { - return (_selection: SelectionWithEditor, _node: SyntaxNode) => { + return (_selection: SelectionWithEditor, _node: Node) => { throw new Error(`Node type '${scopeTypeType}' not supported`); }; } diff --git a/packages/cursorless-engine/src/util/nodeSelectors.ts b/packages/cursorless-engine/src/util/nodeSelectors.ts index d56a9c5123..4b309bc3e3 100644 --- a/packages/cursorless-engine/src/util/nodeSelectors.ts +++ b/packages/cursorless-engine/src/util/nodeSelectors.ts @@ -1,7 +1,7 @@ import type { TextEditor } from "@cursorless/common"; import { Position, Range, Selection } from "@cursorless/common"; import { identity, maxBy } from "lodash-es"; -import type { Point, SyntaxNode } from "web-tree-sitter"; +import type { Point, Node } from "web-tree-sitter"; import type { NodeFinder, SelectionExtractor, @@ -24,7 +24,7 @@ export function positionFromPoint(point: Point): Position { return new Position(point.row, point.column); } -export function getNodeRange(node: SyntaxNode) { +export function getNodeRange(node: Node) { return new Range( node.startPosition.row, node.startPosition.column, @@ -33,7 +33,7 @@ export function getNodeRange(node: SyntaxNode) { ); } -export function makeNodePairSelection(anchor: SyntaxNode, active: SyntaxNode) { +export function makeNodePairSelection(anchor: Node, active: Node) { return new Selection( anchor.startPosition.row, anchor.startPosition.column, @@ -47,7 +47,7 @@ export function makeNodePairSelection(anchor: SyntaxNode, active: SyntaxNode) { * @param node The note for which to get the internal range * @returns The internal range of the given node */ -export function getNodeInternalRange(node: SyntaxNode) { +export function getNodeInternalRange(node: Node) { const children = node.children; return makeRangeFromPositions( @@ -58,7 +58,7 @@ export function getNodeInternalRange(node: SyntaxNode) { export function simpleSelectionExtractor( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { return { selection: new Selection( @@ -81,7 +81,7 @@ export function simpleSelectionExtractor( */ export function extendUntilNextMatchingSiblingOrLast( editor: TextEditor, - node: SyntaxNode, + node: Node, nodeFinder: NodeFinder, ) { const endNode = getNextMatchingSiblingNodeOrLast(node, nodeFinder); @@ -89,11 +89,11 @@ export function extendUntilNextMatchingSiblingOrLast( } function getNextMatchingSiblingNodeOrLast( - node: SyntaxNode, + node: Node, nodeFinder: NodeFinder, -): SyntaxNode { - let currentNode: SyntaxNode = node; - let nextNode: SyntaxNode | null = node.nextSibling; +): Node { + let currentNode: Node = node; + let nextNode: Node | null = node.nextSibling; while (nextNode != null && nodeFinder(nextNode) == null) { currentNode = nextNode; @@ -109,8 +109,8 @@ function getNextMatchingSiblingNodeOrLast( * @returns An extractor */ export function extendForwardPastOptional(...delimiters: string[]) { - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { - const nextNode: SyntaxNode | null = node.nextSibling; + return function (editor: TextEditor, node: Node): SelectionWithContext { + const nextNode: Node | null = node.nextSibling; if (nextNode != null && delimiters.includes(nextNode.type)) { return pairSelectionExtractor(editor, node, nextNode); @@ -126,8 +126,8 @@ export function extendForwardPastOptional(...delimiters: string[]) { */ export function pairSelectionExtractor( editor: TextEditor, - node1: SyntaxNode, - node2: SyntaxNode, + node1: Node, + node2: Node, ): SelectionWithContext { const isForward = node1.startIndex < node2.startIndex; const start = isForward ? node1 : node2; @@ -159,7 +159,7 @@ export function argumentSelectionExtractor(): SelectionExtractor { export function unwrapSelectionExtractor( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { let startIndex = node.startIndex; let endIndex = node.endIndex; @@ -179,7 +179,7 @@ export function unwrapSelectionExtractor( } export function selectWithLeadingDelimiter(...delimiters: string[]) { - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { + return function (editor: TextEditor, node: Node): SelectionWithContext { const firstSibling = node.previousSibling; const secondSibling = firstSibling?.previousSibling; let leadingDelimiterRange; @@ -238,7 +238,7 @@ export function childRangeSelector( typesToInclude: string[] = [], { includeUnnamedChildren = false }: ChildRangeSelectorOptions = {}, ) { - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { + return function (editor: TextEditor, node: Node): SelectionWithContext { if (typesToExclude.length > 0 && typesToInclude.length > 0) { throw new Error("Cannot have both exclusions and inclusions."); } @@ -262,7 +262,7 @@ export function childRangeSelector( } export function selectWithTrailingDelimiter(...delimiters: string[]) { - return function (editor: TextEditor, node: SyntaxNode): SelectionWithContext { + return function (editor: TextEditor, node: Node): SelectionWithContext { const firstSibling = node.nextSibling; const secondSibling = firstSibling?.nextSibling; let trailingDelimiterRange; @@ -296,9 +296,9 @@ export function selectWithTrailingDelimiter(...delimiters: string[]) { } function getNextNonDelimiterNode( - startNode: SyntaxNode, - isDelimiterNode: (node: SyntaxNode) => boolean, -): SyntaxNode | null { + startNode: Node, + isDelimiterNode: (node: Node) => boolean, +): Node | null { let node = startNode.nextSibling; while (node != null) { @@ -313,9 +313,9 @@ function getNextNonDelimiterNode( } function getPreviousNonDelimiterNode( - startNode: SyntaxNode, - isDelimiterNode: (node: SyntaxNode) => boolean, -): SyntaxNode | null { + startNode: Node, + isDelimiterNode: (node: Node) => boolean, +): Node | null { let node = startNode.previousSibling; while (node != null) { @@ -347,12 +347,12 @@ export function delimitersSelector(...delimiters: string[]) { * @returns A selection extractor */ export function delimitedSelector( - isDelimiterNode: (node: SyntaxNode) => boolean, + isDelimiterNode: (node: Node) => boolean, defaultDelimiter: string, - getStartNode: (node: SyntaxNode) => SyntaxNode = identity, - getEndNode: (node: SyntaxNode) => SyntaxNode = identity, + getStartNode: (node: Node) => Node = identity, + getEndNode: (node: Node) => Node = identity, ): SelectionExtractor { - return (editor: TextEditor, node: SyntaxNode) => { + return (editor: TextEditor, node: Node) => { let leadingDelimiterRange: Range | undefined; let trailingDelimiterRange: Range | undefined; const startNode = getStartNode(node); @@ -407,7 +407,7 @@ export function delimitedSelector( export function xmlElementExtractor( editor: TextEditor, - node: SyntaxNode, + node: Node, ): SelectionWithContext { const selection = simpleSelectionExtractor(editor, node); diff --git a/packages/cursorless-engine/src/util/treeSitterUtils.ts b/packages/cursorless-engine/src/util/treeSitterUtils.ts index 6b54bebb17..39f7e4ca1d 100644 --- a/packages/cursorless-engine/src/util/treeSitterUtils.ts +++ b/packages/cursorless-engine/src/util/treeSitterUtils.ts @@ -1,24 +1,23 @@ -import type { SyntaxNode } from "web-tree-sitter"; +import type { Node } from "web-tree-sitter"; -export const getValueNode = (node: SyntaxNode) => - node.childForFieldName("value"); +export const getValueNode = (node: Node) => node.childForFieldName("value"); -export const getLeftNode = (node: SyntaxNode) => node.childForFieldName("left"); +export const getLeftNode = (node: Node) => node.childForFieldName("left"); -export const getNameNode = (node: SyntaxNode) => node.childForFieldName("name"); +export const getNameNode = (node: Node) => node.childForFieldName("name"); -export const getKeyNode = (node: SyntaxNode) => node.childForFieldName("key"); +export const getKeyNode = (node: Node) => node.childForFieldName("key"); -export const getDefinitionNode = (node: SyntaxNode) => +export const getDefinitionNode = (node: Node) => node.childForFieldName("definition"); -export const getDeclarationNode = (node: SyntaxNode) => +export const getDeclarationNode = (node: Node) => node.childForFieldName("declarator"); export function getChildNodesForFieldName( - node: SyntaxNode, + node: Node, fieldName: string, -): SyntaxNode[] { +): Node[] { const treeCursor = node.walk(); treeCursor.gotoFirstChild(); @@ -44,11 +43,11 @@ export function getChildNodesForFieldName( * @param includeNode Whether to include the node itself in the returned list * @returns A list of ancestors possibly including the includeNode node itself */ -export function getAncestors(node: SyntaxNode, includeNode: boolean = true) { - const ancestors: SyntaxNode[] = includeNode ? [node] : []; +export function getAncestors(node: Node, includeNode: boolean = true) { + const ancestors: Node[] = includeNode ? [node] : []; for ( - let currentNode: SyntaxNode | null = node.parent; + let currentNode: Node | null = node.parent; currentNode != null; currentNode = currentNode.parent ) { @@ -63,6 +62,6 @@ export function getAncestors(node: SyntaxNode, includeNode: boolean = true) { * @param node The node to check * @returns True if the given node is contained in an error node */ -export function isContainedInErrorNode(node: SyntaxNode) { +export function isContainedInErrorNode(node: Node) { return getAncestors(node).some((ancestor) => ancestor.type === "ERROR"); } diff --git a/packages/neovim-common/src/getExtensionApi.ts b/packages/neovim-common/src/getExtensionApi.ts index f05f825dfc..3349fc007f 100644 --- a/packages/neovim-common/src/getExtensionApi.ts +++ b/packages/neovim-common/src/getExtensionApi.ts @@ -16,7 +16,7 @@ export interface CursorlessApi { // See packages\cursorless-neovim\src\extension.ts:createTreeSitter() for neovim // export interface ParseTreeApi { -// getNodeAtLocation(location: vscode.Location): SyntaxNode; +// getNodeAtLocation(location: vscode.Location): Node; // getTreeForUri(uri: vscode.Uri): Tree; // loadLanguage: (languageId: string) => Promise; // getLanguage(languageId: string): Language | undefined; diff --git a/packages/vscode-common/src/getExtensionApi.ts b/packages/vscode-common/src/getExtensionApi.ts index 2549c52ac0..f454a9ae6f 100644 --- a/packages/vscode-common/src/getExtensionApi.ts +++ b/packages/vscode-common/src/getExtensionApi.ts @@ -1,6 +1,6 @@ import type { CommandServerApi } from "@cursorless/common"; import * as vscode from "vscode"; -import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; +import type { Language, Node, Tree } from "web-tree-sitter"; import type { VscodeTestHelpers } from "./TestHelpers"; export interface CursorlessApi { @@ -8,7 +8,7 @@ export interface CursorlessApi { } export interface ParseTreeApi { - getNodeAtLocation(location: vscode.Location): SyntaxNode; + getNodeAtLocation(location: vscode.Location): Node; getTreeForUri(uri: vscode.Uri): Tree; loadLanguage: (languageId: string) => Promise; getLanguage(languageId: string): Language | undefined; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7b82005a9..a3480ab9e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,9 @@ importers: mocha: specifier: ^10.7.3 version: 10.7.3 + web-tree-sitter: + specifier: ^0.25.3 + version: 0.25.3 packages/cursorless-cheatsheet: dependencies: @@ -290,6 +293,9 @@ importers: uuid: specifier: ^10.0.0 version: 10.0.0 + web-tree-sitter: + specifier: ^0.25.3 + version: 0.25.3 zod: specifier: 3.23.8 version: 3.23.8 @@ -9789,6 +9795,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-tree-sitter@0.25.3: + resolution: {integrity: sha512-e0hdXG+nJ18Zd/QJFhSx0DNTSMz7miwUjKyJ/lglTnZo6ke08++BQzXkaeaqnGJFi9qq+nPJg2L8hYAjduToHQ==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -21401,6 +21410,8 @@ snapshots: web-namespaces@2.0.1: {} + web-tree-sitter@0.25.3: {} + webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} diff --git a/typings/global.d.ts b/typings/global.d.ts new file mode 100644 index 0000000000..3eff59d4c1 --- /dev/null +++ b/typings/global.d.ts @@ -0,0 +1 @@ +type EmscriptenModule = unknown; diff --git a/typings/treeSitter.d.ts b/typings/treeSitter.d.ts deleted file mode 100644 index 6199d33508..0000000000 --- a/typings/treeSitter.d.ts +++ /dev/null @@ -1,270 +0,0 @@ -// From https://github.com/tree-sitter/tree-sitter/blob/604d38e6b327ed33877e1285680b505b9484a71c/lib/binding_web/tree-sitter-web.d.ts -// License https://github.com/tree-sitter/tree-sitter/blob/604d38e6b327ed33877e1285680b505b9484a71c/LICENSE -declare module "web-tree-sitter" { - class Parser { - /** - * - * @param moduleOptions Optional emscripten module-object, see https://emscripten.org/docs/api_reference/module.html - */ - static init(moduleOptions?: object): Promise; - delete(): void; - parse( - input: string | Parser.Input, - oldTree?: Parser.Tree, - options?: Parser.Options, - ): Parser.Tree; - getIncludedRanges(): Parser.Range[]; - getTimeoutMicros(): number; - setTimeoutMicros(timeout: number): void; - reset(): void; - getLanguage(): Parser.Language; - setLanguage(language?: Parser.Language | null): void; - getLogger(): Parser.Logger; - setLogger(logFunc?: Parser.Logger | false | null): void; - } - - namespace Parser { - export type Options = { - includedRanges?: Range[]; - }; - - export type Point = { - row: number; - column: number; - }; - - export type Range = { - startIndex: number; - endIndex: number; - startPosition: Point; - endPosition: Point; - }; - - export type Edit = { - startIndex: number; - oldEndIndex: number; - newEndIndex: number; - startPosition: Point; - oldEndPosition: Point; - newEndPosition: Point; - }; - - export type Logger = ( - message: string, - params: { [param: string]: string }, - type: "parse" | "lex", - ) => void; - - export interface Input { - (index: number, position?: Point): string | null; - } - - export interface SyntaxNode { - tree: Tree; - id: number; - typeId: number; - grammarId: number; - type: string; - grammarType: string; - isNamed: boolean; - isMissing: boolean; - isExtra: boolean; - hasChanges: boolean; - hasError: boolean; - isError: boolean; - text: string; - parseState: number; - nextParseState: number; - startPosition: Point; - endPosition: Point; - startIndex: number; - endIndex: number; - parent: SyntaxNode | null; - children: Array; - namedChildren: Array; - childCount: number; - namedChildCount: number; - firstChild: SyntaxNode | null; - firstNamedChild: SyntaxNode | null; - lastChild: SyntaxNode | null; - lastNamedChild: SyntaxNode | null; - nextSibling: SyntaxNode | null; - nextNamedSibling: SyntaxNode | null; - previousSibling: SyntaxNode | null; - previousNamedSibling: SyntaxNode | null; - descendantCount: number; - - equals(other: SyntaxNode): boolean; - toString(): string; - child(index: number): SyntaxNode | null; - namedChild(index: number): SyntaxNode | null; - childForFieldName(fieldName: string): SyntaxNode | null; - childForFieldId(fieldId: number): SyntaxNode | null; - fieldNameForChild(childIndex: number): string | null; - childrenForFieldName(fieldName: string): Array; - childrenForFieldId(fieldId: number): Array; - firstChildForIndex(index: number): SyntaxNode | null; - firstNamedChildForIndex(index: number): SyntaxNode | null; - - descendantForIndex(index: number): SyntaxNode; - descendantForIndex(startIndex: number, endIndex: number): SyntaxNode; - namedDescendantForIndex(index: number): SyntaxNode; - namedDescendantForIndex(startIndex: number, endIndex: number): SyntaxNode; - descendantForPosition(position: Point): SyntaxNode; - descendantForPosition( - startPosition: Point, - endPosition: Point, - ): SyntaxNode; - namedDescendantForPosition(position: Point): SyntaxNode; - namedDescendantForPosition( - startPosition: Point, - endPosition: Point, - ): SyntaxNode; - descendantsOfType( - types: string | Array, - startPosition?: Point, - endPosition?: Point, - ): Array; - - walk(): TreeCursor; - } - - export interface TreeCursor { - nodeType: string; - nodeTypeId: number; - nodeStateId: number; - nodeText: string; - nodeId: number; - nodeIsNamed: boolean; - nodeIsMissing: boolean; - startPosition: Point; - endPosition: Point; - startIndex: number; - endIndex: number; - readonly currentNode: SyntaxNode; - readonly currentFieldName: string; - readonly currentFieldId: number; - readonly currentDepth: number; - readonly currentDescendantIndex: number; - - reset(node: SyntaxNode): void; - resetTo(cursor: TreeCursor): void; - delete(): void; - gotoParent(): boolean; - gotoFirstChild(): boolean; - gotoLastChild(): boolean; - gotoFirstChildForIndex(goalIndex: number): boolean; - gotoFirstChildForPosition(goalPosition: Point): boolean; - gotoNextSibling(): boolean; - gotoPreviousSibling(): boolean; - gotoDescendant(goalDescendantIndex: number): void; - } - - export interface Tree { - readonly rootNode: SyntaxNode; - - rootNodeWithOffset(offsetBytes: number, offsetExtent: Point): SyntaxNode; - copy(): Tree; - delete(): void; - edit(edit: Edit): Tree; - walk(): TreeCursor; - getChangedRanges(other: Tree): Range[]; - getIncludedRanges(): Range[]; - getEditedRange(other: Tree): Range; - getLanguage(): Language; - } - - export interface QueryCapture { - name: string; - text?: string; - node: SyntaxNode; - setProperties?: { [prop: string]: string | null }; - assertedProperties?: { [prop: string]: string | null }; - refutedProperties?: { [prop: string]: string | null }; - } - - export interface QueryMatch { - pattern: number; - captures: QueryCapture[]; - } - - export type QueryOptions = { - startPosition?: Point; - endPosition?: Point; - startIndex?: number; - endIndex?: number; - matchLimit?: number; - maxStartDepth?: number; - }; - - interface PredicateResult { - operator: string; - operands: PredicateOperand[]; - } - - interface PredicateCaptureOperand { - type: "capture"; - name: string; - } - - interface PredicateStringOperand { - type: "string"; - value: string; - } - - type PredicateOperand = PredicateCaptureOperand | PredicateStringOperand; - - export class Query { - captureNames: string[]; - readonly predicates: PredicateResult[][]; - readonly setProperties: any[]; - readonly assertedProperties: any[]; - readonly refutedProperties: any[]; - readonly matchLimit: number; - - delete(): void; - captures(node: SyntaxNode, options?: QueryOptions): QueryCapture[]; - matches(node: SyntaxNode, options?: QueryOptions): QueryMatch[]; - predicatesForPattern(patternIndex: number): PredicateResult[]; - disableCapture(captureName: string): void; - disablePattern(patternIndex: number): void; - isPatternGuaranteedAtStep(byteOffset: number): boolean; - isPatternRooted(patternIndex: number): boolean; - isPatternNonLocal(patternIndex: number): boolean; - startIndexForPattern(patternIndex: number): number; - didExceedMatchLimit(): boolean; - } - - class Language { - static load(input: string | Uint8Array): Promise; - - readonly version: number; - readonly fieldCount: number; - readonly stateCount: number; - readonly nodeTypeCount: number; - - fieldNameForId(fieldId: number): string | null; - fieldIdForName(fieldName: string): number | null; - idForNodeType(type: string, named: boolean): number; - nodeTypeForId(typeId: number): string | null; - nodeTypeIsNamed(typeId: number): boolean; - nodeTypeIsVisible(typeId: number): boolean; - nextState(stateId: number, typeId: number): number; - query(source: string): Query; - lookaheadIterator(stateId: number): LookaheadIterable | null; - } - - export class LookaheadIterable { - readonly language: Language; - readonly currentTypeId: number; - readonly currentType: string; - - delete(): void; - reset(language: Language, stateId: number): boolean; - resetState(stateId: number): boolean; - [Symbol.iterator](): Iterator; - } - } - - export = Parser; -} From 76016fd24d05243b2ff7e597e7ea37d17476fa59 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 6 May 2025 12:04:03 +0200 Subject: [PATCH 2/3] Use new createQuery Api --- packages/common/src/types/TreeSitter.ts | 19 ++++++++++--------- .../disabledComponents/DisabledTreeSitter.ts | 6 +++--- .../src/languages/LanguageDefinition.ts | 10 +++++----- packages/cursorless-vscode/src/extension.ts | 2 +- packages/vscode-common/src/getExtensionApi.ts | 4 ++-- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/common/src/types/TreeSitter.ts b/packages/common/src/types/TreeSitter.ts index c3b22e6d07..5127df6a9e 100644 --- a/packages/common/src/types/TreeSitter.ts +++ b/packages/common/src/types/TreeSitter.ts @@ -1,5 +1,5 @@ import type { Range, TextDocument } from "@cursorless/common"; -import type { Language, Node, Tree } from "web-tree-sitter"; +import type { Node, Query, Tree } from "web-tree-sitter"; export interface TreeSitter { /** @@ -12,14 +12,6 @@ export interface TreeSitter { */ getTree(document: TextDocument): Tree; - /** - * Gets a language if it is loaded - * - * @param languageId The language id of the language to get - * @returns The language if it is already loaded - */ - getLanguage(languageId: string): Language | undefined; - /** * Loads a language, returning true if it was successfully loaded * @@ -27,4 +19,13 @@ export interface TreeSitter { * @returns `true` if the language was successfully loaded */ loadLanguage(languageId: string): Promise; + + /** + * Create a query if the language is loaded. + * + * @param languageId The language id of the language to get + * @param source The query source + * @returns The query if that language is already loaded + */ + createQuery(languageId: string, source: string): Query | undefined; } diff --git a/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts b/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts index fdf3820a20..41263578b5 100644 --- a/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts +++ b/packages/cursorless-engine/src/disabledComponents/DisabledTreeSitter.ts @@ -1,5 +1,5 @@ import type { Range, TextDocument, TreeSitter } from "@cursorless/common"; -import type { Language, Node, Tree } from "web-tree-sitter"; +import type { Node, Query, Tree } from "web-tree-sitter"; export class DisabledTreeSitter implements TreeSitter { getTree(_document: TextDocument): Tree { @@ -10,11 +10,11 @@ export class DisabledTreeSitter implements TreeSitter { return Promise.resolve(false); } - getLanguage(_languageId: string): Language | undefined { + getNodeAtLocation(_document: TextDocument, _range: Range): Node { throw new Error("Tree sitter not provided"); } - getNodeAtLocation(_document: TextDocument, _range: Range): Node { + createQuery(_languageId: string, _source: string): Query | undefined { throw new Error("Tree sitter not provided"); } } diff --git a/packages/cursorless-engine/src/languages/LanguageDefinition.ts b/packages/cursorless-engine/src/languages/LanguageDefinition.ts index 19d3ed275d..79aa2e6b2d 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinition.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinition.ts @@ -60,14 +60,14 @@ export class LanguageDefinition { return undefined; } - const language = treeSitter.getLanguage(languageId); + const rawQuery = treeSitter.createQuery(languageId, rawLanguageQueryString); - if (language == null) { - throw Error(`Could not get Tree sitter language for ${languageId}`); + if (rawQuery == null) { + throw Error( + `Could not create Tree sitter query for language ${languageId}`, + ); } - const rawQuery = new Query(language, rawLanguageQueryString); - const query = TreeSitterQuery.create(languageId, treeSitter, rawQuery); return new LanguageDefinition(query); diff --git a/packages/cursorless-vscode/src/extension.ts b/packages/cursorless-vscode/src/extension.ts index 3873024ac0..9293574f1b 100644 --- a/packages/cursorless-vscode/src/extension.ts +++ b/packages/cursorless-vscode/src/extension.ts @@ -265,7 +265,7 @@ function createTreeSitter(parseTreeApi: ParseTreeApi): TreeSitter { }, loadLanguage: parseTreeApi.loadLanguage, - getLanguage: parseTreeApi.getLanguage, + createQuery: parseTreeApi.createQuery, }; } diff --git a/packages/vscode-common/src/getExtensionApi.ts b/packages/vscode-common/src/getExtensionApi.ts index f454a9ae6f..3da201b93a 100644 --- a/packages/vscode-common/src/getExtensionApi.ts +++ b/packages/vscode-common/src/getExtensionApi.ts @@ -1,6 +1,6 @@ import type { CommandServerApi } from "@cursorless/common"; import * as vscode from "vscode"; -import type { Language, Node, Tree } from "web-tree-sitter"; +import type { Node, Query, Tree } from "web-tree-sitter"; import type { VscodeTestHelpers } from "./TestHelpers"; export interface CursorlessApi { @@ -11,7 +11,7 @@ export interface ParseTreeApi { getNodeAtLocation(location: vscode.Location): Node; getTreeForUri(uri: vscode.Uri): Tree; loadLanguage: (languageId: string) => Promise; - getLanguage(languageId: string): Language | undefined; + createQuery(languageId: string, source: string): Query | undefined; } export async function getExtensionApi(extensionId: string) { From 814b9a0f34f1363710018a9c8d37addbea128347 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 6 May 2025 12:41:47 +0200 Subject: [PATCH 3/3] Include Tree sitter types --- packages/common/package.json | 3 +- packages/cursorless-engine/package.json | 1 - .../src/languages/LanguageDefinition.ts | 1 - pnpm-lock.yaml | 11 - typings/global.d.ts | 1 - typings/treeSitter.d.ts | 1047 +++++++++++++++++ 6 files changed, 1048 insertions(+), 16 deletions(-) delete mode 100644 typings/global.d.ts create mode 100644 typings/treeSitter.d.ts diff --git a/packages/common/package.json b/packages/common/package.json index 6e0ef5593a..a072e9f09d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -26,8 +26,7 @@ "cross-spawn": "7.0.5", "fast-check": "3.22.0", "js-yaml": "^4.1.0", - "mocha": "^10.7.3", - "web-tree-sitter": "^0.25.3" + "mocha": "^10.7.3" }, "types": "./out/index.d.ts", "exports": { diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 2ea200e905..503fdb5d8c 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -32,7 +32,6 @@ "nearley": "2.20.1", "talon-snippets": "1.3.0", "uuid": "^10.0.0", - "web-tree-sitter": "^0.25.3", "zod": "3.23.8" }, "devDependencies": { diff --git a/packages/cursorless-engine/src/languages/LanguageDefinition.ts b/packages/cursorless-engine/src/languages/LanguageDefinition.ts index 79aa2e6b2d..366daeb4c8 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinition.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinition.ts @@ -11,7 +11,6 @@ import { type IDE, type TextDocument, } from "@cursorless/common"; -import { Query } from "web-tree-sitter"; import { TreeSitterScopeHandler } from "../processTargets/modifiers/scopeHandlers"; import { TreeSitterQuery } from "./TreeSitterQuery"; import type { QueryCapture } from "./TreeSitterQuery/QueryCapture"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3480ab9e4..b7b82005a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,9 +232,6 @@ importers: mocha: specifier: ^10.7.3 version: 10.7.3 - web-tree-sitter: - specifier: ^0.25.3 - version: 0.25.3 packages/cursorless-cheatsheet: dependencies: @@ -293,9 +290,6 @@ importers: uuid: specifier: ^10.0.0 version: 10.0.0 - web-tree-sitter: - specifier: ^0.25.3 - version: 0.25.3 zod: specifier: 3.23.8 version: 3.23.8 @@ -9795,9 +9789,6 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - web-tree-sitter@0.25.3: - resolution: {integrity: sha512-e0hdXG+nJ18Zd/QJFhSx0DNTSMz7miwUjKyJ/lglTnZo6ke08++BQzXkaeaqnGJFi9qq+nPJg2L8hYAjduToHQ==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -21410,8 +21401,6 @@ snapshots: web-namespaces@2.0.1: {} - web-tree-sitter@0.25.3: {} - webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} diff --git a/typings/global.d.ts b/typings/global.d.ts deleted file mode 100644 index 3eff59d4c1..0000000000 --- a/typings/global.d.ts +++ /dev/null @@ -1 +0,0 @@ -type EmscriptenModule = unknown; diff --git a/typings/treeSitter.d.ts b/typings/treeSitter.d.ts new file mode 100644 index 0000000000..27a0289789 --- /dev/null +++ b/typings/treeSitter.d.ts @@ -0,0 +1,1047 @@ +// From https://github.com/tree-sitter/tree-sitter/blob/a380e1a259667c4d78c30a81bd8005c72577629a/lib/binding_web/web-tree-sitter.d.ts +// License https://github.com/tree-sitter/tree-sitter/blob/a380e1a259667c4d78c30a81bd8005c72577629a/LICENSE + +type EmscriptenModule = unknown; + +declare module "web-tree-sitter" { + /** + * A position in a multi-line text document, in terms of rows and columns. + * + * Rows and columns are zero-based. + */ + export interface Point { + /** The zero-based row number. */ + row: number; + /** The zero-based column number. */ + column: number; + } + /** + * A range of positions in a multi-line text document, both in terms of bytes + * and of rows and columns. + */ + export interface Range { + /** The start position of the range. */ + startPosition: Point; + /** The end position of the range. */ + endPosition: Point; + /** The start index of the range. */ + startIndex: number; + /** The end index of the range. */ + endIndex: number; + } + /** + * A summary of a change to a text document. + */ + export interface Edit { + /** The start position of the change. */ + startPosition: Point; + /** The end position of the change before the edit. */ + oldEndPosition: Point; + /** The end position of the change after the edit. */ + newEndPosition: Point; + /** The start index of the change. */ + startIndex: number; + /** The end index of the change before the edit. */ + oldEndIndex: number; + /** The end index of the change after the edit. */ + newEndIndex: number; + } + /** + * A callback for parsing that takes an index and point, and should return a string. + */ + export type ParseCallback = ( + index: number, + position: Point, + ) => string | undefined; + /** + * A callback that receives the parse state during parsing. + */ + export type ProgressCallback = (progress: ParseState) => boolean; + /** + * A callback for logging messages. + * + * If `isLex` is `true`, the message is from the lexer, otherwise it's from the parser. + */ + export type LogCallback = (message: string, isLex: boolean) => void; + /** + * Options for parsing + * + * The `includedRanges` property is an array of {@link Range} objects that + * represent the ranges of text that the parser should include when parsing. + * + * The `progressCallback` property is a function that is called periodically + * during parsing to check whether parsing should be cancelled. + * + * See {@link Parser#parse} for more information. + */ + export interface ParseOptions { + /** + * An array of {@link Range} objects that + * represent the ranges of text that the parser should include when parsing. + * + * This sets the ranges of text that the parser should include when parsing. + * By default, the parser will always include entire documents. This + * function allows you to parse only a *portion* of a document but + * still return a syntax tree whose ranges match up with the document + * as a whole. You can also pass multiple disjoint ranges. + * If `ranges` is empty, then the entire document will be parsed. + * Otherwise, the given ranges must be ordered from earliest to latest + * in the document, and they must not overlap. That is, the following + * must hold for all `i` < `length - 1`: + * ```text + * ranges[i].end_byte <= ranges[i + 1].start_byte + * ``` + */ + includedRanges?: Range[]; + /** + * A function that is called periodically during parsing to check + * whether parsing should be cancelled. If the progress callback returns + * `true`, then parsing will be cancelled. You can also use this to instrument + * parsing and check where the parser is at in the document. The progress callback + * takes a single argument, which is a {@link ParseState} representing the current + * state of the parser. + */ + progressCallback?: (state: ParseState) => void; + } + /** + * A stateful object that is passed into the progress callback {@link ParseOptions#progressCallback} + * to provide the current state of the parser. + */ + export interface ParseState { + /** The byte offset in the document that the parser is at. */ + currentOffset: number; + /** Indicates whether the parser has encountered an error during parsing. */ + hasError: boolean; + } + /** + * The latest ABI version that is supported by the current version of the + * library. + * + * When Languages are generated by the Tree-sitter CLI, they are + * assigned an ABI version number that corresponds to the current CLI version. + * The Tree-sitter library is generally backwards-compatible with languages + * generated using older CLI versions, but is not forwards-compatible. + */ + export let LANGUAGE_VERSION: number; + /** + * The earliest ABI version that is supported by the current version of the + * library. + */ + export let MIN_COMPATIBLE_VERSION: number; + /** + * A stateful object that is used to produce a {@link Tree} based on some + * source code. + */ + export class Parser { + /** The parser's current language. */ + language: Language | null; + /** + * This must always be called before creating a Parser. + * + * You can optionally pass in options to configure the WASM module, the most common + * one being `locateFile` to help the module find the `.wasm` file. + */ + static init(moduleOptions?: EmscriptenModule): Promise; + /** + * Create a new parser. + */ + constructor(); + /** Delete the parser, freeing its resources. */ + delete(): void; + /** + * Set the language that the parser should use for parsing. + * + * If the language was not successfully assigned, an error will be thrown. + * This happens if the language was generated with an incompatible + * version of the Tree-sitter CLI. Check the language's version using + * {@link Language#version} and compare it to this library's + * {@link LANGUAGE_VERSION} and {@link MIN_COMPATIBLE_VERSION} constants. + */ + setLanguage(language: Language | null): this; + /** + * Parse a slice of UTF8 text. + * + * @param callback - The UTF8-encoded text to parse or a callback function. + * + * @param oldTree - A previous syntax tree parsed from the same document. If the text of the + * document has changed since `oldTree` was created, then you must edit `oldTree` to match + * the new text using {@link Tree#edit}. + * + * @param options - Options for parsing the text. + * This can be used to set the included ranges, or a progress callback. + * + * @returns A {@link Tree} if parsing succeeded, or `null` if: + * - The parser has not yet had a language assigned with {@link Parser#setLanguage}. + * - The progress callback returned true. + */ + parse( + callback: string | ParseCallback, + oldTree?: Tree | null, + options?: ParseOptions, + ): Tree | null; + /** + * Instruct the parser to start the next parse from the beginning. + * + * If the parser previously failed because of a timeout, cancellation, + * or callback, then by default, it will resume where it left off on the + * next call to {@link Parser#parse} or other parsing functions. + * If you don't want to resume, and instead intend to use this parser to + * parse some other document, you must call `reset` first. + */ + reset(): void; + /** Get the ranges of text that the parser will include when parsing. */ + getIncludedRanges(): Range[]; + /** + * @deprecated since version 0.25.0, prefer passing a progress callback to {@link Parser#parse} + * + * Get the duration in microseconds that parsing is allowed to take. + * + * This is set via {@link Parser#setTimeoutMicros}. + */ + getTimeoutMicros(): number; + /** + * @deprecated since version 0.25.0, prefer passing a progress callback to {@link Parser#parse} + * + * Set the maximum duration in microseconds that parsing should be allowed + * to take before halting. + * + * If parsing takes longer than this, it will halt early, returning `null`. + * See {@link Parser#parse} for more information. + */ + setTimeoutMicros(timeout: number): void; + /** Set the logging callback that a parser should use during parsing. */ + setLogger(callback: LogCallback | boolean | null): this; + /** Get the parser's current logger. */ + getLogger(): LogCallback | null; + } + class LanguageMetadata { + readonly major_version: number; + readonly minor_version: number; + readonly patch_version: number; + } + /** + * An opaque object that defines how to parse a particular language. + * The code for each `Language` is generated by the Tree-sitter CLI. + */ + export class Language { + /** + * A list of all node types in the language. The index of each type in this + * array is its node type id. + */ + types: string[]; + /** + * A list of all field names in the language. The index of each field name in + * this array is its field id. + */ + fields: (string | null)[]; + /** + * Gets the name of the language. + */ + get name(): string | null; + /** + * @deprecated since version 0.25.0, use {@link Language#abiVersion} instead + * Gets the version of the language. + */ + get version(): number; + /** + * Gets the ABI version of the language. + */ + get abiVersion(): number; + /** + * Get the metadata for this language. This information is generated by the + * CLI, and relies on the language author providing the correct metadata in + * the language's `tree-sitter.json` file. + */ + get metadata(): LanguageMetadata | null; + /** + * Gets the number of fields in the language. + */ + get fieldCount(): number; + /** + * Gets the number of states in the language. + */ + get stateCount(): number; + /** + * Get the field id for a field name. + */ + fieldIdForName(fieldName: string): number | null; + /** + * Get the field name for a field id. + */ + fieldNameForId(fieldId: number): string | null; + /** + * Get the node type id for a node type name. + */ + idForNodeType(type: string, named: boolean): number | null; + /** + * Gets the number of node types in the language. + */ + get nodeTypeCount(): number; + /** + * Get the node type name for a node type id. + */ + nodeTypeForId(typeId: number): string | null; + /** + * Check if a node type is named. + * + * @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/2-basic-parsing.html#named-vs-anonymous-nodes} + */ + nodeTypeIsNamed(typeId: number): boolean; + /** + * Check if a node type is visible. + */ + nodeTypeIsVisible(typeId: number): boolean; + /** + * Get the supertypes ids of this language. + * + * @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types.html?highlight=supertype#supertype-nodes} + */ + get supertypes(): number[]; + /** + * Get the subtype ids for a given supertype node id. + */ + subtypes(supertype: number): number[]; + /** + * Get the next state id for a given state id and node type id. + */ + nextState(stateId: number, typeId: number): number; + /** + * Create a new lookahead iterator for this language and parse state. + * + * This returns `null` if state is invalid for this language. + * + * Iterating {@link LookaheadIterator} will yield valid symbols in the given + * parse state. Newly created lookahead iterators will return the `ERROR` + * symbol from {@link LookaheadIterator#currentType}. + * + * Lookahead iterators can be useful for generating suggestions and improving + * syntax error diagnostics. To get symbols valid in an `ERROR` node, use the + * lookahead iterator on its first leaf node state. For `MISSING` nodes, a + * lookahead iterator created on the previous non-extra leaf node may be + * appropriate. + */ + lookaheadIterator(stateId: number): LookaheadIterator | null; + /** + * @deprecated since version 0.25.0, call `new` on a {@link Query} instead + * + * 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} + */ + query(source: string): Query; + /** + * Load a language from a WebAssembly module. + * The module can be provided as a path to a file or as a buffer. + */ + static load(input: string | Uint8Array): Promise; + } + /** A tree that represents the syntactic structure of a source code file. */ + export class Tree { + /** The language that was used to parse the syntax tree. */ + language: Language; + /** Create a shallow copy of the syntax tree. This is very fast. */ + copy(): Tree; + /** Delete the syntax tree, freeing its resources. */ + delete(): void; + /** Get the root node of the syntax tree. */ + get rootNode(): Node; + /** + * Get the root node of the syntax tree, but with its position shifted + * forward by the given offset. + */ + rootNodeWithOffset(offsetBytes: number, offsetExtent: Point): Node; + /** + * 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: Edit): void; + /** Create a new {@link TreeCursor} starting from the root of the tree. */ + walk(): TreeCursor; + /** + * 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: Tree): Range[]; + /** Get the included ranges that were used to parse the syntax tree. */ + getIncludedRanges(): Range[]; + } + /** A single node within a syntax {@link Tree}. */ + export class Node { + /** + * 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: number; + /** The byte index where this node starts. */ + startIndex: number; + /** The position where this node starts. */ + startPosition: Point; + /** The tree that this node belongs to. */ + tree: Tree; + /** Get this node's type as a numerical id. */ + get typeId(): number; + /** + * Get the node's type as a numerical id as it appears in the grammar, + * ignoring aliases. + */ + get grammarId(): number; + /** Get this node's type as a string. */ + get type(): string; + /** + * Get this node's symbol name as it appears in the grammar, ignoring + * aliases as a string. + */ + get grammarType(): string; + /** + * 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(): boolean; + /** + * 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(): boolean; + /** + * 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(): boolean; + /** + * 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(): boolean; + /** Check if this node has been edited. */ + get hasChanges(): boolean; + /** + * Check if this node represents a syntax error or contains any syntax + * errors anywhere within it. + */ + get hasError(): boolean; + /** Get the byte index where this node ends. */ + get endIndex(): number; + /** Get the position where this node ends. */ + get endPosition(): Point; + /** Get the string content of this node. */ + get text(): string; + /** Get this node's parse state. */ + get parseState(): number; + /** Get the parse state after this node. */ + get nextParseState(): number; + /** Check if this node is equal to another node. */ + equals(other: Node): boolean; + /** + * 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: number): Node | null; + /** + * 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: number): Node | null; + /** + * 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: number): Node | null; + /** + * 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: string): Node | null; + /** Get the field name of this node's child at the given index. */ + fieldNameForChild(index: number): string | null; + /** Get the field name of this node's named child at the given index. */ + fieldNameForNamedChild(index: number): string | null; + /** + * Get an array of this node's children with a given field name. + * + * See also {@link Node#children}. + */ + childrenForFieldName(fieldName: string): Node[]; + /** + * Get an array of this node's children with a given field id. + * + * See also {@link Node#childrenForFieldName}. + */ + childrenForFieldId(fieldId: number): Node[]; + /** Get the node's first child that contains or starts after the given byte offset. */ + firstChildForIndex(index: number): Node | null; + /** Get the node's first named child that contains or starts after the given byte offset. */ + firstNamedChildForIndex(index: number): Node | null; + /** Get this node's number of children. */ + get childCount(): number; + /** + * Get this node's number of *named* children. + * + * See also {@link Node#isNamed}. + */ + get namedChildCount(): number; + /** Get this node's first child. */ + get firstChild(): Node | null; + /** + * Get this node's first named child. + * + * See also {@link Node#isNamed}. + */ + get firstNamedChild(): Node | null; + /** Get this node's last child. */ + get lastChild(): Node | null; + /** + * Get this node's last named child. + * + * See also {@link Node#isNamed}. + */ + get lastNamedChild(): Node | null; + /** + * 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(): Node[]; + /** + * Iterate over this node's named children. + * + * See also {@link Node#children}. + */ + get namedChildren(): Node[]; + /** + * 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: string | string[], + startPosition?: Point, + endPosition?: Point, + ): Node[]; + /** Get this node's next sibling. */ + get nextSibling(): Node | null; + /** Get this node's previous sibling. */ + get previousSibling(): Node | null; + /** + * Get this node's next *named* sibling. + * + * See also {@link Node#isNamed}. + */ + get nextNamedSibling(): Node | null; + /** + * Get this node's previous *named* sibling. + * + * See also {@link Node#isNamed}. + */ + get previousNamedSibling(): Node | null; + /** Get the node's number of descendants, including one for the node itself. */ + get descendantCount(): number; + /** + * Get this node's immediate parent. + * Prefer {@link Node#childWithDescendant} for iterating over this node's ancestors. + */ + get parent(): Node | null; + /** + * Get the node that contains `descendant`. + * + * Note that this can return `descendant` itself. + */ + childWithDescendant(descendant: Node): Node | null; + /** Get the smallest node within this node that spans the given byte range. */ + descendantForIndex(start: number, end?: number): Node | null; + /** Get the smallest named node within this node that spans the given byte range. */ + namedDescendantForIndex(start: number, end?: number): Node | null; + /** Get the smallest node within this node that spans the given point range. */ + descendantForPosition(start: Point, end?: Point): Node | null; + /** Get the smallest named node within this node that spans the given point range. */ + namedDescendantForPosition(start: Point, end?: Point): Node | null; + /** + * 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(): TreeCursor; + /** + * 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: Edit): void; + /** Get the S-expression representation of this node. */ + toString(): string; + } + /** A stateful object for walking a syntax {@link Tree} efficiently. */ + export class TreeCursor { + /** Creates a deep copy of the tree cursor. This allocates new memory. */ + copy(): TreeCursor; + /** Delete the tree cursor, freeing its resources. */ + delete(): void; + /** Get the tree cursor's current {@link Node}. */ + get currentNode(): Node; + /** + * Get the numerical field id of this tree cursor's current node. + * + * See also {@link TreeCursor#currentFieldName}. + */ + get currentFieldId(): number; + /** Get the field name of this tree cursor's current node. */ + get currentFieldName(): string | null; + /** + * Get the depth of the cursor's current node relative to the original + * node that the cursor was constructed with. + */ + get currentDepth(): number; + /** + * 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; + /** Get the type of the cursor's current node. */ + get nodeType(): string; + /** Get the type id of the cursor's current node. */ + get nodeTypeId(): number; + /** Get the state id of the cursor's current node. */ + get nodeStateId(): number; + /** Get the id of the cursor's current node. */ + get nodeId(): number; + /** + * 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; + /** + * 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; + /** Get the string content of the cursor's current node. */ + get nodeText(): string; + /** Get the start position of the cursor's current node. */ + get startPosition(): Point; + /** Get the end position of the cursor's current node. */ + get endPosition(): Point; + /** Get the start index of the cursor's current node. */ + get startIndex(): number; + /** Get the end index of the cursor's current node. */ + get endIndex(): number; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * Re-initialize this tree cursor to start at the original node that the + * cursor was constructed with. + */ + reset(node: Node): void; + /** + * 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; + } + /** + * Options for query execution + */ + export interface QueryOptions { + /** The start position of the range to query */ + startPosition?: Point; + /** The end position of the range to query */ + endPosition?: Point; + /** The start index of the range to query */ + startIndex?: number; + /** The end index of the range to query */ + endIndex?: number; + /** + * The maximum number of in-progress matches for this query. + * The limit must be > 0 and <= 65536. + */ + matchLimit?: number; + /** + * 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 `null` to remove the maximum start depth. + */ + maxStartDepth?: number; + /** + * The maximum duration in microseconds that query execution should be allowed to + * take before halting. + * + * If query execution takes longer than this, it will halt early, returning an empty array. + */ + timeoutMicros?: number; + /** + * A function that will be called periodically during the execution of the query to check + * if query execution should be cancelled. You can also use this to instrument query execution + * and check where the query is at in the document. The progress callback takes a single argument, + * which is a {@link QueryState} representing the current state of the query. + */ + progressCallback?: (state: QueryState) => void; + } + /** + * A stateful object that is passed into the progress callback {@link QueryOptions#progressCallback} + * to provide the current state of the query. + */ + export interface QueryState { + /** The byte offset in the document that the query is at. */ + currentOffset: number; + } + /** A record of key-value pairs associated with a particular pattern in a {@link Query}. */ + export type QueryProperties = Record; + /** + * A predicate that contains an operator and list of operands. + */ + export interface QueryPredicate { + /** The operator of the predicate, like `match?`, `eq?`, `set!`, etc. */ + operator: string; + /** The operands of the predicate, which are either captures or strings. */ + operands: PredicateStep[]; + } + /** + * A particular {@link Node} that has been captured with a particular name within a + * {@link Query}. + */ + export interface QueryCapture { + /** The index of the pattern that matched. */ + patternIndex: number; + /** The name of the capture */ + name: string; + /** The captured node */ + node: Node; + /** The properties for predicates declared with the operator `set!`. */ + setProperties?: QueryProperties; + /** The properties for predicates declared with the operator `is?`. */ + assertedProperties?: QueryProperties; + /** The properties for predicates declared with the operator `is-not?`. */ + refutedProperties?: QueryProperties; + } + /** A match of a {@link Query} to a particular set of {@link Node}s. */ + export interface QueryMatch { + /** @deprecated since version 0.25.0, use `patternIndex` instead. */ + pattern: number; + /** The index of the pattern that matched. */ + patternIndex: number; + /** The captures associated with the match. */ + captures: QueryCapture[]; + /** The properties for predicates declared with the operator `set!`. */ + setProperties?: QueryProperties; + /** The properties for predicates declared with the operator `is?`. */ + assertedProperties?: QueryProperties; + /** The properties for predicates declared with the operator `is-not?`. */ + refutedProperties?: QueryProperties; + } + /** A quantifier for captures */ + export const CaptureQuantifier: { + readonly Zero: 0; + readonly ZeroOrOne: 1; + readonly ZeroOrMore: 2; + readonly One: 3; + readonly OneOrMore: 4; + }; + /** A quantifier for captures */ + export type CaptureQuantifier = + (typeof CaptureQuantifier)[keyof typeof CaptureQuantifier]; + /** + * Predicates are represented as a single array of steps. There are two + * types of steps, which correspond to the two legal values for + * the `type` field: + * + * - `CapturePredicateStep` - Steps with this type represent names + * of captures. + * + * - `StringPredicateStep` - Steps with this type represent literal + * strings. + */ + export type PredicateStep = CapturePredicateStep | StringPredicateStep; + /** + * A step in a predicate that refers to a capture. + * + * The `name` field is the name of the capture. + */ + interface CapturePredicateStep { + type: "capture"; + name: string; + } + /** + * A step in a predicate that refers to a string. + * + * The `value` field is the string value. + */ + interface StringPredicateStep { + type: "string"; + value: string; + } + export class Query { + /** The names of the captures used in the query. */ + readonly captureNames: string[]; + /** The quantifiers of the captures used in the query. */ + readonly captureQuantifiers: CaptureQuantifier[][]; + /** + * 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!` + */ + readonly predicates: QueryPredicate[][]; + /** The properties for predicates with the operator `set!`. */ + readonly setProperties: QueryProperties[]; + /** The properties for predicates with the operator `is?`. */ + readonly assertedProperties: QueryProperties[]; + /** The properties for predicates with the operator `is-not?`. */ + readonly refutedProperties: QueryProperties[]; + /** The maximum number of in-progress matches for this cursor. */ + matchLimit?: number; + /** + * 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: Language, source: string); + /** Delete the query, freeing its resources. */ + delete(): void; + /** + * Iterate over all of the matches in the order that they were found. + * + * Each match contains the index of the pattern that matched, and a list 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. + * + * @param node - The node to execute the query on. + * + * @param options - Options for query execution. + */ + matches(node: Node, options?: QueryOptions): QueryMatch[]; + /** + * Iterate over all of the individual captures in the order that they + * appear. + * + * This is useful if you don't care about which pattern matched, and just + * want a single, ordered sequence of captures. + * + * @param node - The node to execute the query on. + * + * @param options - Options for query execution. + */ + captures(node: Node, options?: QueryOptions): QueryCapture[]; + /** Get the predicates for a given pattern. */ + predicatesForPattern(patternIndex: number): QueryPredicate[]; + /** + * Disable a certain capture within a query. + * + * This prevents the capture from being returned in matches, and also + * avoids any resource usage associated with recording the capture. + */ + disableCapture(captureName: string): void; + /** + * Disable a certain pattern within a query. + * + * This prevents the pattern from matching, and also avoids any resource + * usage associated with the pattern. This throws an error if the pattern + * index is out of bounds. + */ + disablePattern(patternIndex: number): void; + /** + * Check if, on its last execution, this cursor exceeded its maximum number + * of in-progress matches. + */ + didExceedMatchLimit(): boolean; + /** Get the byte offset where the given pattern starts in the query's source. */ + startIndexForPattern(patternIndex: number): number; + /** Get the byte offset where the given pattern ends in the query's source. */ + endIndexForPattern(patternIndex: number): number; + /** Get the number of patterns in the query. */ + patternCount(): number; + /** Get the index for a given capture name. */ + captureIndexForName(captureName: string): number; + /** Check if a given pattern within a query has a single root node. */ + isPatternRooted(patternIndex: number): boolean; + /** Check if a given pattern within a query has a single root node. */ + isPatternNonLocal(patternIndex: number): boolean; + /** + * Check if a given step in a query is 'definite'. + * + * A query step is 'definite' if its parent pattern will be guaranteed to + * match successfully once it reaches the step. + */ + isPatternGuaranteedAtStep(byteIndex: number): boolean; + } + export class LookaheadIterator implements Iterable { + /** Get the current symbol of the lookahead iterator. */ + get currentTypeId(): number; + /** Get the current symbol name of the lookahead iterator. */ + get currentType(): string; + /** Delete the lookahead iterator, freeing its resources. */ + delete(): void; + /** + * Reset the lookahead iterator. + * + * This returns `true` if the language was set successfully and `false` + * otherwise. + */ + reset(language: Language, stateId: number): boolean; + /** + * Reset the lookahead iterator to another state. + * + * This returns `true` if the iterator was reset to the given state and + * `false` otherwise. + */ + resetState(stateId: number): boolean; + /** + * 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](): Iterator; + } + + export {}; +} + +//# sourceMappingURL=web-tree-sitter.d.ts.map