diff --git a/src/webviews/apps/shared/appBase.ts b/src/webviews/apps/shared/appBase.ts index 737c1a6..ef143b4 100644 --- a/src/webviews/apps/shared/appBase.ts +++ b/src/webviews/apps/shared/appBase.ts @@ -89,31 +89,33 @@ export abstract class App { const id = nextIpcId(); this.log(`${this.appName}.sendCommand(${id}): name=${command.method}`); - return this.postMessage({ id: id, method: command.method, params: params }); + this.postMessage({ id: id, method: command.method, params: params }); } - protected sendCommandWithCompletion< + protected async sendCommandWithCompletion< TCommand extends IpcCommandType, - TCompletion extends IpcNotificationType<{ completionId: string }>, + TCompletion extends IpcNotificationType, >( command: TCommand, params: IpcMessageParams, completion: TCompletion, - callback: (params: IpcMessageParams) => void, - ): void { + ): Promise> { const id = nextIpcId(); this.log(`${this.appName}.sendCommandWithCompletion(${id}): name=${command.method}`); - const disposable = DOM.on(window, 'message', e => { - onIpc(completion, e.data as IpcMessage, params => { - if (params.completionId === id) { - disposable.dispose(); - callback(params); - } + const promise = new Promise>(resolve => { + const disposable = DOM.on(window, 'message', (e: MessageEvent) => { + onIpc(completion, e.data, params => { + if (e.data.completionId === id) { + disposable.dispose(); + resolve(params); + } + }); }); }); - return this.postMessage({ id: id, method: command.method, params: params }); + this.postMessage({ id: id, method: command.method, params: params, completionId: id }); + return promise; } protected setState(state: State) { diff --git a/src/webviews/apps/shared/appWithConfigBase.ts b/src/webviews/apps/shared/appWithConfigBase.ts index be6ad3c..48d123d 100644 --- a/src/webviews/apps/shared/appWithConfigBase.ts +++ b/src/webviews/apps/shared/appWithConfigBase.ts @@ -570,7 +570,7 @@ export abstract class AppWithConfig extends Ap return; } - this.sendCommandWithCompletion( + void this.sendCommandWithCompletion( GenerateConfigurationPreviewCommandType, { key: el.dataset.settingPreview!, @@ -578,10 +578,9 @@ export abstract class AppWithConfig extends Ap format: value, }, DidGenerateConfigurationPreviewNotificationType, - params => { - el.innerText = params.preview ?? ''; - }, - ); + ).then(params => { + el.innerText = params.preview ?? ''; + }); break; } diff --git a/src/webviews/protocol.ts b/src/webviews/protocol.ts index 6bf992c..aad94f4 100644 --- a/src/webviews/protocol.ts +++ b/src/webviews/protocol.ts @@ -4,6 +4,7 @@ export interface IpcMessage { id: string; method: string; params?: unknown; + completionId?: string; } abstract class IpcMessageType { @@ -73,7 +74,6 @@ export const DidChangeConfigurationNotificationType = new IpcNotificationType implements Disposable { const html = content.replace( /#{(head|body|endOfBody|placement|cspSource|cspNonce|root|webroot)}/g, - (_substring, token) => { + (_substring: string, token: string) => { switch (token) { case 'head': return head ?? ''; @@ -249,12 +249,18 @@ export abstract class WebviewBase implements Disposable { return html; } - protected notify>(type: T, params: IpcMessageParams): Thenable { - return this.postMessage({ id: nextIpcId(), method: type.method, params: params }); + protected notify>( + type: T, + params: IpcMessageParams, + completionId?: string, + ): Thenable { + return this.postMessage({ id: nextIpcId(), method: type.method, params: params, completionId: completionId }); } @serialize() - @debug['postMessage']>({ args: { 0: m => `(id=${m.id}, method=${m.method})` } }) + @debug['postMessage']>({ + args: { 0: m => `(id=${m.id}, method=${m.method}${m.completionId ? `, completionId=${m.completionId}` : ''})` }, + }) private postMessage(message: IpcMessage): Promise { if (this._panel == null) return Promise.resolve(false); diff --git a/src/webviews/webviewViewBase.ts b/src/webviews/webviewViewBase.ts index 62bc47e..6c13375 100644 --- a/src/webviews/webviewViewBase.ts +++ b/src/webviews/webviewViewBase.ts @@ -17,7 +17,6 @@ import { serialize } from '../system/decorators/serialize'; import type { TrackedUsageFeatures } from '../usageTracker'; import type { IpcMessage, IpcMessageParams, IpcNotificationType } from './protocol'; import { ExecuteCommandType, onIpc, WebviewReadyCommandType } from './protocol'; -import type { WebviewBase } from './webviewBase'; const maxSmallIntegerV8 = 2 ** 30; // Max number that can be stored in V8's smis (small integers) @@ -243,12 +242,18 @@ export abstract class WebviewViewBase implements return html; } - protected notify>(type: T, params: IpcMessageParams): Thenable { - return this.postMessage({ id: nextIpcId(), method: type.method, params: params }); + protected notify>( + type: T, + params: IpcMessageParams, + completionId?: string, + ): Thenable { + return this.postMessage({ id: nextIpcId(), method: type.method, params: params, completionId: completionId }); } @serialize() - @debug['postMessage']>({ args: { 0: m => `(id=${m.id}, method=${m.method})` } }) + @debug['postMessage']>({ + args: { 0: m => `(id=${m.id}, method=${m.method}${m.completionId ? `, completionId=${m.completionId}` : ''})` }, + }) protected postMessage(message: IpcMessage) { if (this._view == null) return Promise.resolve(false); diff --git a/src/webviews/webviewWithConfigBase.ts b/src/webviews/webviewWithConfigBase.ts index 8a68747..03d7b76 100644 --- a/src/webviews/webviewWithConfigBase.ts +++ b/src/webviews/webviewWithConfigBase.ts @@ -172,10 +172,11 @@ export abstract class WebviewWithConfigBase extends WebviewBase { preview = 'Invalid format'; } - await this.notify(DidGenerateConfigurationPreviewNotificationType, { - completionId: e.id, - preview: preview, - }); + await this.notify( + DidGenerateConfigurationPreviewNotificationType, + { preview: preview }, + e.completionId, + ); } } });