Skip to content

Commit 751df62

Browse files
Move languages to separate file
1 parent 71f7f39 commit 751df62

6 files changed

Lines changed: 134 additions & 127 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ https://code.visualstudio.com/api/references/vscode-api#extensions.
3131
It's straightforward to add any [language with a tree-sitter grammar](https://tree-sitter.github.io/tree-sitter/).
3232

3333
1. Add a dependency on the npm package for that language in [tree-sitter-wasms](https://github.com/cursorless-dev/tree-sitter-wasms)
34-
2. Add a language to the dictionary at the top of `./src/extension.ts`
34+
2. Add a language to the map in [`languages.ts`](./src/languages.ts)
3535
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).
3636
4. Run `npm install` and `npm run compile`, then hit `F5` in VSCode, with this project open, to test your changes.
3737
5. Submit a PR!

src/disabledLanguages.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as vscode from "vscode";
2+
import * as semver from "semver";
3+
4+
/**
5+
* FIXME: On newer vscode versions some Tree sitter parser throws memory errors
6+
* https://github.com/cursorless-dev/cursorless/issues/2879
7+
* https://github.com/cursorless-dev/vscode-parse-tree/issues/110
8+
*/
9+
10+
const isProblemVersion =
11+
semver.lt(vscode.version, "1.107.0") && semver.gte(vscode.version, "1.98.0");
12+
13+
const disabledLanguages = new Set(isProblemVersion ? ["latex", "swift"] : []);
14+
15+
export function isLanguageDisabled(languageId: string): boolean {
16+
return disabledLanguages.has(languageId);
17+
}
18+
19+
export function throwIfLanguageIsDisabled(languageId: string) {
20+
if (isLanguageDisabled(languageId)) {
21+
throw new Error(
22+
`${languageId} is disabled on vscode versions 1.98.0 through 1.06.3. See https://github.com/cursorless-dev/cursorless/issues/2879`,
23+
);
24+
}
25+
}

src/extension.ts

Lines changed: 36 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,33 @@
1-
import * as fs from "fs";
2-
import * as path from "path";
3-
import * as semver from "semver";
4-
import * as vscode from "vscode";
5-
import * as treeSitter from "web-tree-sitter";
1+
import * as path from "node:path";
2+
import type {
3+
ExtensionContext,
4+
TextDocument,
5+
TextDocumentChangeEvent,
6+
} from "vscode";
7+
import { window, workspace } from "vscode";
8+
import type { Tree } from "web-tree-sitter";
9+
import { Parser, Query, Language as TreeSitterLanguage } from "web-tree-sitter";
10+
import {
11+
isLanguageDisabled,
12+
throwIfLanguageIsDisabled,
13+
} from "./disabledLanguages";
614
import {
715
DeprecatedError,
816
LanguageFailedToLoad,
917
UnsupportedLanguageError,
1018
} from "./errors";
19+
import { languages } from "./languages";
1120
import { Trees } from "./Trees";
12-
import { isDocumentVisible } from "./utils";
13-
import type { Language } from "./types";
14-
15-
/* eslint-disable @typescript-eslint/naming-convention */
16-
17-
// Be sure to declare the language in package.json
18-
const languages: Record<string, Language | undefined> = {
19-
"java-properties": { module: "tree-sitter-properties" },
20-
"talon-list": { module: "tree-sitter-talon" },
21-
agda: { module: "tree-sitter-agda" },
22-
c: { module: "tree-sitter-c" },
23-
clojure: { module: "tree-sitter-clojure" },
24-
cpp: { module: "tree-sitter-cpp" },
25-
csharp: { module: "tree-sitter-c_sharp" },
26-
css: { module: "tree-sitter-css" },
27-
dart: { module: "tree-sitter-dart" },
28-
elixir: { module: "tree-sitter-elixir" },
29-
elm: { module: "tree-sitter-elm" },
30-
gdscript: { module: "tree-sitter-gdscript" },
31-
gleam: { module: "tree-sitter-gleam" },
32-
go: { module: "tree-sitter-go" },
33-
haskell: { module: "tree-sitter-haskell" },
34-
html: { module: "tree-sitter-html" },
35-
java: { module: "tree-sitter-java" },
36-
javascript: { module: "tree-sitter-javascript" },
37-
javascriptreact: { module: "tree-sitter-javascript" },
38-
json: { module: "tree-sitter-json" },
39-
jsonc: { module: "tree-sitter-json" },
40-
jsonl: { module: "tree-sitter-json" },
41-
julia: { module: "tree-sitter-julia" },
42-
kotlin: { module: "tree-sitter-kotlin" },
43-
latex: { module: "tree-sitter-latex" },
44-
lua: { module: "tree-sitter-lua" },
45-
markdown: { module: "tree-sitter-markdown" },
46-
nix: { module: "tree-sitter-nix" },
47-
perl: { module: "tree-sitter-perl" },
48-
php: { module: "tree-sitter-php" },
49-
properties: { module: "tree-sitter-properties" },
50-
python: { module: "tree-sitter-python" },
51-
r: { module: "tree-sitter-r" },
52-
ruby: { module: "tree-sitter-ruby" },
53-
rust: { module: "tree-sitter-rust" },
54-
scala: { module: "tree-sitter-scala" },
55-
scm: { module: "tree-sitter-query" },
56-
scss: { module: "tree-sitter-scss" },
57-
shellscript: { module: "tree-sitter-bash" },
58-
sparql: { module: "tree-sitter-sparql" },
59-
starlark: { module: "tree-sitter-python" },
60-
swift: { module: "tree-sitter-swift" },
61-
talon: { module: "tree-sitter-talon" },
62-
terraform: { module: "tree-sitter-hcl" },
63-
typescript: { module: "tree-sitter-typescript" },
64-
typescriptreact: { module: "tree-sitter-tsx" },
65-
xml: { module: "tree-sitter-xml" },
66-
yaml: { module: "tree-sitter-yaml" },
67-
zig: { module: "tree-sitter-zig" },
68-
};
21+
import { getWasmPath, isDocumentVisible } from "./utils";
6922

