/******************************************************************************** * Copyright (C) 2024-2025 Arm Limited and others. * * This program and the accompanying materials are made available under the * terms of the MIT License as outlined in the LICENSE File ********************************************************************************/ import * as vscode from 'vscode'; import { Messenger } from 'vscode-messenger'; import { NotificationType, WebviewIdMessageParticipant } from 'vscode-messenger-common'; import { CDTTreeExecuteCommand, CDTTreeMessengerType, CDTTreeNotification } from '../common'; import type { CDTTreeDataProvider } from './tree-data-provider'; export abstract class CDTTreeWebviewViewProvider implements vscode.WebviewViewProvider { protected onDidToggleNodeEvent = new vscode.EventEmitter>(); public readonly onDidToggleNode = this.onDidToggleNodeEvent.event; protected onDidExecuteCommandEvent = new vscode.EventEmitter>(); public readonly onDidExecuteCommand = this.onDidExecuteCommandEvent.event; protected onDidClickNodeEvent = new vscode.EventEmitter>(); public readonly onDidClickNode = this.onDidClickNodeEvent.event; protected onDidSearchChangeEvent = new vscode.EventEmitter>(); public readonly onDidSearchChange = this.onDidSearchChangeEvent.event; protected get extensionUri(): vscode.Uri { return this.context.extensionUri; } protected _view?: vscode.WebviewView; protected participant: WebviewIdMessageParticipant | undefined; public constructor( protected readonly dataProvider: CDTTreeDataProvider, protected readonly context: vscode.ExtensionContext, protected readonly messenger = new Messenger({ ignoreHiddenViews: false, debugLog: true }) ) {} public async resolveWebviewView( webviewView: vscode.WebviewView, _context: vscode.WebviewViewResolveContext, _token: vscode.CancellationToken ): Promise { this._view = webviewView; const baseExtensionUriString = this.extensionUri.toString(); const distPathUri = vscode.Uri.parse(`${baseExtensionUriString}/dist/views`, true /* strict */); const mediaPathUri = vscode.Uri.parse(`${baseExtensionUriString}/media`, true /* strict */); const nodeModulesPathUri = vscode.Uri.parse(`${baseExtensionUriString}/node_modules`, true /* strict */); // Allow scripts in the webview webviewView.webview.options = { enableScripts: true, // enable scripts in the webview localResourceRoots: [distPathUri, mediaPathUri, nodeModulesPathUri] // restrict extension's local file access }; // Set the HTML content that will fill the webview view webviewView.webview.html = await this.getWebviewContent(webviewView.webview, this.extensionUri); // Sets up an event listener to listen for messages passed from the webview view context // and executes code based on the message that is received this.setWebviewMessageListener(webviewView); this.setWebviewData(webviewView); } protected setWebviewData(_webviewView: vscode.WebviewView): void { // Nothing to do } protected async getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): Promise { const codiconsUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css') ); const mainUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'dist', 'views', 'treeWebView.js')); return `
`; } protected setWebviewMessageListener(webview: vscode.WebviewView): void { const participant = (this.participant = this.messenger.registerWebviewView(webview)); if (this.participant === undefined) { return; } const disposables = [ this.dataProvider.onDidTerminate(async event => { if (event.remaining > 0) { this.refreshFull(); } }), this.dataProvider.onDidChangeTreeData(async event => { if (event.data) { if (Array.isArray(event.data)) { await this.refreshPartial(event.data); } else { await this.refreshPartial([event.data]); } } else { this.refreshFull(); } }), this.messenger.onNotification(CDTTreeMessengerType.ready, () => this.onReady(), { sender: participant }), this.messenger.onNotification(CDTTreeMessengerType.executeCommand, event => this.onDidExecuteCommandEvent.fire(event), { sender: participant }), this.messenger.onNotification(CDTTreeMessengerType.toggleNode, event => this.onDidToggleNodeEvent.fire(event), { sender: participant }), this.messenger.onNotification(CDTTreeMessengerType.clickNode, event => this.onDidClickNodeEvent.fire(event), { sender: participant }), this.messenger.onNotification(CDTTreeMessengerType.searchChanged, event => this.onDidSearchChangeEvent.fire(event), { sender: participant }) ]; webview.onDidDispose(() => disposables.forEach(disposible => disposible.dispose())); } protected async onReady(): Promise { await this.refreshFull(); } protected async refreshFull(): Promise { if (!this.participant) { return; } const columnFields = this.dataProvider.getColumnDefinitions(); const items = await this.dataProvider.getSerializedRoots(); this.sendNotification(CDTTreeMessengerType.updateState, { columnFields, items }); } protected async refreshPartial(nodes: TNode[]): Promise { if (!this.participant) { return; } const items = await Promise.all(nodes.map(async node => this.dataProvider.getSerializedData(node))); this.sendNotification(CDTTreeMessengerType.updatePartial, { items }); } public sendNotification

(type: NotificationType

, params?: P): void { if (this.participant) { this.messenger.sendNotification(type, this.participant, params); } } }