Diagnostics, hover, go-to-definition, references, rename, scope-aware
completions, signature help, outline, folding, quick fixes, canonical
formatting, semantic highlighting — plus a CPU-interpreter kernel
debugger, an optimization advisor, an ML-kernel analyzer, and WGSL→MSL
translation. Every semantic decision flows from a wasm-compiled
libwgsl frontend — the same spec-conformant pipeline that ships as a
C library.
v2.0 status
| Spec coverage | W3C WGSL Recommendation §3–§18 — full |
| WGSL spec pin | CRD-2026-05-07 |
| Unicode | 17.0.0 |
| Engine | libwgsl wasm via the official bindings/wgsl.js surface (ABI table from wgsl_abi_layout) |
| Re-check model | incremental wgsl_session_* — byte-identical edits are cache hits, body-only edits skip clean function bodies |
| Positions | exact: UTF-8 byte ↔ UTF-16 mapping on every wasm boundary crossing |
Design — wasm first, TS minimum
Conventional WGSL extensions ship a TextMate grammar (regex-driven) for instant offline coloring, then layer LSP semantic tokens on top. This extension deliberately omits the regex grammar — every coloured byte, every completion, every quick fix comes from the wasm frontend.
- one source of truth for what every identifier means;
- the engine that produces diagnostics also produces highlights, completions, and quick fixes, so they can never disagree;
- what you see in the editor is exactly what
wgsl_checkreports to any C / wasm consumer downstream.
language-configuration.json is the only declarative metadata
(bracket pairs, comment markers) — purely editing UX. No grammar.
Architecture
(wasm bundle) (official JS binding)
wgsl_compiler.{js,wasm} + wasm/wgsl.js (ABI-table offsets)
│
▼ WGSL facade (src/client.ts)
│ core: check/diag/hover/def/tokens/format/interp/…
│ extras: sessions, symbols, refs, rename, folding,
│ completions, signature help, code actions,
│ wgslconfig.toml project API
▼
DocumentCompiler ── for each .wgsl document:
│ 1. resolve preamble (wgslconfig.toml or _shared.wgsl)
│ 2. split on `// --- KERNEL: <name> ---`
│ 3. build N synthetic sources:
│ preamble ++ file_preamble ++ section_body
│ 4. one wgsl_session per section →
│ incremental re-check on every edit
│ 5. cache by (uri, version)
▼
{ split, results[] } → providers (all coordinates remapped
synth ↔ original through TextIndex,
UTF-8 bytes ↔ UTF-16 code units)
Kernel-split + preamble injection
Multi-kernel .wgsl files (the WebGPU ML pattern: one resource set
per // --- KERNEL: <name> --- section, helper preamble at the top,
shared utilities in a sibling file) are recognised automatically.
Each section compiles as its own module — exactly how the runtime
engine dispatches them — so cross-kernel n redeclarations and
preamble helpers resolve without false positives.
Preamble resolution
-
wgslconfig.toml(recommended) — discovered by walking up from the document's directory; matched by the C-sidewgsl_project_match.[preamble] files = [ "_enables.wgsl", # enable subgroups; enable f16; "_shared.wgsl", # helpers used by every kernel ] auto_inject_into = ["*.wgsl"] -
Legacy
_shared.wgsl— a sibling_shared.wgsl, if present, is prepended when no config matches.
Preamble diagnostics surface in the preamble file itself, never on
dependents. Saving a preamble re-checks every open .wgsl document.
Capabilities matrix
| Feature | Source |
|---|---|
| Error/warning diagnostics (+ rule codes) | wgsl_session_check_n + wgsl_diagnostic* |
| Hover: type, signature, builtin docs | wgsl_hover_at_into |
| Go-to-definition (incl. into preamble files) | wgsl_definition_at_into |
| Find references (one round-trip) | wgsl_references_at |
| Rename with validation | wgsl_prepare_rename + wgsl_references_at |
| Scope-aware completions | wgsl_completions_at |
| Signature help (active parameter) | wgsl_signature_help_at_into |
| Outline / breadcrumbs (nested symbols) | wgsl_document_symbols |
| Folding ranges | wgsl_folding_ranges |
| Quick fixes (enable-extension, @location, @align, safe-optimize) | wgsl_code_actions |
| Format document / selection | wgsl_format / wgsl_format_range_n |
| Semantic-token colouring (22 resolver kinds) | wgsl_semantic_tokens |
| Debug kernel on the CPU (code lens / command) | wgsl_interp + wgsl_debug_oracle |
| Module info: entry points, bind group layouts, struct memory layout | wgsl_module_json |
| Optimization report + safe apply | wgsl_optimize_json_src / wgsl_optimize_apply |
| ML kernel analysis (patterns, workgroup sweep, advice) | wgsl_ml_analyze_json_src |
| WGSL → Metal translation | wgsl_to_msl |
wgslconfig.toml projects |
wgsl_project_open_from_string + wgsl_project_match |
| Incremental re-check | wgsl_session_* (interface-hash gated) |
Commands
| Command | What it does |
|---|---|
| WGSL: Debug Kernel in CPU Interpreter | Runs the selected @compute entry through libwgsl's SIMT interpreter (whole workgroup, barriers, shared memory) — verdict, data races, NaN/Inf anomalies, per-line trace, cost model, divergence, buffer contents. Also available as a ▶ Debug kernel code lens. |
| WGSL: Show Module Info | Entry points, bind group layouts, resources, struct sizes/offsets/alignment as JSON. |
| WGSL: Show Optimization Report | Dead code, unused decls, constant branches, CSE, loop findings. |
| WGSL: Apply Optimizations | Applies the safe subset as one undoable edit (single-kernel documents without injected preambles — whole-unit rewrites are only well-defined there). |
| WGSL: Analyze ML Compute Patterns | Detects softmax/attention/matmul/…, suggests workgroup-size sweeps, prints advice. |
| WGSL: Translate to Metal Shading Language | Best-effort MSL preview of the current kernel. |
Settings
| Setting | Default | |
|---|---|---|
wgsl.interp.defaultBufferLength |
16 |
Element count assumed for runtime-sized storage buffers in the interpreter. |
wgsl.codeLens.enabled |
true |
▶ Debug kernel lenses above compute entry points. |
Build
# once: build the wasm bundle in the sibling libwgsl checkout
make -C ../libwgsl wasm
npm run compile # copies wasm + official binding, then tsc
npm run package # vsce package
Limitations
- Cold-start flash: no colour for ~50 ms while the wasm bundle initialises.
- Formatting refuses lossy runs: the formatter is parse→print, so on syntactically-broken sources it would silently drop the broken decls. The extension token-compares input and output and applies nothing unless the format is lossless — format-on-save can never eat half-typed code.
- Renaming a preamble decl from a dependent file is refused to avoid silently missing other dependents — rename in the preamble file itself.
- MSL output is best-effort: the structured MSL diagnostics API is not yet exported to wasm, so unsupported constructs yield an empty translation rather than a detailed reason.
- Interpreter caps: 2M scheduler steps, 256 lanes, 4096 values per buffer in reports — traces beyond that are truncated and flagged.
License
MIT.