- This repository contains the Visual Studio Code extension for WurstScript.
- WurstScript is an all-in-one Warcraft III modding toolkit and language ecosystem (compiler, stdlib, package manager, and related tools).
- The extension is the client-side glue to the Wurst language server and exposes editor features and user commands.
../casc-ts— CASC archive reader library; consumed via a localnode_modulessymlink. Edit the source there and rebuild (npm run buildin that dir) when fixing extraction issues.../war3-model— WC3 model (MDX/MDL) parser and renderer; used by the model preview feature.
src/extension.ts: minimalactivate()entry point — wires up features and starts the language client.src/paths.ts: all~/.wurstpath constants and GitHub API URLs.src/languageServer.ts: language client lifecycle (startLanguageClient,stopLanguageServerIfRunning, file watcher).src/install/installer.ts: install/update orchestration (checks layout, downloads nightly, runs grill).src/install/downloader.ts: GitHub API helpers, file download with progress, zip extraction.src/install/fsUtils.ts: pure filesystem helpers (retry, copy, migrate legacy layout, cleanup).src/install/pathManager.ts: PATH management for terminals and shell profiles.src/features/commands.ts: command registration and forwarding to language-server execute-command requests.src/features/newProject.ts:wurst.newProjectcommand — interactive project scaffold via grill.src/features/compileTimeDecorator.ts: gutter icon decorator for@compiletimefunctions.src/features/*: focused feature modules (file creation, preview features, custom editor support).package.json: VS Code contributions (commands, activation events, menus, keybindings, configuration).
- Add user-facing command IDs to
package.json:contributes.commandsactivationEventswhen command-driven activation is needed
- Register command handlers in
src/features/commands.tsviavscode.commands.registerCommand. - For language server actions, forward through
ExecuteCommandRequestwith the exact server command name (for examplewurst.fix_all_quickfixes). - Keep VS Code command IDs and language-server command IDs distinct when needed:
- VS Code command: namespaced for UX/discoverability.
- LSP execute-command: must match the server contract exactly.
- Keep changes minimal and localized.
- Reuse existing helper patterns in
commands.tsinstead of introducing new abstractions unless needed. - Prefer clear command names and explicit request objects.
- Avoid introducing unrelated formatting churn in JSON/TS files.
- Keep
extension.tsminimal — it is the entry point only. New features belong insrc/features/or the appropriate module undersrc/install/. - Do not add new top-level files to
src/for logic that belongs in an existing module. Follow the existing split: paths →paths.ts, install logic →src/install/, LS lifecycle →languageServer.ts, editor features →src/features/. - Do not create utility files, helper files, or abstractions for one-off operations. Three similar lines of code is better than a premature abstraction.
Preview features are split across focused modules — pick the slice you need:
-
src/features/preview/imageDecoders.ts — pure binary decoders (BLP, DDS, TGA → RGBA). No VS Code imports, no CASC. To add a new raster format: add a
decodeXxx()function and a branch indecodeRasterPreview(). -
src/features/preview/cascStorage.ts — CASC singleton and WC3 game-file extraction. Touch for: new WC3 install paths, CASC API changes, disk-cache invalidation.
-
src/features/preview/mdxDecode.ts — MDX/MDL binary model parser (existing, unchanged).
-
src/features/blpPreview.ts — VS Code custom editor provider + webview HTML for BLP/DDS/TGA/MDX. Touch for: UI changes, preview toolbar controls, message protocol between host and webview.
-
src/features/imageAssetSupport.ts — shared Node-side image utilities (PNG encode, scale, preview cache).
-
src/features/webviewUtils.ts —
makeNonce()+escapeHtml()only. Shared by all webview builders. -
src/features/webviewShared.ts — shared CSS (VS Code theme token mapping,
.wv-header,.wv-toolbar,.wv-btn,.wv-sep,.wv-scroll, spinner overlay), plusbuildPage(),sep(),spinnerOverlay()helpers. All webview panels use this as their CSS/HTML base. Touch for: cross-viewer style changes, new shared components, VS Code theme token additions. Do NOT put viewer-specific CSS here — pass it viabuildPage({ extraCss })instead.
Rule: Before adding new image/model decode logic, check imageDecoders.ts.
Before adding new CASC extraction logic, check cascStorage.ts.
Before adding new webview CSS that should be consistent across viewers, add it to webviewShared.ts.
Do not duplicate decoders across features.
- src/features/preview/framework.ts supports optional webview message handling via
onMessage; use it for lazy webview data instead of eagerly serializing large parsed structures. - src/features/objModPreview.ts is the Object Editor-style preview for
.w3u/.w3t/.w3a/.w3b/.w3d/.w3h/.w3q. Keep it lazy: send object summaries first, then load field rows for the selected object on demand..w3afiles can contain hundreds of objects and become slow if every base-field row is serialized up front. - Object editor labels and base values come from WC3 game data in CASC. Metadata paths include
Units\UnitMetaData.slk,Units\AbilityMetaData.slk,Units\AbilityBuffMetaData.slk,Doodads\DoodadMetaData.slk, and profile/string files underUnits\*.txt. - Reforged object button art often lives in skin TXT files, not the older func files:
Units\UnitSkin.txt,Units\ItemSkin.txt,Units\AbilitySkin.txt,Units\DestructableSkin.txt,Doodads\DoodadSkins.txt, and sometimesUnits\UpgradeSkin.txt. These files may start with a UTF-8 BOM; strip it before parsing section headers. - WESTRING labels are resolved from localized CASC files such as
war3.w3mod:_locales\enus.w3mod:UI\WorldEditStrings.txtandWorldEditGameStrings.txt. If labels show rawWESTRING_*, check locale path candidates incascStorage.ts. TRIGSTR_###values are map-local string references; resolve them throughwar3map.wtsviasrc/features/preview/triggerStrings.ts, but still show the source reference where useful.- For icons/thumbnails in webviews, reuse
imageAssetSupport.ensurePreview,getCandidateRoots, and CASC texture extraction (ensureGameTextureCached) instead of adding another decoder/cache path.
- src/features/preview/wc3Data.ts — generic CASC game-data loaders/parsers:
readGameData,parseSlk,parseProfile,loadProfilePaths,loadWorldEditStrings,resolveWorldEditString, plus the per-kind profile/skin path lists. Add new game-data loading here, not in a viewer. - src/features/preview/objectCatalog.ts —
getObjectCatalog()→Map<rawcode, { name, iconPath, modelPath }>built from profiles/skins + world strings. Use it to turn raw 4-char object ids into named, icon-decorated references in any viewer (doo, trigger, map data). - Lazy inline icons: host side
imageAssetSupport.requestPreviewIcon(iconPath, key, webview, uri)+ client sideICON_LAZYLOAD_SCRIPT/ICON_INLINE_CSS/PREVIEW_ICON_CSPfromwebviewShared.ts. Markup contract:<span class="object-icon" data-key data-icon></span>. WireonMessage→requestPreviewIcon. Validate any new inline<script>string withvm.Script. - Extension-agnostic asset resolution: WC3 looks up assets by name, ignoring the requested extension. Use
imageAssetSupport.resolveAssetPathWithCasc(assetPath, roots)(built onassetPathVariants) which probes.mdx/.mdlfor models and.blp/.dds/.tgafor textures across local roots (map folder,imports/, workspace, game cache) then CASC. Don't resolve a single fixed extension. - Inline model preview: to embed a 3D model render in a webview (not a separate window), load
dist/webview/mdxViewer.jsviawebview.asWebviewUri(requiresextensionUrion the provider,dist/webviewinlocalResourceRoots, andscript-src 'unsafe-inline' ${webview.cspSource}— do NOT add a nonce, it would disable'unsafe-inline'for inline scripts). Host side:preview/modelPreviewHost.tspostModelToWebview/postTexturesToWebview; the viewer postsrequestTexturesback. The objmod editor's#mpv-boxis the reference implementation. - Objmod asset-browser model thumbnails: visible model cards should enter a pending/spinner state immediately and stay there until the thumbnail is either loaded or decisively marked missing (
?). Generation must drain visible thumbnails in DOM order, one complete thumbnail lifecycle at a time: host resolve -> warm webview renderer -> cache/write or missing decision -> next item. Do not pre-resolve/render later visible models in parallel, and do not add fixed inter-thumbnail idle delays after a thumbnail has finished. Cancel queued work only when a thumbnail scrolls out of view before it starts; when it returns, re-observe/requeue it. The grid thumbnail budget is intentionally strict: models above the host-side size cutoff (WURST_MODEL_THUMB_MAX_MODEL_BYTES, default 160 KB) should become?quickly rather than burning CPU; the full model preview can still be opened separately. UseWURST_MODEL_THUMB_DISABLE_CACHE=1for local validation so tests measure actual generation rather than cached webps. - Local-only thumbnail validation: use
npm run test:e2e:objmod-thumbs:localwithWURST_OBJMOD_E2E=1to launch VS Code against the checked-ine2e/war3map.w3ufixture, open the objmod asset browser, disable thumbnail cache, and assert visible FIFO order plus per-thumbnail timing (default max 200ms). OverrideWURST_OBJMOD_E2E_PROJECTandWURST_OBJMOD_E2E_FILEfor a real map/project. This is intentionally not a CI test because it depends on local WC3 data and VS Code/Electron.
- .w3i is an editable custom editor (
wurst.w3iEditor, inmapDataPreview.ts) backed bycasc-tsparseW3i/serializeW3i, which use a parse-prefix + opaque-tail model: only leading string/scalar fields are editable; players/forces/lists are preserved verbatim infile.tail(and parsed best-effort for display only). Every save passes a round-trip safety gate (serializeValidatedW3i). TRIGSTR-backed strings editwar3map.wts; inline strings edit the w3i bytes. The other map-data formats remain read-only underwurst.mapDataPreview(the old read-onlyrenderW3i/parseW3iin that file are retained but no longer routed to). - When adding a new editable binary format, mirror this: a casc-ts parser+serializer with a byte-exact round-trip test, a
CustomEditorProviderwith dirty tracking, and a serialize→re-parse→compare safety gate before any write.
- Compile TypeScript (
npx tsc -p . --noEmit) after command or API wiring changes. - Ensure command appears in Command Palette via
contributes.commands. - Ensure command can activate extension when run from a cold start (
activationEvents).