diff --git a/.nvmrc b/.nvmrc index 373e6d5..a4a7a41 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v25.0.0 +v24.14.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e047a..995da37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to the "vscode-parse-tree" extension will be documented in this file. +## 0.51.0 (12 Mar 2026) + +Removed deprecated api endpoints: + +- `getLanguage` - Replaced by `createQuery` +- `getNodeAtLocation` - No direct replacement. Use scm queries and `createQuery` instead. +- `registerLanguage` - No replacement. We don't believe anyone is using this. + ## 0.33.0 (22 Apr 2025) ### Enhancements diff --git a/README.md b/README.md index f21051b..743ec19 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Parse tree -Exposes an api function that can be used to get a parse tree node for a given file location. +Exposes an api function that can be used to get a parse tree for a given file. ## Usage @@ -13,25 +13,13 @@ if (parseTreeExtension == null) { throw new Error("Depends on pokey.parse-tree extension"); } -const { getNodeAtLocation } = await parseTreeExtension.activate(); +const { loadLanguage } = await parseTreeExtension.activate(); ``` Don't forget to add an `extensionDependencies`-entry to `package.json` as described in https://code.visualstudio.com/api/references/vscode-api#extensions. -### Parsing a custom language - -If you'd like to add support for a new language, see the [Adding a new language](#adding-a-new-language) section below. Alternatively, your extension can register a custom language with this extension. Although this is not the preferred way to add a new language, it can be convenient when you have a parser that you don't believe belongs in the main extension. - -Parsing your own language is as simple as registering your `languageId` with an absolute path to your `.wasm` file: - -```ts -const { registerLanguage } = await parseTreeExtension.activate(); - -registerLanguage(languageId, wasmPath); -``` - ## Contributing ### Setup @@ -43,7 +31,7 @@ registerLanguage(languageId, wasmPath); It's straightforward to add any [language with a tree-sitter grammar](https://tree-sitter.github.io/tree-sitter/). 1. Add a dependency on the npm package for that language in [tree-sitter-wasms](https://github.com/cursorless-dev/tree-sitter-wasms) -2. Add a language to the dictionary at the top of `./src/extension.ts` +2. Add a language to the map in [`languages.ts`](./src/languages.ts) 3. Add a reference to `onLanguage:yourlang` to the [activationEvents section of package.json](package.json). `yourlang` must be a [VSCode language identifier](https://code.visualstudio.com/docs/languages/identifiers). 4. Run `npm install` and `npm run compile`, then hit `F5` in VSCode, with this project open, to test your changes. 5. Submit a PR! diff --git a/package-lock.json b/package-lock.json index 64e64da..1ea4618 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "parse-tree", - "version": "0.50.0", + "version": "0.51.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "parse-tree", - "version": "0.50.0", + "version": "0.51.0", "license": "MIT", "dependencies": { "semver": "^7.7.4", @@ -15,16 +15,16 @@ "devDependencies": { "@cursorless/tree-sitter-wasms": "0.7.0", "@eslint/js": "^10.0.1", - "@types/node": "^25.3.3", + "@types/node": "^24.12.0", "@types/semver": "^7.7.1", "@types/vscode": "1.98.0", "@vscode/vsce": "^3.7.1", - "eslint": "^10.0.2", + "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", "jiti": "^2.6.1", "prettier": "^3.8.1", "typescript": "^5.9.3", - "typescript-eslint": "^8.56.1" + "typescript-eslint": "^8.57.0" }, "engines": { "vscode": "^1.98.0" @@ -278,15 +278,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz", - "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==", + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.2", + "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", - "minimatch": "^10.2.1" + "minimatch": "^10.2.4" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -345,9 +345,9 @@ } }, "node_modules/@eslint/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", - "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -379,9 +379,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", - "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -389,13 +389,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", - "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0", + "@eslint/core": "^1.1.1", "levn": "^0.4.1" }, "engines": { @@ -795,13 +795,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", - "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/normalize-package-data": { @@ -833,17 +833,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", - "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/type-utils": "8.56.1", - "@typescript-eslint/utils": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -856,7 +856,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.56.1", + "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -872,16 +872,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", - "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { @@ -897,14 +897,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", - "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.1", - "@typescript-eslint/types": "^8.56.1", + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { @@ -919,14 +919,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", - "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1" + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -937,9 +937,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", - "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { @@ -954,15 +954,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", - "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -979,9 +979,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", - "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { @@ -993,16 +993,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", - "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.56.1", - "@typescript-eslint/tsconfig-utils": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1060,16 +1060,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", - "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1" + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1084,13 +1084,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", - "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2153,18 +2153,18 @@ } }, "node_modules/eslint": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz", - "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz", + "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.2", + "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", - "@eslint/core": "^1.1.0", - "@eslint/plugin-kit": "^0.6.0", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2173,7 +2173,7 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.1", + "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", @@ -2186,7 +2186,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.1", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2225,9 +2225,9 @@ } }, "node_modules/eslint-scope": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", - "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5003,16 +5003,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", - "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", + "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.56.1", - "@typescript-eslint/parser": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1" + "@typescript-eslint/eslint-plugin": "8.57.0", + "@typescript-eslint/parser": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5034,9 +5034,9 @@ "license": "MIT" }, "node_modules/underscore": { - "version": "1.13.7", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", - "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", "dev": true, "license": "MIT" }, @@ -5051,9 +5051,9 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index cbadeb9..607b19a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "parse-tree", "displayName": "Parse tree", "description": "Access document syntax using tree-sitter", - "version": "0.50.0", + "version": "0.51.0", "publisher": "pokey", "repository": { "type": "git", @@ -96,15 +96,15 @@ "devDependencies": { "@cursorless/tree-sitter-wasms": "0.7.0", "@eslint/js": "^10.0.1", - "@types/node": "^25.3.3", + "@types/node": "^24.12.0", "@types/semver": "^7.7.1", "@types/vscode": "1.98.0", "@vscode/vsce": "^3.7.1", "eslint-config-prettier": "^10.1.8", - "eslint": "^10.0.2", + "eslint": "^10.0.3", "jiti": "^2.6.1", "prettier": "^3.8.1", - "typescript-eslint": "^8.56.1", + "typescript-eslint": "^8.57.0", "typescript": "^5.9.3" } } diff --git a/src/Trees.ts b/src/Trees.ts new file mode 100644 index 0000000..414e1bb --- /dev/null +++ b/src/Trees.ts @@ -0,0 +1,64 @@ +import type { Position, TextDocumentChangeEvent } from "vscode"; +import { Edit, type Parser, type Point, type Tree } from "web-tree-sitter"; + +export class Trees { + private trees: Map = new Map(); + + get(uri: string): Tree | undefined { + return this.trees.get(uri); + } + + set(uri: string, tree: Tree): void { + this.trees.set(uri, tree); + } + + delete(uri: string): void { + this.trees.delete(uri); + } + + updateTree(parser: Parser, edit: TextDocumentChangeEvent) { + if (edit.contentChanges.length === 0) { + return; + } + + const uriString = edit.document.uri.toString(); + const old = this.trees.get(uriString); + + if (old == null) { + throw new Error(`No existing tree for ${uriString}`); + } + + for (const e of edit.contentChanges) { + const startIndex = e.rangeOffset; + const oldEndIndex = e.rangeOffset + e.rangeLength; + const newEndIndex = e.rangeOffset + e.text.length; + const startPos = edit.document.positionAt(startIndex); + const oldEndPos = edit.document.positionAt(oldEndIndex); + const newEndPos = edit.document.positionAt(newEndIndex); + const startPosition = asPoint(startPos); + const oldEndPosition = asPoint(oldEndPos); + const newEndPosition = asPoint(newEndPos); + const delta = new Edit({ + startIndex, + oldEndIndex, + newEndIndex, + startPosition, + oldEndPosition, + newEndPosition, + }); + old.edit(delta); + } + + const tree = parser.parse(edit.document.getText(), old); + + if (tree == null) { + throw Error(`Failed to parse ${uriString}`); + } + + this.trees.set(uriString, tree); + } +} + +function asPoint(pos: Position): Point { + return { row: pos.line, column: pos.character }; +} diff --git a/src/disabledLanguages.ts b/src/disabledLanguages.ts new file mode 100644 index 0000000..1760df5 --- /dev/null +++ b/src/disabledLanguages.ts @@ -0,0 +1,25 @@ +import * as vscode from "vscode"; +import * as semver from "semver"; + +/** + * FIXME: On newer vscode versions some Tree sitter parser throws memory errors + * https://github.com/cursorless-dev/cursorless/issues/2879 + * https://github.com/cursorless-dev/vscode-parse-tree/issues/110 + */ + +const isProblemVersion = + semver.lt(vscode.version, "1.107.0") && semver.gte(vscode.version, "1.98.0"); + +const disabledLanguages = new Set(isProblemVersion ? ["latex", "swift"] : []); + +export function isLanguageDisabled(languageId: string): boolean { + return disabledLanguages.has(languageId); +} + +export function throwIfLanguageIsDisabled(languageId: string) { + if (isLanguageDisabled(languageId)) { + throw new Error( + `${languageId} is disabled on vscode versions 1.98.0 through 1.06.3. See https://github.com/cursorless-dev/cursorless/issues/2879`, + ); + } +} diff --git a/src/errors.ts b/src/errors.ts index 23fd50c..2f4c519 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,3 +1,5 @@ +import type { Uri } from "vscode"; + export class UnsupportedLanguageError extends Error { constructor(language: string) { super( @@ -13,3 +15,24 @@ export class LanguageStillLoadingError extends Error { this.name = "LanguageStillLoadingError"; } } + +export class DeprecatedError extends Error { + constructor(name: string) { + super(`${name} is deprecated and has been removed from the API`); + this.name = "DeprecatedError"; + } +} + +export class FailedToParseError extends Error { + constructor(uri: Uri) { + super(`Failed to parse document: ${uri.toString()}`); + this.name = "FailedToParseError"; + } +} + +export class DocumentNotOpenError extends Error { + constructor(uri: Uri) { + super(`Document not open: ${uri.toString()}`); + this.name = "DocumentNotOpenError"; + } +} diff --git a/src/extension.ts b/src/extension.ts index 71921ec..f0dc4a6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,98 +1,38 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as semver from "semver"; -import * as vscode from "vscode"; -import * as treeSitter from "web-tree-sitter"; -import { Edit } from "web-tree-sitter"; -import { LanguageStillLoadingError, UnsupportedLanguageError } from "./errors"; - -interface Language { - module: string; - parser?: treeSitter.Parser; -} - -/* eslint-disable @typescript-eslint/naming-convention */ - -// Be sure to declare the language in package.json and include a minimalist grammar. -const languages: Record = { - "java-properties": { module: "tree-sitter-properties" }, - "talon-list": { module: "tree-sitter-talon" }, - agda: { module: "tree-sitter-agda" }, - c: { module: "tree-sitter-c" }, - clojure: { module: "tree-sitter-clojure" }, - cpp: { module: "tree-sitter-cpp" }, - csharp: { module: "tree-sitter-c_sharp" }, - css: { module: "tree-sitter-css" }, - dart: { module: "tree-sitter-dart" }, - elixir: { module: "tree-sitter-elixir" }, - elm: { module: "tree-sitter-elm" }, - gdscript: { module: "tree-sitter-gdscript" }, - gleam: { module: "tree-sitter-gleam" }, - go: { module: "tree-sitter-go" }, - haskell: { module: "tree-sitter-haskell" }, - html: { module: "tree-sitter-html" }, - java: { module: "tree-sitter-java" }, - javascript: { module: "tree-sitter-javascript" }, - javascriptreact: { module: "tree-sitter-javascript" }, - json: { module: "tree-sitter-json" }, - jsonc: { module: "tree-sitter-json" }, - jsonl: { module: "tree-sitter-json" }, - julia: { module: "tree-sitter-julia" }, - kotlin: { module: "tree-sitter-kotlin" }, - latex: { module: "tree-sitter-latex" }, - lua: { module: "tree-sitter-lua" }, - markdown: { module: "tree-sitter-markdown" }, - nix: { module: "tree-sitter-nix" }, - perl: { module: "tree-sitter-perl" }, - php: { module: "tree-sitter-php" }, - properties: { module: "tree-sitter-properties" }, - python: { module: "tree-sitter-python" }, - r: { module: "tree-sitter-r" }, - ruby: { module: "tree-sitter-ruby" }, - rust: { module: "tree-sitter-rust" }, - scala: { module: "tree-sitter-scala" }, - scm: { module: "tree-sitter-query" }, - scss: { module: "tree-sitter-scss" }, - shellscript: { module: "tree-sitter-bash" }, - sparql: { module: "tree-sitter-sparql" }, - starlark: { module: "tree-sitter-python" }, - swift: { module: "tree-sitter-swift" }, - talon: { module: "tree-sitter-talon" }, - terraform: { module: "tree-sitter-hcl" }, - typescript: { module: "tree-sitter-typescript" }, - typescriptreact: { module: "tree-sitter-tsx" }, - xml: { module: "tree-sitter-xml" }, - yaml: { module: "tree-sitter-yaml" }, - zig: { module: "tree-sitter-zig" }, -}; +import * as path from "node:path"; +import type { + ExtensionContext, + TextDocument, + TextDocumentChangeEvent, + Uri, +} from "vscode"; +import { window, workspace } from "vscode"; +import type { Tree } from "web-tree-sitter"; +import { Parser, Query, Language as TreeSitterLanguage } from "web-tree-sitter"; +import { + isLanguageDisabled, + throwIfLanguageIsDisabled, +} from "./disabledLanguages"; +import { + DeprecatedError, + LanguageStillLoadingError, + UnsupportedLanguageError, +} from "./errors"; +import { languages } from "./languages"; +import { Trees } from "./Trees"; +import { + getOpenDocument, + getWasmPath, + isDocumentVisible, + parseDocument, +} from "./utils"; // For some reason this crashes if we put it inside activate // Fix: this isn't a field, suppress package member coloring like Go -const initParser = treeSitter.Parser.init(); +const initParser = Parser.init(); -// Called when the extension is first activated by user opening a file with the appropriate language -export function activate(context: vscode.ExtensionContext) { - console.debug("Activating tree-sitter..."); +export function activate(context: ExtensionContext) { // Parse of all visible documents - const trees: Record = {}; - - /** - * FIXME: On newer vscode versions some Tree sitter parser throws memory errors - * https://github.com/cursorless-dev/cursorless/issues/2879 - * https://github.com/cursorless-dev/vscode-parse-tree/issues/110 - */ - const disabledLanguages = - semver.lt(vscode.version, "1.107.0") && semver.gte(vscode.version, "1.98.0") - ? new Set(["latex", "swift"]) - : null; - - const validateGetLanguage = (languageId: string) => { - if (disabledLanguages?.has(languageId)) { - throw new Error( - `${languageId} is disabled on vscode versions 1.98.0 through 1.06.3. See https://github.com/cursorless-dev/cursorless/issues/2879`, - ); - } - }; + const trees = new Trees(); /** * Load the parser model for a given language @@ -101,255 +41,169 @@ export function activate(context: vscode.ExtensionContext) { */ async function loadLanguage(languageId: string) { const language = languages[languageId]; + + // Language without a parser, e.g. plaintext if (language == null) { return false; } + + // Already loaded if (language.parser != null) { return true; } - if (disabledLanguages?.has(languageId)) { + // Disabled on certain vscode versions due to memory errors in tree-sitter parsers + if (isLanguageDisabled(languageId)) { return false; } - let absolute; - if (path.isAbsolute(language.module)) { - absolute = language.module; - } else { - absolute = path.join( - context.extensionPath, - "parsers", - language.module + ".wasm", - ); - } - - if (!fs.existsSync(absolute)) { - throw Error(`Parser for ${languageId} not found at ${absolute}`); - } - + const absolute = getWasmPath(context.extensionPath, language.module); const wasm = path.relative(process.cwd(), absolute); + await initParser; - const lang = await treeSitter.Language.load(wasm); - const parser = new treeSitter.Parser(); + + const lang = await TreeSitterLanguage.load(wasm); + const parser = new Parser(); parser.setLanguage(lang); language.parser = parser; return true; } - async function open(document: vscode.TextDocument) { + /** + * Open a document and parse it, returning the resulting tree + * @param document the document to open + * @returns the resulting tree, or undefined if the language couldn't be loaded + */ + async function openDocument( + document: TextDocument, + ): Promise { const uriString = document.uri.toString(); - if (uriString in trees) { - return; + let tree = trees.get(uriString); + + // Document is already opened + if (tree != null) { + return tree; } + // Couldn't load language, skip opening document if (!(await loadLanguage(document.languageId))) { - return; + return undefined; } const language = languages[document.languageId]; + if (language?.parser == null) { throw new Error(`No parser for language ${document.languageId}`); } - const t = language.parser?.parse(document.getText()); - if (t == null) { - throw Error(`Failed to parse ${document.uri.toString()}`); - } - trees[uriString] = t; + + tree = parseDocument(language.parser, document); + + trees.set(uriString, tree); + + return tree; } - function openIfLanguageLoaded(document: vscode.TextDocument) { - const uriString = document.uri.toString(); - if (uriString in trees) { - return null; + /** + * Get the parse tree for a given document, parsing it if necessary + * @param document the document to get the tree for + * @returns the parse tree for the document + */ + function getTreeForUri(uri: Uri): Tree { + const uriString = uri.toString(); + let tree = trees.get(uriString); + + // Document is already opened + if (tree != null) { + return tree; } + const document = getOpenDocument(uri); const language = languages[document.languageId]; - if (language?.parser == null) { - return null; - } - const t = language.parser.parse(document.getText()); - if (t == null) { - throw Error(`Failed to parse ${document.uri.toString()}`); + // Language without a parser, e.g. plaintext + if (language == null) { + throw new UnsupportedLanguageError(document.languageId); } - trees[uriString] = t; - return t; - } - // NOTE: if you make this an async function, it seems to cause edit anomalies - function edit(edit: vscode.TextDocumentChangeEvent) { - const language = languages[edit.document.languageId]; - if (language == null || language.parser == null) { - return; + // Language definition exists, but the parser is not loaded. Could be a race + // condition or a disabled language. + if (language.parser == null) { + throwIfLanguageIsDisabled(document.languageId); + throw new LanguageStillLoadingError(document.languageId); } - updateTree(language.parser, edit); + + tree = parseDocument(language.parser, document); + trees.set(uriString, tree); + return tree; } - function updateTree( - parser: treeSitter.Parser, - edit: vscode.TextDocumentChangeEvent, - ) { - if (edit.contentChanges.length === 0) { - return; - } - const old = trees[edit.document.uri.toString()]; - if (old == null) { - throw new Error(`No existing tree for ${edit.document.uri.toString()}`); - } - for (const e of edit.contentChanges) { - const startIndex = e.rangeOffset; - const oldEndIndex = e.rangeOffset + e.rangeLength; - const newEndIndex = e.rangeOffset + e.text.length; - const startPos = edit.document.positionAt(startIndex); - const oldEndPos = edit.document.positionAt(oldEndIndex); - const newEndPos = edit.document.positionAt(newEndIndex); - const startPosition = asPoint(startPos); - const oldEndPosition = asPoint(oldEndPos); - const newEndPosition = asPoint(newEndPos); - const delta = new Edit({ - startIndex, - oldEndIndex, - newEndIndex, - startPosition, - oldEndPosition, - newEndPosition, - }); - old.edit(delta); - } - const t = parser.parse(edit.document.getText(), old); - if (t == null) { - throw Error(`Failed to parse ${edit.document.uri.toString()}`); + /** + * Create a tree-sitter query for a given language and query source + * @param languageId the vscode language id of the language to create the query for + * @param source the source of the query + * @returns the created query, or undefined if the language couldn't be loaded + */ + function createQuery(languageId: string, source: string): Query | undefined { + const language = languages[languageId]?.parser?.language; + if (language == null) { + throwIfLanguageIsDisabled(languageId); + return undefined; } - trees[edit.document.uri.toString()] = t; + return new Query(language, source); } - function asPoint(pos: vscode.Position): treeSitter.Point { - return { row: pos.line, column: pos.character }; + // NOTE: if you make this an async function, it seems to cause edit anomalies + function onChange(edit: TextDocumentChangeEvent) { + const language = languages[edit.document.languageId]; + if (language?.parser != null) { + trees.updateTree(language.parser, edit); + } } - function close(document: vscode.TextDocument) { - delete trees[document.uri.toString()]; + async function openAllVisibleDocuments() { + for (const editor of window.visibleTextEditors) { + await openDocument(editor.document); + } } - async function colorAllOpen() { - for (const editor of vscode.window.visibleTextEditors) { - await open(editor.document); + async function openDocumentIfVisible(document: TextDocument) { + if (isDocumentVisible(document)) { + await openDocument(document); } } - async function openIfVisible(document: vscode.TextDocument) { - if ( - vscode.window.visibleTextEditors.some( - (editor) => editor.document.uri.toString() === document.uri.toString(), - ) - ) { - await open(document); - } + function closeDocument(document: TextDocument) { + trees.delete(document.uri.toString()); } context.subscriptions.push( - vscode.window.onDidChangeVisibleTextEditors(colorAllOpen), - ); - context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(edit)); - context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(close)); - context.subscriptions.push( - vscode.workspace.onDidOpenTextDocument(openIfVisible), + window.onDidChangeVisibleTextEditors(openAllVisibleDocuments), + workspace.onDidChangeTextDocument(onChange), + workspace.onDidCloseTextDocument(closeDocument), + workspace.onDidOpenTextDocument(openDocumentIfVisible), ); - // Don't wait for the initial color, it takes too long to inspect the themes and causes VSCode extension host to hang - void colorAllOpen(); - - function getTreeForUri(uri: vscode.Uri) { - const ret = trees[uri.toString()]; - - if (ret == null) { - const document = vscode.workspace.textDocuments.find( - (textDocument) => textDocument.uri.toString() === uri.toString(), - ); - - if (document == null) { - throw new Error(`Document ${uri.toString()} is not open`); - } - - const ret = openIfLanguageLoaded(document); - - if (ret != null) { - return ret; - } - - const languageId = document.languageId; - - if (languageId in languages) { - validateGetLanguage(document.languageId); - throw new LanguageStillLoadingError(languageId); - } else { - throw new UnsupportedLanguageError(languageId); - } - } - - return ret; - } + // Don't wait for the initial load, it takes too long to inspect the themes and causes VSCode extension host to hang + void openAllVisibleDocuments(); return { loadLanguage, + createQuery, + getTreeForUri, - /** - * @deprecated - */ - getLanguage(languageId: string): treeSitter.Language | undefined { - console.warn( - "vscode-parse-tree: getLanguage is deprecated, use createQuery(languageId, source) instead.", - ); - validateGetLanguage(languageId); - return languages[languageId]?.parser?.language ?? undefined; - }, - - createQuery( - languageId: string, - source: string, - ): treeSitter.Query | undefined { - const language = languages[languageId]?.parser?.language; - if (language == null) { - validateGetLanguage(languageId); - return undefined; - } - return new treeSitter.Query(language, source); + getTree(document: TextDocument) { + return getTreeForUri(document.uri); }, - /** - * Register a parser wasm file for a language not supported by this - * extension. Note that {@link wasmPath} must be an absolute path, and - * {@link languageId} must not already have a registered parser. - * @param languageId The VSCode language id that you'd like to register a - * parser for - * @param wasmPath The absolute path to the wasm file for your parser - */ - registerLanguage(languageId: string, wasmPath: string) { - if (languages[languageId] != null) { - throw new Error(`Language id '${languageId}' is already registered`); - } - - languages[languageId] = { module: wasmPath }; - void colorAllOpen(); + getLanguage() { + throw new DeprecatedError("getLanguage"); }, - - getTree(document: vscode.TextDocument) { - return getTreeForUri(document.uri); + getNodeAtLocation() { + throw new DeprecatedError("getNodeAtLocation"); }, - - getTreeForUri, - - getNodeAtLocation(location: vscode.Location) { - return getTreeForUri(location.uri).rootNode.descendantForPosition({ - row: location.range.start.line, - column: location.range.start.character, - }); + registerLanguage() { + throw new DeprecatedError("registerLanguage"); }, }; } - -// this method is called when your extension is deactivated -export function deactivate() { - // Empty -} diff --git a/src/global.d.ts b/src/global.d.ts deleted file mode 100644 index 06d1c5e..0000000 --- a/src/global.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -interface EmscriptenModule {} diff --git a/src/languages.ts b/src/languages.ts new file mode 100644 index 0000000..4d6de15 --- /dev/null +++ b/src/languages.ts @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type { Parser } from "web-tree-sitter"; + +// Be sure to declare the language in package.json +export const languages: Record = { + "java-properties": { module: "tree-sitter-properties" }, + "talon-list": { module: "tree-sitter-talon" }, + agda: { module: "tree-sitter-agda" }, + c: { module: "tree-sitter-c" }, + clojure: { module: "tree-sitter-clojure" }, + cpp: { module: "tree-sitter-cpp" }, + csharp: { module: "tree-sitter-c_sharp" }, + css: { module: "tree-sitter-css" }, + dart: { module: "tree-sitter-dart" }, + elixir: { module: "tree-sitter-elixir" }, + elm: { module: "tree-sitter-elm" }, + gdscript: { module: "tree-sitter-gdscript" }, + gleam: { module: "tree-sitter-gleam" }, + go: { module: "tree-sitter-go" }, + haskell: { module: "tree-sitter-haskell" }, + html: { module: "tree-sitter-html" }, + java: { module: "tree-sitter-java" }, + javascript: { module: "tree-sitter-javascript" }, + javascriptreact: { module: "tree-sitter-javascript" }, + json: { module: "tree-sitter-json" }, + jsonc: { module: "tree-sitter-json" }, + jsonl: { module: "tree-sitter-json" }, + julia: { module: "tree-sitter-julia" }, + kotlin: { module: "tree-sitter-kotlin" }, + latex: { module: "tree-sitter-latex" }, + lua: { module: "tree-sitter-lua" }, + markdown: { module: "tree-sitter-markdown" }, + nix: { module: "tree-sitter-nix" }, + perl: { module: "tree-sitter-perl" }, + php: { module: "tree-sitter-php" }, + properties: { module: "tree-sitter-properties" }, + python: { module: "tree-sitter-python" }, + r: { module: "tree-sitter-r" }, + ruby: { module: "tree-sitter-ruby" }, + rust: { module: "tree-sitter-rust" }, + scala: { module: "tree-sitter-scala" }, + scm: { module: "tree-sitter-query" }, + scss: { module: "tree-sitter-scss" }, + shellscript: { module: "tree-sitter-bash" }, + sparql: { module: "tree-sitter-sparql" }, + starlark: { module: "tree-sitter-python" }, + swift: { module: "tree-sitter-swift" }, + talon: { module: "tree-sitter-talon" }, + terraform: { module: "tree-sitter-hcl" }, + typescript: { module: "tree-sitter-typescript" }, + typescriptreact: { module: "tree-sitter-tsx" }, + xml: { module: "tree-sitter-xml" }, + yaml: { module: "tree-sitter-yaml" }, + zig: { module: "tree-sitter-zig" }, +}; + +interface Language { + module: string; + parser?: Parser; +} diff --git a/src/treeSitter.d.ts b/src/treeSitter.d.ts new file mode 100644 index 0000000..7c42b98 --- /dev/null +++ b/src/treeSitter.d.ts @@ -0,0 +1,2 @@ +// Necessary because of faulty type in web-tree-sitter +type EmscriptenModule = unknown; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..b474e4a --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,46 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import type { TextDocument, Uri } from "vscode"; +import { window, workspace } from "vscode"; +import type { Parser, Tree } from "web-tree-sitter"; +import { DocumentNotOpenError, FailedToParseError } from "./errors"; + +export function isDocumentVisible(document: TextDocument): boolean { + const uriString = document.uri.toString(); + return window.visibleTextEditors.some( + (editor) => editor.document.uri.toString() === uriString, + ); +} + +export function getOpenDocument(uri: Uri): TextDocument { + const uriString = uri.toString(); + const document = workspace.textDocuments.find( + (doc) => doc.uri.toString() === uriString, + ); + + if (document == null) { + throw new DocumentNotOpenError(uri); + } + + return document; +} + +export function getWasmPath(extensionPath: string, moduleName: string): string { + const absolute = path.join(extensionPath, "parsers", moduleName + ".wasm"); + + if (!fs.existsSync(absolute)) { + throw Error(`Parser ${moduleName} not found at ${absolute}`); + } + + return absolute; +} + +export function parseDocument(parser: Parser, document: TextDocument): Tree { + const tree = parser.parse(document.getText()); + + if (tree == null) { + throw new FailedToParseError(document.uri); + } + + return tree; +}