Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import * as vscode from 'vscode';
import { container } from 'tsyringe';
import { ServiceToken } from '@src/services/tokens';
import { WebviewComponentFactory } from './webview-component-factory';
import { ExecuteCommandMessage } from './webview-messaging';
/**
* Base controller for webviews providing unified lifecycle for both Views (WebviewView)
* and Panels (WebviewPanel), plus typed message handling.
*
* Subclasses automatically self-register with the extension context for disposal.
*/
export abstract class WebviewController<M = any> implements vscode.WebviewViewProvider {
/**
* Optional contributed view id for components that are shown in the sidebar or the panel area.
* @remarks If set, the controller supports opening a panel via the `show` method.
*/
readonly viewType?: string;
/**
* Optional panel identifier for components that are shown in the editor area.
* @remarks If set, the controller supports opening a panel via the `show` method.
*/
readonly panelId?: string;
/**
* Optional panel title (for editor/terminal panels).
*/
readonly panelTitle?: string;
/**
* Optional panel icon (for editor/terminal panels).
*/
readonly panelIcon?: string;
/**
* Relative JS bundle path inside dist/ used by this controller (e.g. "sparql-endpoint-view.js").
*/
protected readonly componentPath: string;
protected readonly context: vscode.ExtensionContext;
protected view?: vscode.WebviewView;
protected panel?: vscode.WebviewPanel;
private _subscriptions: vscode.Disposable[] = [];
constructor(init: {
componentPath: string;
viewType?: string;
panelId?: string;
panelTitle?: string;
panelIcon?: string;
}) {
this.componentPath = init.componentPath;
this.viewType = init.viewType;
this.panelId = init.panelId;
this.panelTitle = init.panelTitle;
this.panelIcon = init.panelIcon;
// Resolve the extension context from DI and self-register
this.context = container.resolve<vscode.ExtensionContext>(ServiceToken.ExtensionContext);
if (this.viewType) {
this._subscriptions.push(vscode.window.registerWebviewViewProvider(this.viewType, this));
}
// Self-register with the extension context for automatic disposal
this.context.subscriptions.push(...this._subscriptions);
}
/**
* Add disposables to be cleaned up when the extension is deactivated.
* @param disposables The disposables to add.
*/
protected subscribe(...disposables: vscode.Disposable[]): void {
this._subscriptions.push(...disposables);
this.context.subscriptions.push(...disposables);
}
/**
* Unified show for panel controllers. Creates or reveals the editor panel.
* @param viewColumn The view column to show the panel in.
* @throws If the controller does not support panels.
*/
async show(viewColumn: vscode.ViewColumn = vscode.ViewColumn.Active) {
if (!this.panelId || !this.panelTitle) {
throw new Error('This controller does not support panels (panelId or panelTitle are not set).');
}
if (!this.panel) {
this.panel = new WebviewComponentFactory(this.context, this.componentPath).createPanel(
this.panelId,
this.panelTitle,
this.panelIcon,
viewColumn
);
this.panel.webview.onDidReceiveMessage((message: M) => this.onDidReceiveMessage(message));
this.panel.onDidDispose(() => (this.panel = undefined));
} else {
this.panel.reveal(viewColumn);
}
}
resolveWebviewView(webviewView: vscode.WebviewView): void | Thenable<void> {
this.view = new WebviewComponentFactory(this.context, this.componentPath).createView(webviewView);
this.view.webview.onDidReceiveMessage((message: M) => this.onDidReceiveMessage(message), this, this._subscriptions);
}
/**
* Post a message to the active webview (view preferred, else panel).
* @param message The message to post.
*/
protected postMessage(message: M) {
if (this.view) {
this.view.webview.postMessage(message);
} else if (this.panel) {
this.panel.webview.postMessage(message);
}
}
/**
* Handle messages received from the webview. Override this in subclasses to handle
* custom messages. Call `super.onDidReceiveMessage(message)` for unhandled messages
* to enable default handling of common message types like `ExecuteCommand`.
* @param message The message received from the webview.
* @returns `true` if the message was handled, `false` otherwise.
*/
protected async onDidReceiveMessage(message: M): Promise<boolean> {
const msg = message as unknown as ExecuteCommandMessage;
if (msg.id === 'ExecuteCommand') {
await vscode.commands.executeCommand(msg.command, ...(msg.args || []));
return true;
}
return false;
}
}
|