7023
// For some reason this crashes if we put it inside activate
7124
// Fix: this isn't a field, suppress package member coloring like Go
72-
const initParser = treeSitter.Parser.init();
25+
const initParser = Parser.init();
7326

74-
export function activate(context: vscode.ExtensionContext) {
27+
export function activate(context: ExtensionContext) {
7528
// Parse of all visible documents
7629
const trees = new Trees();
7730

78-
/**
79-
* FIXME: On newer vscode versions some Tree sitter parser throws memory errors
80-
* https://github.com/cursorless-dev/cursorless/issues/2879
81-
* https://github.com/cursorless-dev/vscode-parse-tree/issues/110
82-
*/
83-
const disabledLanguages =
84-
semver.lt(vscode.version, "1.107.0") && semver.gte(vscode.version, "1.98.0")
85-
? new Set(["latex", "swift"])
86-
: null;
87-
88-
const validateGetLanguage = (languageId: string) => {
89-
if (disabledLanguages?.has(languageId)) {
90-
throw new Error(
91-
`${languageId} is disabled on vscode versions 1.98.0 through 1.06.3. See https://github.com/cursorless-dev/cursorless/issues/2879`,
92-
);
93-
}
94-
};
95-
9631
/**
9732
* Load the parser model for a given language
9833
* @param languageId The vscode language id of the language to load
@@ -112,45 +47,31 @@ export function activate(context: vscode.ExtensionContext) {
11247
}
11348

11449
// Disabled on certain vscode versions due to memory errors in tree-sitter parsers
115-
if (disabledLanguages?.has(languageId)) {
50+
if (isLanguageDisabled(languageId)) {
11651
return false;
11752
}
11853

119-
const absolute = getWasmPath(language.module);
54+
const absolute = getWasmPath(context.extensionPath, language.module);
12055
const wasm = path.relative(process.cwd(), absolute);
12156

12257
await initParser;
12358

124-
const lang = await treeSitter.Language.load(wasm);
125-
const parser = new treeSitter.Parser();
59+
const lang = await TreeSitterLanguage.load(wasm);
60+
const parser = new Parser();
12661
parser.setLanguage(lang);
12762
language.parser = parser;
12863

12964
return true;
13065
}
13166

132-
function getWasmPath(moduleName: string): string {
133-
const absolute = path.join(
134-
context.extensionPath,
135-
"parsers",
136-
moduleName + ".wasm",
137-
);
138-
139-
if (!fs.existsSync(absolute)) {
140-
throw Error(`Parser ${moduleName} not found at ${absolute}`);
141-
}
142-
143-
return absolute;
144-
}
145-
14667
/**
14768
* Open a document and parse it, returning the resulting tree
14869
* @param document the document to open
14970
* @returns the resulting tree, or undefined if the language couldn't be loaded
15071
*/
15172
async function openDocument(
152-
document: vscode.TextDocument,
153-
): Promise<treeSitter.Tree | undefined> {
73+
document: TextDocument,
74+
): Promise<Tree | undefined> {
15475
const uriString = document.uri.toString();
15576
let tree = trees.get(uriString);
15677

@@ -186,9 +107,7 @@ export function activate(context: vscode.ExtensionContext) {
186107
* @param document the document to get the tree for
187108
* @returns the parse tree for the document
188109
*/
189-
async function getTree(
190-
document: vscode.TextDocument,
191-
): Promise<treeSitter.Tree> {
110+
async function getTree(document: TextDocument): Promise<Tree> {
192111
const uriString = document.uri.toString();
193112
let tree = trees.get(uriString);
194113

@@ -203,7 +122,7 @@ export function activate(context: vscode.ExtensionContext) {
203122
}
204123

205124
if (document.languageId in languages) {
206-
validateGetLanguage(document.languageId);
125+
throwIfLanguageIsDisabled(document.languageId);
207126
throw new LanguageFailedToLoad(document.languageId);
208127
}
209128

@@ -216,47 +135,44 @@ export function activate(context: vscode.ExtensionContext) {
216135
* @param source the source of the query
217136
* @returns the created query, or undefined if the language couldn't be loaded
218137
*/
219-
function createQuery(
220-
languageId: string,
221-
source: string,
222-
): treeSitter.Query | undefined {
138+
function createQuery(languageId: string, source: string): Query | undefined {
223139
const language = languages[languageId]?.parser?.language;
224140
if (language == null) {
225-
validateGetLanguage(languageId);
141+
throwIfLanguageIsDisabled(languageId);
226142
return undefined;
227143
}
228-
return new treeSitter.Query(language, source);
144+
return new Query(language, source);
229145
}
230146

231147
// NOTE: if you make this an async function, it seems to cause edit anomalies
232-
function onChange(edit: vscode.TextDocumentChangeEvent) {
148+
function onChange(edit: TextDocumentChangeEvent) {
233149
const language = languages[edit.document.languageId];
234150
if (language?.parser != null) {
235151
trees.updateTree(language.parser, edit);
236152
}
237153
}
238154

239155
async function openAllVisibleDocuments() {
240-
for (const editor of vscode.window.visibleTextEditors) {
156+
for (const editor of window.visibleTextEditors) {
241157
await openDocument(editor.document);
242158
}
243159
}
244160

245-
async function openDocumentIfVisible(document: vscode.TextDocument) {
161+
async function openDocumentIfVisible(document: TextDocument) {
246162
if (isDocumentVisible(document)) {
247163
await openDocument(document);
248164
}
249165
}
250166

251-
function closeDocument(document: vscode.TextDocument) {
167+
function closeDocument(document: TextDocument) {
252168
trees.delete(document.uri.toString());
253169
}
254170

255171
context.subscriptions.push(
256-
vscode.window.onDidChangeVisibleTextEditors(openAllVisibleDocuments),
257-
vscode.workspace.onDidChangeTextDocument(onChange),
258-
vscode.workspace.onDidCloseTextDocument(closeDocument),
259-
vscode.workspace.onDidOpenTextDocument(openDocumentIfVisible),
172+
window.onDidChangeVisibleTextEditors(openAllVisibleDocuments),
173+
workspace.onDidChangeTextDocument(onChange),
174+
workspace.onDidCloseTextDocument(closeDocument),
175+
workspace.onDidOpenTextDocument(openDocumentIfVisible),
260176
);
261177

262178
// Don't wait for the initial load, it takes too long to inspect the themes and causes VSCode extension host to hang

src/languages.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
import type { Parser } from "web-tree-sitter";
3+
4+
// Be sure to declare the language in package.json
5+
export const languages: Record<string, Language | undefined> = {
6+
"java-properties": { module: "tree-sitter-properties" },
7+
"talon-list": { module: "tree-sitter-talon" },
8+
agda: { module: "tree-sitter-agda" },
9+
c: { module: "tree-sitter-c" },
10+
clojure: { module: "tree-sitter-clojure" },
11+
cpp: { module: "tree-sitter-cpp" },
12+
csharp: { module: "tree-sitter-c_sharp" },
13+
css: { module: "tree-sitter-css" },
14+
dart: { module: "tree-sitter-dart" },
15+
elixir: { module: "tree-sitter-elixir" },
16+
elm: { module: "tree-sitter-elm" },
17+
gdscript: { module: "tree-sitter-gdscript" },
18+
gleam: { module: "tree-sitter-gleam" },
19+
go: { module: "tree-sitter-go" },
20+
haskell: { module: "tree-sitter-haskell" },
21+
html: { module: "tree-sitter-html" },
22+
java: { module: "tree-sitter-java" },
23+
javascript: { module: "tree-sitter-javascript" },
24+
javascriptreact: { module: "tree-sitter-javascript" },
25+
json: { module: "tree-sitter-json" },
26+
jsonc: { module: "tree-sitter-json" },
27+
jsonl: { module: "tree-sitter-json" },
28+
julia: { module: "tree-sitter-julia" },
29+
kotlin: { module: "tree-sitter-kotlin" },
30+
latex: { module: "tree-sitter-latex" },
31+
lua: { module: "tree-sitter-lua" },
32+
markdown: { module: "tree-sitter-markdown" },
33+
nix: { module: "tree-sitter-nix" },
34+
perl: { module: "tree-sitter-perl" },
35+
php: { module: "tree-sitter-php" },
36+
properties: { module: "tree-sitter-properties" },
37+
python: { module: "tree-sitter-python" },
38+
r: { module: "tree-sitter-r" },
39+
ruby: { module: "tree-sitter-ruby" },
40+
rust: { module: "tree-sitter-rust" },
41+
scala: { module: "tree-sitter-scala" },
42+
scm: { module: "tree-sitter-query" },
43+
scss: { module: "tree-sitter-scss" },
44+
shellscript: { module: "tree-sitter-bash" },
45+
sparql: { module: "tree-sitter-sparql" },
46+
starlark: { module: "tree-sitter-python" },
47+
swift: { module: "tree-sitter-swift" },
48+
talon: { module: "tree-sitter-talon" },
49+
terraform: { module: "tree-sitter-hcl" },
50+
typescript: { module: "tree-sitter-typescript" },
51+
typescriptreact: { module: "tree-sitter-tsx" },
52+
xml: { module: "tree-sitter-xml" },
53+
yaml: { module: "tree-sitter-yaml" },
54+
zig: { module: "tree-sitter-zig" },
55+
};
56+
57+
interface Language {
58+
module: string;
59+
parser?: Parser;
60+
}

src/types.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as fs from "node:fs";
2+
import * as path from "node:path";
13
import type { TextDocument } from "vscode";
24
import { window } from "vscode";
35

@@ -7,3 +9,13 @@ export function isDocumentVisible(document: TextDocument): boolean {
79
(editor) => editor.document.uri.toString() === uriString,
810
);
911
}
12+
13+
export function getWasmPath(extensionPath: string, moduleName: string): string {
14+
const absolute = path.join(extensionPath, "parsers", moduleName + ".wasm");
15+
16+
if (!fs.existsSync(absolute)) {
17+
throw Error(`Parser ${moduleName} not found at ${absolute}`);
18+
}
19+
20+
return absolute;
21+
}

0 commit comments

Comments
 (0)