Skip to content

Commit 9a81075

Browse files
committed
feat: basic syntax checking
Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
1 parent e32c8a6 commit 9a81075

23 files changed

Lines changed: 5030 additions & 1199 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,5 @@ dist/
7777
*.vsix
7878
*.tmLanguage.json
7979
!/syntaxes/*.tmLanguage.json
80+
types/
81+
out/

.vscode/settings.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
{
22
"editor.formatOnSave": true,
3-
"editor.defaultFormatter": "esbenp.prettier-vscode",
3+
"editor.defaultFormatter": "vscode.typescript-language-features",
44
"editor.codeActionsOnSave": {
55
"source.fixAll.eslint": "explicit"
66
},
77
"[typescript]": {
8-
"editor.defaultFormatter": "esbenp.prettier-vscode",
8+
"editor.defaultFormatter": "vscode.typescript-language-features",
99
"editor.formatOnSave": true
1010
},
1111
"[javascript]": {
12-
"editor.defaultFormatter": "esbenp.prettier-vscode",
12+
"editor.defaultFormatter": "vscode.typescript-language-features",
1313
"editor.formatOnSave": true
1414
},
1515
"[json]": {
16-
"editor.defaultFormatter": "esbenp.prettier-vscode",
16+
"editor.defaultFormatter": "vscode.json-language-features",
1717
"editor.formatOnSave": true
1818
},
1919
"[jsonc]": {
20-
"editor.defaultFormatter": "esbenp.prettier-vscode",
20+
"editor.defaultFormatter": "vscode.json-language-features",
2121
"editor.formatOnSave": true
2222
}
2323
}

.vscode/tasks.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@
22
"version": "2.0.0",
33
"tasks": [
44
{
5-
"label": "watch",
65
"type": "npm",
7-
"script": "watch",
6+
"label": "gen-watch",
7+
"script": "gen-watch",
8+
"problemMatcher": ["$tsc-watch"],
9+
"presentation": {
10+
"group": "build"
11+
}
12+
},
13+
{
14+
"type": "npm",
15+
"label": "build-watch",
16+
"script": "build-watch",
817
"problemMatcher": [],
918
"presentation": {
1019
"group": "build"
1120
}
1221
},
1322
{
1423
"label": "build",
15-
"dependsOn": ["watch"],
24+
"dependsOn": ["build-watch", "gen-watch"],
1625
"group": {
1726
"kind": "build",
1827
"isDefault": true

.vscodeignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
.vscode-test/**
33
.github/
44
.gitignore
5+
node_modules/
56
tests/
6-
commitlint.config.js
7+
commitlint.config.cjs
78
src/**
89
.yarnrc
910
**/tsconfig.json

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,57 @@ This extension provides:
1414
- [Snippets](https://code.visualstudio.com/docs/editor/userdefinedsnippets) for worlds and interfaces.
1515
- Basic markdown highlighting in comments.
1616
- Simple list-based autocomplete.
17+
- **WIT Syntax Checking**: Validate WIT files for common syntax errors and provide diagnostics.
18+
19+
### Syntax Validation and Error Display
20+
21+
The extension provides comprehensive WIT syntax validation with error display in VS Code's PROBLEMS pane:
22+
23+
#### Automatic Validation
24+
- **On File Save**: Automatically validates WIT files when saved
25+
- **On File Open**: Validates WIT files when opened in the editor
26+
- **Real-time Feedback**: Errors appear immediately in the PROBLEMS pane
27+
28+
#### Manual Commands
29+
- **WIT: Check Syntax** (`Ctrl+Shift+P` → "WIT: Check Syntax")
30+
- Validates the currently active WIT file
31+
- Shows detailed error information in notifications
32+
- Displays errors in the PROBLEMS pane
33+
34+
- **WIT: Check Syntax in Workspace** (`Ctrl+Shift+P` → "WIT: Check Syntax in Workspace")
35+
- Validates all WIT files in the workspace
36+
- Shows progress notification during validation
37+
- Provides summary of results
38+
- Creates detailed report in output channel
39+
40+
#### Error Information
41+
When validation fails, the extension displays:
42+
- **Error location**: Precise line and column numbers
43+
- **Error message**: Detailed description of the syntax error
44+
- **Context**: Additional information about the error
45+
- **Related information**: Links to relevant documentation or context
46+
47+
#### PROBLEMS Pane Integration
48+
- Errors appear automatically in VS Code's PROBLEMS pane
49+
- Click on any error to jump directly to the problematic line
50+
- Errors are cleared automatically when files are fixed or closed
51+
- Supports multiple files with errors simultaneously
52+
53+
### Code Completion
54+
55+
The extension offers intelligent code completion for WIT files:
56+
57+
- **Context-aware suggestions**: Provides completion items based on the current context
58+
- **Keyword snippets**: Includes common WIT keywords and constructs
59+
- **Custom snippets**: User-defined snippets for faster coding
60+
61+
### Command Palette Integration
62+
63+
Easily access extension features through the Command Palette:
64+
65+
- **WIT: Check Syntax**: Validate the current file's syntax
66+
- **WIT: Check Syntax in Workspace**: Validate all WIT files in the workspace
67+
- **WIT: Show Output Channel**: Display the extension's output channel
1768

1869
## Installation
1970

TESTING.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Testing with Vitest
2+
3+
This project now uses [Vitest](https://vitest.dev/) for unit testing, providing a fast and modern testing experience with excellent TypeScript support.
4+
5+
## Available Test Scripts
6+
7+
- `npm run test:unit` - Run all unit tests once
8+
- `npm run test:unit:watch` - Run tests in watch mode (reruns when files change)
9+
- `npm run test:unit:ui` - Open the Vitest UI for interactive testing
10+
- `npm test` - Run the full test suite (lint, format check, build, package, grammar tests, and unit tests)
11+
12+
## Test Structure
13+
14+
### Unit Tests
15+
- Located in `tests/` directory
16+
- Use `.test.ts` or `.spec.ts` suffix
17+
- Written using Vitest's `describe`, `it`, and `expect` APIs
18+
19+
### Current Tests
20+
- `tests/errorParser.test.ts` - Tests for WIT error parsing functionality
21+
22+
## Writing Tests
23+
24+
```typescript
25+
import { describe, it, expect } from 'vitest';
26+
import { yourFunction } from '../src/yourModule';
27+
28+
describe('Your Module', () => {
29+
it('should do something', () => {
30+
const result = yourFunction('input');
31+
expect(result).toBe('expected output');
32+
});
33+
});
34+
```
35+
36+
## Error Parser Tests
37+
38+
The `errorParser.test.ts` file contains comprehensive tests for the WIT error parsing regex, including:
39+
40+
- Extraction from real WIT error stacks
41+
- Handling invalid/malformed stacks
42+
- Edge cases with large line/column numbers
43+
- Different file path formats
44+
- Stacks with and without "Caused by" sections
45+
46+
## Configuration
47+
48+
The Vitest configuration is in `vitest.config.ts` and includes:
49+
- TypeScript support out of the box
50+
- Node.js environment
51+
- Path aliases for cleaner imports
52+
- Automatic file discovery for tests
53+
54+
## VS Code Integration
55+
56+
Vitest works great with VS Code. You can:
57+
1. Install the [Vitest extension](https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer)
58+
2. Run tests directly from the editor
59+
3. See test results inline
60+
4. Debug tests with breakpoints

esbuild.mjs

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,142 @@
22
// See: https://github.com/microsoft/vscode-extension-samples/blob/main/esbuild-sample/esbuild.js
33

44
import * as esbuild from "esbuild";
5+
import * as fs from "fs";
6+
import * as path from "path";
57

68
const production = process.argv.includes("--production");
79
const watch = process.argv.includes("--watch");
810

11+
/**
12+
* Copy WASM and worker resources from node_modules to dist
13+
*/
14+
function copyWasmResources() {
15+
const distDir = path.join(process.cwd(), "dist");
16+
17+
// Ensure dist directory exists
18+
fs.mkdirSync(distDir, { recursive: true });
19+
20+
// Copy WASM files from @bytecodealliance/jco/lib
21+
const jcoLibDir = path.join(process.cwd(), "node_modules", "@bytecodealliance", "jco", "lib");
22+
const jcoLibFiles = ["wasi_snapshot_preview1.command.wasm", "wasi_snapshot_preview1.reactor.wasm"];
23+
24+
jcoLibFiles.forEach((file) => {
25+
const srcPath = path.join(jcoLibDir, file);
26+
const destPath = path.join(distDir, file);
27+
28+
if (fs.existsSync(srcPath)) {
29+
fs.copyFileSync(srcPath, destPath);
30+
console.log(`Copied ${file} to dist/`);
31+
} else {
32+
console.warn(`Warning: ${srcPath} not found`);
33+
}
34+
});
35+
36+
// Copy all WASM files from jco/obj
37+
const jcoObjDir = path.join(process.cwd(), "node_modules", "@bytecodealliance", "jco", "obj");
38+
if (fs.existsSync(jcoObjDir)) {
39+
const objFiles = fs.readdirSync(jcoObjDir).filter((file) => file.endsWith(".wasm"));
40+
objFiles.forEach((file) => {
41+
const srcPath = path.join(jcoObjDir, file);
42+
const destPath = path.join(distDir, file);
43+
fs.copyFileSync(srcPath, destPath);
44+
console.log(`Copied ${file} to dist/`);
45+
});
46+
}
47+
48+
// Copy WASM files from @bytecodealliance/componentize-js
49+
const componentizeJsLibDir = path.join(
50+
process.cwd(),
51+
"node_modules",
52+
"@bytecodealliance",
53+
"componentize-js",
54+
"lib"
55+
);
56+
57+
const componentizeJsLibFiles = [
58+
"spidermonkey-embedding-splicer.core.wasm",
59+
"spidermonkey-embedding-splicer.core2.wasm",
60+
"starlingmonkey_embedding.wasm",
61+
"starlingmonkey_embedding.debug.wasm",
62+
"starlingmonkey_embedding_weval.wasm",
63+
];
64+
65+
componentizeJsLibFiles.forEach((file) => {
66+
const srcPath = path.join(componentizeJsLibDir, file);
67+
const destPath = path.join(distDir, file);
68+
69+
if (fs.existsSync(srcPath)) {
70+
fs.copyFileSync(srcPath, destPath);
71+
console.log(`Copied ${file} to dist/`);
72+
} else {
73+
console.warn(`Warning: ${srcPath} not found`);
74+
}
75+
});
76+
77+
// Also check nested componentize-js in jco
78+
const jcoNestedComponentizeJsLibDir = path.join(
79+
process.cwd(),
80+
"node_modules",
81+
"@bytecodealliance",
82+
"jco",
83+
"node_modules",
84+
"@bytecodealliance",
85+
"componentize-js",
86+
"lib"
87+
);
88+
89+
if (fs.existsSync(jcoNestedComponentizeJsLibDir)) {
90+
componentizeJsLibFiles.forEach((file) => {
91+
const srcPath = path.join(jcoNestedComponentizeJsLibDir, file);
92+
const destPath = path.join(distDir, file);
93+
94+
if (fs.existsSync(srcPath) && !fs.existsSync(destPath)) {
95+
fs.copyFileSync(srcPath, destPath);
96+
console.log(`Copied ${file} from nested jco/componentize-js to dist/`);
97+
}
98+
});
99+
}
100+
101+
// Copy files from @bytecodealliance/preview2-shim
102+
const preview2ShimDir = path.join(process.cwd(), "node_modules", "@bytecodealliance", "preview2-shim");
103+
104+
if (fs.existsSync(preview2ShimDir)) {
105+
// Copy the main module files
106+
const shimFiles = ["lib", "src"];
107+
shimFiles.forEach((item) => {
108+
const srcPath = path.join(preview2ShimDir, item);
109+
const destPath = path.join(distDir, "preview2-shim", item);
110+
111+
if (fs.existsSync(srcPath)) {
112+
// Create destination directory
113+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
114+
115+
if (fs.statSync(srcPath).isDirectory()) {
116+
// Copy directory recursively
117+
fs.cpSync(srcPath, destPath, { recursive: true });
118+
console.log(`Copied ${item}/ from preview2-shim to dist/preview2-shim/`);
119+
} else {
120+
// Copy file
121+
fs.copyFileSync(srcPath, destPath);
122+
console.log(`Copied ${item} from preview2-shim to dist/preview2-shim/`);
123+
}
124+
}
125+
});
126+
127+
// Copy package.json for module resolution
128+
const packageJsonSrc = path.join(preview2ShimDir, "package.json");
129+
const packageJsonDest = path.join(distDir, "preview2-shim", "package.json");
130+
if (fs.existsSync(packageJsonSrc)) {
131+
fs.mkdirSync(path.dirname(packageJsonDest), { recursive: true });
132+
fs.copyFileSync(packageJsonSrc, packageJsonDest);
133+
console.log("Copied package.json from preview2-shim to dist/preview2-shim/");
134+
}
135+
}
136+
137+
// Note: Worker files are bundled directly into dist/ to ensure proper module resolution
138+
// This prevents import errors when the extension tries to load worker threads at runtime
139+
}
140+
9141
/**
10142
* @type {import('esbuild').Plugin}
11143
*/
@@ -28,18 +160,56 @@ const esbuildProblemMatcherPlugin = {
28160
};
29161

30162
async function main() {
163+
// Copy WASM resources first
164+
console.log("Copying WASM resources...");
165+
copyWasmResources();
166+
167+
// Find all worker files in preview2-shim
168+
const preview2ShimWorkerDir = path.join(
169+
process.cwd(),
170+
"node_modules",
171+
"@bytecodealliance",
172+
"preview2-shim",
173+
"lib",
174+
"io"
175+
);
176+
177+
const workerEntryPoints = [];
178+
if (fs.existsSync(preview2ShimWorkerDir)) {
179+
const workerFiles = fs
180+
.readdirSync(preview2ShimWorkerDir)
181+
.filter((file) => file.startsWith("worker-") && file.endsWith(".js"))
182+
.map((file) => path.join(preview2ShimWorkerDir, file));
183+
workerEntryPoints.push(...workerFiles);
184+
console.log(
185+
`Found ${workerFiles.length} worker files to bundle:`,
186+
workerFiles.map((f) => path.basename(f))
187+
);
188+
}
189+
31190
const ctx = await esbuild.context({
32-
entryPoints: ["src/extension.ts"],
191+
entryPoints: ["src/extension.ts", ...workerEntryPoints],
33192
bundle: true,
34-
format: "cjs",
193+
format: "esm",
35194
minify: production,
36195
sourcemap: !production,
37196
sourcesContent: false,
38197
platform: "node",
39-
outfile: "dist/extension.js",
198+
target: "node18",
199+
outdir: "dist",
200+
entryNames: "[name]",
40201
external: ["vscode"],
41202
logLevel: "silent",
42203
plugins: [esbuildProblemMatcherPlugin],
204+
inject: ["src/node-polyfills.js"],
205+
define: {
206+
// Define globals to prevent bundling issues
207+
"process.env.NODE_ENV": JSON.stringify(production ? "production" : "development"),
208+
},
209+
// Add banner to ensure polyfills are loaded first
210+
banner: {
211+
js: "// VS Code Extension - ESM Node.js Environment\n",
212+
},
43213
});
44214
if (watch) {
45215
await ctx.watch();

0 commit comments

Comments
 (0)