JS-side adapter for the stdlib/Vscode.affine and
stdlib/VscodeLanguageClient.affine binding modules. Issue #35 Phase 2
deliverable.
npm install @hyperpolymath/affine-vscodeAdd it to your extension’s package.json dependencies. vscode and
vscode-languageclient are peer dependencies (the latter is optional —
omit it with --vscode-extension-no-lc).
Compile with the --vscode-extension flag and the generated .cjs is
directly loadable as the extension’s main — the adapter wiring is
emitted inline, so there is no hand-written entry point to maintain:
affinescript compile src/extension.affine -o out/extension.cjs --vscode-extensionPoint your extension’s package.json main at the unmodified
out/extension.cjs and list @hyperpolymath/affine-vscode in
dependencies. Sub-flags: --vscode-extension-adapter <specifier>
overrides the adapter require() target; --vscode-extension-no-lc
omits the vscode-languageclient/node dependency.
If you cannot use the flag, set the shim’s extraImports hook before the
first activate/deactivate call so the extern fns resolve to live
vscode APIs. Pass the shim module itself as the third argument — the
adapter reads its handle table and _instance lazily:
const vscode = require("vscode");
const lc = require("vscode-languageclient/node");
const makeBindings = require("@hyperpolymath/affine-vscode");
const shim = require("./extension.cjs");
shim.extraImports = () => makeBindings(vscode, lc, shim);
exports.activate = shim.activate;
exports.deactivate = shim.deactivate;The adapter implements every extern fn declared in:
-
stdlib/Vscode.affine— initial set (issue #35 Phase 2): commands.registerCommand, workspace.getConfiguration / createFileSystemWatcher, window.{showError,showWarning,showInformation}Message, window.createTerminal + terminal.show / sendText, ExtensionContext.subscriptions.push, window.activeTextEditor, editorActive{FilePath,LanguageId}, workspaceConfigGet{Bool,String}, consoleLog, execSync, and three string helpers.Expanded 2026-05-11 for the rsr-certifier port (issue #64): workspaceFolderFirstPath / workspaceRootUri, uriFromPath / uriJoinPath / uriPath, fsWriteFile, openTextDocument / showTextDocument, createStatusBarItem + statusBarItem.{setText,setTooltip,setCommand,setBackgroundColorTheme,show,hide,asDisposable}, createDiagnosticCollection + diagnosticCollection.{clear,setForUri,asDisposable}, createWebviewPanel + webviewPanel.{setHtml,asDisposable}, clipboardWriteText, onDidSaveTextDocument, pathBasename / pathJoin / processPlatform, and extensionAbsolutePath.
-
stdlib/VscodeLanguageClient.affine— 3 bindings covering new LanguageClient(…) / start() / stop().
Two API shapes are not bound because they cannot be expressed in the current synchronous extern-call ABI without an async-extern hookup:
-
vscode.window.withProgress(opts, async task)— the second arg is an async Thenable-returning task. -
LanguageClient.sendRequest(method, params)— returns a Thenable.
Extensions that need either should fall back to shelling out to a CLI
via Vscode::createTerminal / Vscode::execSync until an async-extern
ABI lands.
-
All host objects (Disposable, Terminal, ExtensionContext, StatusBarItem, DiagnosticCollection, WebviewPanel, Uri, TextDocument, …) are represented as opaque integer handles on both sides of the FFI. The adapter maintains a JS-side handle table.
-
String args are passed across as i32 pointers into the wasm linear memory; the adapter reads the
[u32 length][utf-8 bytes]layout out ofinstance.exports.memory.buffer. -
Wasm function-pointer args (e.g. command handlers) come in as
__indirect_function_tableindices; the adapter wraps each in a JS thunk that re-enters the wasm module on invocation. -
Diagnostics avoid a Range+Diagnostic struct FFI by accepting a JSON array:
[{startLine,startCol,endLine,endCol,message,severity}, …]. The adapter parses it and constructs the vscode objects.