diff --git a/packages/affine-vscode/mod.js b/packages/affine-vscode/mod.js index 7310a0f5..0b97778a 100644 --- a/packages/affine-vscode/mod.js +++ b/packages/affine-vscode/mod.js @@ -33,6 +33,8 @@ module.exports = function makeVscodeBindings(vscode, lcModule, hostShim) { const reg = (obj) => hostShim._registerHandle(obj); const get = (h) => hostShim._getHandle(h); const getInstance = () => hostShim._instance; + // Settled host-Thenable values, keyed by Thenable handle (issue #205). + const __thenableResults = new Map(); // ── String marshalling ───────────────────────────────────────────── // AffineScript's WASM 1.0 codegen stores string literals at the offset @@ -333,6 +335,34 @@ module.exports = function makeVscodeBindings(vscode, lcModule, hostShim) { const ctx = get(ctxHandle); return reg(ctx ? ctx.asAbsolutePath(readString(relPtr)) : ""); }, + + // ── Thenable resolution (issue #205) ─────────────────────────── + // The wasm guest cannot await; these let it observe a settled host + // Thenable. thenableThen registers the guest closure (reusing the + // #199 closure-pointer marshalling via wrapHandler) and stores the + // settled value keyed by the Thenable handle; thenableResultJson + // returns it JSON-encoded (same reg(string) return convention as + // every other `-> String` extern). + thenableThen: (tHandle, onSettlePtr) => { + const thenable = get(tHandle); + const cb = wrapHandler(onSettlePtr); + if (!thenable || typeof thenable.then !== "function") { + return reg({ dispose() {} }); + } + Promise.resolve(thenable).then( + (val) => { __thenableResults.set(tHandle, val); try { cb(); } catch (_e) {} }, + (err) => { + __thenableResults.set(tHandle, { __error: String(err) }); + try { cb(); } catch (_e) {} + } + ); + return reg({ dispose() {} }); + }, + thenableResultJson: (tHandle) => { + if (!__thenableResults.has(tHandle)) return reg(""); + try { return reg(JSON.stringify(__thenableResults.get(tHandle))); } + catch (_e) { return reg(""); } + }, }; const VscodeLanguageClient = { diff --git a/stdlib/Vscode.affine b/stdlib/Vscode.affine index df0d5b50..beccf7c9 100644 --- a/stdlib/Vscode.affine +++ b/stdlib/Vscode.affine @@ -305,3 +305,20 @@ pub extern fn extensionAbsolutePath(ctx: ExtensionContext, rel_path: String) -> /// explicit `Unit` param avoids the zero-param-fn return-type collapse). /// Returns the progress Thenable. pub extern fn withProgressNotification(title: String, work: fn(Unit) -> Thenable) -> Thenable / Async; + +// ── Thenable resolution (issue #205) ───────────────────────────────── +// +// The source-to-source backend can `await` a Thenable directly; the +// wasm/Node backend cannot, so these primitives let a wasm guest +// observe a settled host Thenable. `thenableThen` registers a guest +// callback (the #199 function-value ABI — the host re-enters it when +// the Thenable settles); `thenableResultJson` reads the settled payload +// JSON-encoded once the callback has fired. + +/// Run `on_settle` when `t` resolves. `on_settle` is a function value +/// (#199); the host invokes it after settlement. Returns a Disposable. +pub extern fn thenableThen(t: Thenable, on_settle: fn(Unit) -> Int) -> Disposable / Async; + +/// The resolved value of `t`, JSON-encoded. Empty string until `t` has +/// settled (i.e. until the `thenableThen` callback has fired). +pub extern fn thenableResultJson(t: Thenable) -> String;