Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

- `toml` and `xml` support, thanks [@sanarise](https://github.com/selfint/code-blocks/pull/186)!

- `css` support ([#191](https://github.com/selfint/code-blocks/issues/191)) by switching to more robust Parser loading method.

## Fixed

- Add 'Remove' option to notification if parser fails to load ([#180](https://github.com/selfint/code-blocks/issues/180)).
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ View your code's syntax tree directly

- `node` / `npm`: Used to download tree-sitter language parsers. Can be installed from [here](https://nodejs.org/en/download).

- **OPTIONAL:** `tree-sitter` / `node-gyp-build`: Used for tree-sitter language parsers that need to be locally built.
- **OPTIONAL:** `tree-sitter`: Used for tree-sitter language parsers that need to be locally built.

After installing `npm`, can be installed by running:
`npm i -g tree-sitter-cli node-gyp-build`.
`npm i -g tree-sitter-cli`.

If you don't want to install these tools, there's a good chance you don't
need them. Try the extension without it, it will notify you if they are required.
Expand Down Expand Up @@ -117,8 +117,7 @@ Or [create a pull request](https://github.com/selfint/code-blocks/pulls) with yo
- `codeBlocks.npmPackageName`: [NPM](https://www.npmjs.com/) package name of the `tree-sitter` parser to use for the
language. Defaults to `tree-sitter-<languageId>`, change if the package name doesn't match the languageId.

- `codeBlocks.parserName`: Filename of the WASM parser built by the `tree-sitter build --wasm` command, without the
`.wasm` extension. Defaults to `tree-sitter-<languageId>`, change if the parser filename doesn't match the languageId.
- `codeBlocks.parserName`: Name to save parser as (defaults to `tree-sitter-<languageID>`), change if the package name doesn't match the languageId (e.g., `tree-sitter-typescript` for `[typescriptreact]` languageId).

- `codeBlocks.subdirectory`: Directory inside the NPM package containing the `tree-sitter` grammar. Defaults to the
root directory of the package, change if the grammar isn't there.
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default defineConfig([
"error",
{
argsIgnorePattern: "^_.*",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],

Expand Down
3 changes: 1 addition & 2 deletions src/FileTree.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as vscode from "vscode";

import { Block, getQueryBlocks } from "./BlockTree";
import Parser, { Query, SyntaxNode, Tree } from "tree-sitter";
import Parser, { type Language, Query, SyntaxNode, Tree } from "tree-sitter";
import { Result, err, ok } from "./result";

import { Language } from "./Installer";
import { Selection } from "./Selection";
import { getLanguageConfig } from "./configuration";
import { getLogger } from "./outputChannel";
Expand Down
28 changes: 14 additions & 14 deletions src/Installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import * as vscode from "vscode";
import { ExecException, ExecOptions, exec } from "child_process";
import { Result, err, ok } from "./result";
import { existsSync, rmSync } from "fs";
import Parser from "tree-sitter";
import type { Language } from "tree-sitter";
import { getLogger } from "./outputChannel";
import { mkdir } from "fs/promises";
import which from "which";

const NPM_INSTALL_URL = "https://nodejs.org/en/download";

export type Language = Parser.Language;

export function getAbsoluteParserDir(parsersDir: string, parserName: string): string {
return path.resolve(path.join(parsersDir, parserName));
}
Expand All @@ -22,11 +20,11 @@ export function getAbsoluteBindingsDir(parsersDir: string, parserName: string):
return path.resolve(path.join(parsersDir, parserName, "bindings", "node", "index.js"));
}

export function loadParser(
export async function loadParser(
parsersDir: string,
parserName: string,
subdirectory?: string
): Result<Language, string> {
): Promise<Result<Language, string>> {
const logger = getLogger();

const bindingsDir = getAbsoluteBindingsDir(parsersDir, parserName);
Expand All @@ -39,12 +37,14 @@ export function loadParser(
try {
logger.log(`Loading parser from ${bindingsDir}`);

// using dynamic import causes issues on windows
// make sure to test well on windows before changing this
// TODO(02/11/24): change to dynamic import
// let { default: language } = (await import(bindingsDir)) as { default: Language };
// eslint-disable-next-line @typescript-eslint/no-require-imports
let language = require(bindingsDir) as Language;
let language: Language;
try {
language = ((await import(bindingsDir)) as { default: Language }).default;
} catch (_) {
// TODO(1/11/25): Always use import and remove this backup
// eslint-disable-next-line @typescript-eslint/no-require-imports
language = require(bindingsDir) as Language;
}

logger.log(`Got language: ${JSON.stringify(Object.keys(language))}`);

Expand Down Expand Up @@ -117,7 +117,7 @@ export async function downloadAndBuildParser(
}

// try to load parser optimistically
const loadResult = loadParser(parsersDir, parserName);
const loadResult = await loadParser(parsersDir, parserName);
if (loadResult.status === "ok") {
return ok(undefined);
}
Expand All @@ -136,7 +136,7 @@ export async function downloadAndBuildParser(
}

// if it fails, try to build it
const buildResult = await runCmd(`${treeSitterCli} generate`, { cwd: parserDir }, (d) =>
const buildResult = await runCmd(`${treeSitterCli} build`, { cwd: parserDir }, (d) =>
onData?.(d.toString())
);
if (buildResult.status === "err") {
Expand Down Expand Up @@ -272,7 +272,7 @@ export async function getLanguage(
}
}

const loadResult = loadParser(parsersDir, parserName, subdirectory);
const loadResult = await loadParser(parsersDir, parserName, subdirectory);
if (loadResult.status === "err") {
const msg = `Failed to load parser for language ${languageId} > ${loadResult.result}`;

Expand Down
2 changes: 1 addition & 1 deletion src/examples/suite/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from "path";
import Mocha from "mocha";
import { glob } from "glob"; // ✅ named import for v10+
import { glob } from "glob";

export async function run(): Promise<void> {
let example = process.env.EXAMPLE;
Expand Down
8 changes: 5 additions & 3 deletions src/test/suite/Installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import * as vscode from "vscode";
import Parser from "tree-sitter";
import { TreeViewer } from "../../TreeViewer";
import { openDocument } from "./testUtils";
import path from "path";

export async function testParser(language: string, content?: string): Promise<void> {
// fail the test if the parser could not be installed
const result = await Installer.getLanguage("test-parsers", language, true);
const testParsersDir = path.resolve(__dirname, "..", "..", "..", "test-parsers");
const result = await Installer.getLanguage(testParsersDir, language, true);
if (result.status === "err") {
throw new Error(`Failed to install language: ${JSON.stringify(result.result)}`);
}
Expand Down Expand Up @@ -49,15 +51,15 @@ suite("Installer integration tests", function () {
["Ruby", "ruby", "def foo\nend\ndef bar\nend"],
// ["SQL", "sql"],
["HTML", "html", "<html></html>"],
// ["CSS", "css", "body { color: red; }"],
["CSS", "css", "body { color: red; }"],
["YAML", "yaml", "key: value"],
["JSON", "json", '{ "key": "value" }'],
["XML", "xml"],
["Markdown", "markdown", "# Title"],
// ["LaTeX", "latex"],
["Bash", "shellscript", "echo 'Hello, World!'"],
["TOML", "toml"],
// ["Swift", "swift"],
["Swift", "swift"],
["Kotlin", "kotlin", "fun main() { }"],
["Zig", "zig", 'const std = @import("std");\n\npub fn main() void { }'],
];
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "ES2020",
"outDir": "out",
"lib": [
"ES2022",
Expand Down
Loading