diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 9704da5c..91336e04 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,2 +1,2 @@ # Code formatted w Prettier -d12683728c74866ea0bbea8b7847e91b122e220d \ No newline at end of file +d12683728c74866ea0bbea8b7847e91b122e220d diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d7b75f8..c41a131d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,7 +82,10 @@ file. Adding the SHA-1 of a commit to this file will make `git-blame` ignore tha * For GitHub, this file is automatically detected and will ignore all the commits that are included in the file. * With Git CLI, run `git blame --ignore-revs-file=.git-blame-ignore-revs ` to ignore the commits. +<<<<<<< HEAD * `git config --global blame.ignoreRevsFile .git-blame-ignore-revs` will automatically detect these files for every repository. +======= +>>>>>>> d126837 (Repository formatted with prettier) ## Contact diff --git a/vscode-trace-common/src/client/tsp-client-provider-impl.ts b/vscode-trace-common/src/client/tsp-client-provider-impl.ts index 8f4bd53b..30d8ef3b 100644 --- a/vscode-trace-common/src/client/tsp-client-provider-impl.ts +++ b/vscode-trace-common/src/client/tsp-client-provider-impl.ts @@ -7,7 +7,6 @@ import { VsCodeMessageManager } from '../messages/vscode-message-manager'; import { TraceServerUrlProvider } from '../server/trace-server-url-provider'; export class TspClientProvider implements ITspClientProvider { - private _tspClient: TspClient; private _traceManager: TraceManager; private _experimentManager: ExperimentManager; @@ -16,16 +15,19 @@ export class TspClientProvider implements ITspClientProvider { private _urlProvider: TraceServerUrlProvider; private _listeners: ((tspClient: TspClient) => void)[]; - constructor(traceServerUrl: string, signalHandler: VsCodeMessageManager | undefined, _urlProvider: TraceServerUrlProvider + constructor( + traceServerUrl: string, + signalHandler: VsCodeMessageManager | undefined, + _urlProvider: TraceServerUrlProvider ) { this._tspClient = new TspClient(traceServerUrl); this._traceManager = new TraceManager(this._tspClient); this._experimentManager = new ExperimentManager(this._tspClient, this._traceManager); this._signalHandler = signalHandler; - this._statusListener = ((status: boolean) => { + this._statusListener = (status: boolean) => { this._signalHandler?.notifyConnection(status); - }); + }; RestClient.addConnectionStatusListener(this._statusListener); this._tspClient.checkHealth(); diff --git a/vscode-trace-common/src/messages/vscode-message-manager.ts b/vscode-trace-common/src/messages/vscode-message-manager.ts index 40f901e5..4c897813 100644 --- a/vscode-trace-common/src/messages/vscode-message-manager.ts +++ b/vscode-trace-common/src/messages/vscode-message-manager.ts @@ -6,7 +6,7 @@ import JSONBigConfig from 'json-bigint'; import { TimeRangeUpdatePayload } from 'traceviewer-base/lib/signals/time-range-data-signal-payloads'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -66,7 +66,7 @@ export const VSCODE_MESSAGES = { SELECTION_RANGE_UPDATED: 'selectionRangeUpdated', REQUEST_SELECTION_RANGE_CHANGE: 'requestSelectionRangeChange', RESTORE_VIEW: 'restoreView', - RESTORE_COMPLETE: 'restoreComplete', + RESTORE_COMPLETE: 'restoreComplete' }; export class VsCodeMessageManager extends Messages.MessageManager { @@ -74,23 +74,28 @@ export class VsCodeMessageManager extends Messages.MessageManager { super(); } - addStatusMessage(messageKey: string, {text, - category = Messages.MessageCategory.SERVER_MESSAGE, - severity = Messages.MessageSeverity.INFO }: Messages.StatusMessage): void { - vscode.postMessage({command: VSCODE_MESSAGES.NEW_STATUS, data: {messageKey, text, category, severity }}); + addStatusMessage( + messageKey: string, + { + text, + category = Messages.MessageCategory.SERVER_MESSAGE, + severity = Messages.MessageSeverity.INFO + }: Messages.StatusMessage + ): void { + vscode.postMessage({ command: VSCODE_MESSAGES.NEW_STATUS, data: { messageKey, text, category, severity } }); } removeStatusMessage(messageKey: string): void { - vscode.postMessage({command: VSCODE_MESSAGES.REMOVE_STATUS, data: { messageKey }}); + vscode.postMessage({ command: VSCODE_MESSAGES.REMOVE_STATUS, data: { messageKey } }); } notifyReady(): void { - vscode.postMessage({command: VSCODE_MESSAGES.WEBVIEW_READY}); + vscode.postMessage({ command: VSCODE_MESSAGES.WEBVIEW_READY }); } notifyConnection(serverStatus: boolean): void { const status: string = JSON.stringify(serverStatus); - vscode.postMessage({command: VSCODE_MESSAGES.CONNECTION_STATUS, data: { status }}); + vscode.postMessage({ command: VSCODE_MESSAGES.CONNECTION_STATUS, data: { status } }); } /************************************************************************** @@ -98,26 +103,26 @@ export class VsCodeMessageManager extends Messages.MessageManager { *************************************************************************/ openTrace(): void { - vscode.postMessage({command: VSCODE_MESSAGES.OPEN_TRACE}); + vscode.postMessage({ command: VSCODE_MESSAGES.OPEN_TRACE }); } updateOpenedTraces(numberOfOpenedTraces: number): void { - vscode.postMessage({command: VSCODE_MESSAGES.OPENED_TRACES_UPDATED, numberOfOpenedTraces}); + vscode.postMessage({ command: VSCODE_MESSAGES.OPENED_TRACES_UPDATED, numberOfOpenedTraces }); } reOpenTrace(experiment: Experiment): void { const wrapper: string = JSONBig.stringify(experiment); - vscode.postMessage({command: VSCODE_MESSAGES.RE_OPEN_TRACE, data: {wrapper}}); + vscode.postMessage({ command: VSCODE_MESSAGES.RE_OPEN_TRACE, data: { wrapper } }); } closeTrace(experiment: Experiment): void { const wrapper: string = JSONBig.stringify(experiment); - vscode.postMessage({command: VSCODE_MESSAGES.CLOSE_TRACE, data: {wrapper}}); + vscode.postMessage({ command: VSCODE_MESSAGES.CLOSE_TRACE, data: { wrapper } }); } deleteTrace(experiment: Experiment): void { const wrapper: string = JSONBig.stringify(experiment); - vscode.postMessage({command: VSCODE_MESSAGES.DELETE_TRACE, data: {wrapper}}); + vscode.postMessage({ command: VSCODE_MESSAGES.DELETE_TRACE, data: { wrapper } }); } experimentSelected(experiment?: Experiment | undefined): void { @@ -139,11 +144,14 @@ export class VsCodeMessageManager extends Messages.MessageManager { outputAdded(payload: OutputAddedSignalPayload): void { const expWrapper = JSONBig.stringify(payload.getExperiment()); const descWrapper = JSONBig.stringify(payload.getOutputDescriptor()); - vscode.postMessage({command: VSCODE_MESSAGES.OUTPUT_ADDED, data: {data: expWrapper, descriptor: descWrapper }}); + vscode.postMessage({ + command: VSCODE_MESSAGES.OUTPUT_ADDED, + data: { data: expWrapper, descriptor: descWrapper } + }); } propertiesUpdated(properties: { [key: string]: string }): void { - vscode.postMessage({command: VSCODE_MESSAGES.UPDATE_PROPERTIES, data: {properties}}); + vscode.postMessage({ command: VSCODE_MESSAGES.UPDATE_PROPERTIES, data: { properties } }); } viewRangeUpdated(payload: TimeRangeUpdatePayload): void { @@ -161,27 +169,27 @@ export class VsCodeMessageManager extends Messages.MessageManager { vscode.postMessage({ command: VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE, data }); } - saveAsCSV(payload: {traceId: string, data: string}): void { - vscode.postMessage({command: VSCODE_MESSAGES.SAVE_AS_CSV, payload}); + saveAsCSV(payload: { traceId: string; data: string }): void { + vscode.postMessage({ command: VSCODE_MESSAGES.SAVE_AS_CSV, payload }); } - fetchMarkerCategories(payload: Map): void { + fetchMarkerCategories(payload: Map): void { const wrapper: string = JSON.stringify([...payload]); - vscode.postMessage({command: VSCODE_MESSAGES.SHOW_MARKER_CATEGORIES, data: {wrapper}}); + vscode.postMessage({ command: VSCODE_MESSAGES.SHOW_MARKER_CATEGORIES, data: { wrapper } }); } - fetchMarkerSets(payload: Map): void { + fetchMarkerSets(payload: Map): void { const wrapper: string = JSON.stringify([...payload]); - vscode.postMessage({command: VSCODE_MESSAGES.SEND_MARKER_SETS, data: {wrapper}}); + vscode.postMessage({ command: VSCODE_MESSAGES.SEND_MARKER_SETS, data: { wrapper } }); } setMarkerSetsContext(context: boolean): void { const status: string = JSON.stringify(context); - vscode.postMessage({command: VSCODE_MESSAGES.MARKER_SETS_CONTEXT, data: { status }}); + vscode.postMessage({ command: VSCODE_MESSAGES.MARKER_SETS_CONTEXT, data: { status } }); } setMarkerCategoriesContext(context: boolean): void { const status: string = JSON.stringify(context); - vscode.postMessage({command: VSCODE_MESSAGES.MARKER_CATEGORIES_CONTEXT, data: { status }}); + vscode.postMessage({ command: VSCODE_MESSAGES.MARKER_CATEGORIES_CONTEXT, data: { status } }); } } diff --git a/vscode-trace-common/src/server/trace-server-url-provider.ts b/vscode-trace-common/src/server/trace-server-url-provider.ts index f30030e1..36999a4e 100644 --- a/vscode-trace-common/src/server/trace-server-url-provider.ts +++ b/vscode-trace-common/src/server/trace-server-url-provider.ts @@ -1,13 +1,13 @@ -export class TraceServerUrlProvider { - private onDidChangeConfigurationHandlers: ((url: string) => void)[] = []; - - public updateTraceServerUrl = (newUrl: string): void => { - this.onDidChangeConfigurationHandlers.forEach(handler => { - handler(newUrl); - }); - }; - - public onTraceServerUrlChange(handler: ((url: string) => void)): void{ - this.onDidChangeConfigurationHandlers.push(handler); - } -} +export class TraceServerUrlProvider { + private onDidChangeConfigurationHandlers: ((url: string) => void)[] = []; + + public updateTraceServerUrl = (newUrl: string): void => { + this.onDidChangeConfigurationHandlers.forEach(handler => { + handler(newUrl); + }); + }; + + public onTraceServerUrlChange(handler: (url: string) => void): void { + this.onDidChangeConfigurationHandlers.push(handler); + } +} diff --git a/vscode-trace-common/src/signals/vscode-signal-converter.ts b/vscode-trace-common/src/signals/vscode-signal-converter.ts index 74beeef4..004b428a 100644 --- a/vscode-trace-common/src/signals/vscode-signal-converter.ts +++ b/vscode-trace-common/src/signals/vscode-signal-converter.ts @@ -31,4 +31,3 @@ export function convertSignalTraces(signalExperiment: Experiment): Trace[] { }); return traces; } - diff --git a/vscode-trace-extension/src/common/trace-message.ts b/vscode-trace-extension/src/common/trace-message.ts index 64787c8b..20eae37f 100644 --- a/vscode-trace-extension/src/common/trace-message.ts +++ b/vscode-trace-extension/src/common/trace-message.ts @@ -1,16 +1,20 @@ import * as vscode from 'vscode'; import * as Messages from 'traceviewer-base/lib/message-manager'; -const statusBarItem: { [ key: string ]: vscode.StatusBarItem} = {}; +const statusBarItem: { [key: string]: vscode.StatusBarItem } = {}; -const statusPerExperiment: { [ key: string]: { [key: string]: string}} = {}; +const statusPerExperiment: { [key: string]: { [key: string]: string } } = {}; function getOrCreateBarItem(key: string, category: Messages.MessageCategory) { const statusBar = getBarItem(key); if (statusBar) { return statusBar; } - const newBarItem = vscode.window.createStatusBarItem(category === Messages.MessageCategory.TRACE_CONTEXT ? vscode.StatusBarAlignment.Left : vscode.StatusBarAlignment.Right); + const newBarItem = vscode.window.createStatusBarItem( + category === Messages.MessageCategory.TRACE_CONTEXT + ? vscode.StatusBarAlignment.Left + : vscode.StatusBarAlignment.Right + ); statusBarItem[key] = newBarItem; return newBarItem; } @@ -31,33 +35,36 @@ function removeStatusForPanel(panelName: string, messageKey: string) { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function handleStatusMessage(panelName: string, { - text = '', - category = Messages.MessageCategory.SERVER_MESSAGE, - severity = Messages.MessageSeverity.INFO, - messageKey = '' -}): void { +export function handleStatusMessage( + panelName: string, + { + text = '', + category = Messages.MessageCategory.SERVER_MESSAGE, + severity = Messages.MessageSeverity.INFO, + messageKey = '' + } +): void { switch (severity) { - case Messages.MessageSeverity.ERROR: - vscode.window.showErrorMessage(text); - return; - case Messages.MessageSeverity.WARNING: - case Messages.MessageSeverity.INFO: - const barItem = getOrCreateBarItem(messageKey, category); - barItem.text = text; - barItem.show(); - if (messageKey) { - setStatusForPanel(panelName, messageKey, text); - } - return; - case Messages.MessageSeverity.DEBUG: - console.log('Status message ' + messageKey + '(' + category + '): ' + text); - return; + case Messages.MessageSeverity.ERROR: + vscode.window.showErrorMessage(text); + return; + case Messages.MessageSeverity.WARNING: + case Messages.MessageSeverity.INFO: + const barItem = getOrCreateBarItem(messageKey, category); + barItem.text = text; + barItem.show(); + if (messageKey) { + setStatusForPanel(panelName, messageKey, text); + } + return; + case Messages.MessageSeverity.DEBUG: + console.log('Status message ' + messageKey + '(' + category + '): ' + text); + return; } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function handleRemoveMessage(panelName: string, {messageKey = '' }): void { +export function handleRemoveMessage(panelName: string, { messageKey = '' }): void { const barItem = getBarItem(messageKey); if (barItem) { barItem.text = ''; diff --git a/vscode-trace-extension/src/extension.ts b/vscode-trace-extension/src/extension.ts index 0b2a3b67..f5feb55e 100644 --- a/vscode-trace-extension/src/extension.ts +++ b/vscode-trace-extension/src/extension.ts @@ -5,11 +5,18 @@ import { TraceExplorerItemPropertiesProvider } from './trace-explorer/properties import { TraceExplorerTimeRangeDataProvider } from './trace-explorer/time-range/trace-explorer-time-range-data-webview-provider'; import { TraceExplorerAvailableViewsProvider } from './trace-explorer/available-views/trace-explorer-available-views-webview-provider'; import { TraceExplorerOpenedTracesViewProvider } from './trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider'; -import { fileHandler, openOverviewHandler, resetZoomHandler, undoRedoHandler, zoomHandler, keyboardShortcutsHandler } from './trace-explorer/trace-tree'; +import { + fileHandler, + openOverviewHandler, + resetZoomHandler, + undoRedoHandler, + zoomHandler, + keyboardShortcutsHandler +} from './trace-explorer/trace-tree'; import { TraceServerConnectionStatusService } from './utils/trace-server-status'; import { getTraceServerUrl, getTspClientUrl, updateTspClient } from './utils/tspClient'; import { TraceExtensionLogger } from './utils/trace-extension-logger'; -import { ExternalAPI, traceExtensionAPI} from './external-api/external-api'; +import { ExternalAPI, traceExtensionAPI } from './external-api/external-api'; import { TraceExtensionWebviewManager } from './utils/trace-extension-webview-manager'; import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message-manager'; import { TraceViewerPanel } from './trace-viewer-panel/trace-viewer-webview-panel'; @@ -24,116 +31,156 @@ export function activate(context: vscode.ExtensionContext): ExternalAPI { traceLogger = new TraceExtensionLogger('Trace Extension'); const serverStatusBarItemPriority = 1; - const serverStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, serverStatusBarItemPriority); + const serverStatusBarItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + serverStatusBarItemPriority + ); context.subscriptions.push(serverStatusBarItem); const serverStatusService = new TraceServerConnectionStatusService(serverStatusBarItem); const tracesProvider = new TraceExplorerOpenedTracesViewProvider(context.extensionUri, serverStatusService); context.subscriptions.push( - vscode.window.registerWebviewViewProvider(TraceExplorerOpenedTracesViewProvider.viewType, tracesProvider)); + vscode.window.registerWebviewViewProvider(TraceExplorerOpenedTracesViewProvider.viewType, tracesProvider) + ); const myAnalysisProvider = new TraceExplorerAvailableViewsProvider(context.extensionUri, serverStatusService); context.subscriptions.push( - vscode.window.registerWebviewViewProvider(TraceExplorerAvailableViewsProvider.viewType, myAnalysisProvider)); + vscode.window.registerWebviewViewProvider(TraceExplorerAvailableViewsProvider.viewType, myAnalysisProvider) + ); const propertiesProvider = new TraceExplorerItemPropertiesProvider(context.extensionUri); context.subscriptions.push( - vscode.window.registerWebviewViewProvider(TraceExplorerItemPropertiesProvider.viewType, propertiesProvider)); + vscode.window.registerWebviewViewProvider(TraceExplorerItemPropertiesProvider.viewType, propertiesProvider) + ); const timeRangeDataProvider = new TraceExplorerTimeRangeDataProvider(context.extensionUri); context.subscriptions.push( - vscode.window.registerWebviewViewProvider(TraceExplorerTimeRangeDataProvider.viewType, timeRangeDataProvider)); + vscode.window.registerWebviewViewProvider(TraceExplorerTimeRangeDataProvider.viewType, timeRangeDataProvider) + ); - context.subscriptions.push(vscode.commands.registerCommand('messages.post.propertiespanel', (command: string, data) => { - if (propertiesProvider) { - propertiesProvider.postMessagetoWebview(command, data); - } - })); + context.subscriptions.push( + vscode.commands.registerCommand('messages.post.propertiespanel', (command: string, data) => { + if (propertiesProvider) { + propertiesProvider.postMessagetoWebview(command, data); + } + }) + ); const analysisProvider = new AnalysisProvider(); // TODO: For now, a different command opens traces from file explorer. Remove when we have a proper trace finder const fileOpenHandler = fileHandler(analysisProvider); - context.subscriptions.push(vscode.commands.registerCommand('traces.openTraceFile', async file => { - await startTraceServerIfAvailable(); - if (await isUp()) { - fileOpenHandler(context, file); - } - })); + context.subscriptions.push( + vscode.commands.registerCommand('traces.openTraceFile', async file => { + await startTraceServerIfAvailable(); + if (await isUp()) { + fileOpenHandler(context, file); + } + }) + ); // Listening to configuration change for the trace server URL - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('trace-compass.traceserver.url') || e.affectsConfiguration('trace-compass.traceserver.apiPath')) { - updateTspClient(); - } + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(e => { + if ( + e.affectsConfiguration('trace-compass.traceserver.url') || + e.affectsConfiguration('trace-compass.traceserver.apiPath') + ) { + updateTspClient(); + } + + if (e.affectsConfiguration('trace-compass.traceserver.url')) { + const newUrl = getTraceServerUrl(); + + // Signal the change to the `Opened traces` and `Available views` webview + tracesProvider.updateTraceServerUrl(newUrl); + myAnalysisProvider.updateTraceServerUrl(newUrl); + + // Signal the change to all trace panels + TraceViewerPanel.updateTraceServerUrl(newUrl); + } + }) + ); + + const overViewOpenHandler = openOverviewHandler(); + + const zoomResetHandler = resetZoomHandler(); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.reset', () => { + zoomResetHandler(); + }) + ); - if (e.affectsConfiguration('trace-compass.traceserver.url')) { - const newUrl = getTraceServerUrl(); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.openOverview', () => { + overViewOpenHandler(); + }) + ); - // Signal the change to the `Opened traces` and `Available views` webview - tracesProvider.updateTraceServerUrl(newUrl); - myAnalysisProvider.updateTraceServerUrl(newUrl); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.undo', () => { + undoRedoHandler(true); + }) + ); - // Signal the change to all trace panels - TraceViewerPanel.updateTraceServerUrl(newUrl); - } - })); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.redo', () => { + undoRedoHandler(false); + }) + ); - const overViewOpenHandler = openOverviewHandler(); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.zoomIn', () => { + zoomHandler(true); + }) + ); - const zoomResetHandler = resetZoomHandler(); - context.subscriptions.push(vscode.commands.registerCommand('outputs.reset', () => { - zoomResetHandler(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('outputs.openOverview', () => { - overViewOpenHandler(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('outputs.undo', () => { - undoRedoHandler(true); - })); - - context.subscriptions.push(vscode.commands.registerCommand('outputs.redo', () => { - undoRedoHandler(false); - })); - - context.subscriptions.push(vscode.commands.registerCommand('outputs.zoomIn', () => { - zoomHandler(true); - })); - - context.subscriptions.push(vscode.commands.registerCommand('outputs.zoomOut', () => { - zoomHandler(false); - })); - - context.subscriptions.push(vscode.commands.registerCommand('trace.viewer.toolbar.markersets', () => { - TraceViewerPanel.showMarkerSetsOnCurrent(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('trace.viewer.toolbar.filter', () => { - TraceViewerPanel.showMarkersFilterOnCurrent(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('openedTraces.openTraceFolder', async () => { - await startTraceServerIfAvailable(); - if (await isUp()) { - fileOpenHandler(context, undefined); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('traceViewer.shortcuts', () => { - keyboardShortcutsHandler(context.extensionUri); - })); - - context.subscriptions.push(vscode.commands.registerCommand('serverStatus.started', () => { - serverStatusService.render(true); - if (tracesProvider) { - tracesProvider.postMessagetoWebview(VSCODE_MESSAGES.TRACE_SERVER_STARTED, undefined); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('serverStatus.stopped', () => { - serverStatusService.render(false); - })); + context.subscriptions.push( + vscode.commands.registerCommand('outputs.zoomOut', () => { + zoomHandler(false); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('trace.viewer.toolbar.markersets', () => { + TraceViewerPanel.showMarkerSetsOnCurrent(); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('trace.viewer.toolbar.filter', () => { + TraceViewerPanel.showMarkersFilterOnCurrent(); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('openedTraces.openTraceFolder', async () => { + await startTraceServerIfAvailable(); + if (await isUp()) { + fileOpenHandler(context, undefined); + } + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('traceViewer.shortcuts', () => { + keyboardShortcutsHandler(context.extensionUri); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('serverStatus.started', () => { + serverStatusService.render(true); + if (tracesProvider) { + tracesProvider.postMessagetoWebview(VSCODE_MESSAGES.TRACE_SERVER_STARTED, undefined); + } + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('serverStatus.stopped', () => { + serverStatusService.render(false); + }) + ); vscode.commands.executeCommand('setContext', 'traceViewer.markerSetsPresent', false); vscode.commands.executeCommand('setContext', 'traceViewer.markerCategoriesPresent', false); @@ -148,7 +195,7 @@ export function deactivate(): void { async function startTraceServerIfAvailable(): Promise { const extensionId = 'vscode-trace-server'; const traceServerExtension = vscode.extensions.getExtension('tracecompass-community.' + extensionId); - if (!traceServerExtension || await isUp()) { + if (!traceServerExtension || (await isUp())) { return; } await vscode.commands.executeCommand(extensionId + '.start-if-stopped'); diff --git a/vscode-trace-extension/src/external-api/external-api.ts b/vscode-trace-extension/src/external-api/external-api.ts index cf927eb0..ce161651 100644 --- a/vscode-trace-extension/src/external-api/external-api.ts +++ b/vscode-trace-extension/src/external-api/external-api.ts @@ -9,11 +9,11 @@ import * as vscode from 'vscode'; import { traceExtensionWebviewManager } from '../extension'; export interface ExternalAPI { - getActiveExperiment(): Experiment | undefined, - getActiveWebviewPanels(): { [key: string]: TraceViewerPanel | undefined; }, - getActiveWebviews(): vscode.WebviewView[], - onWebviewCreated(listener: (data: vscode.WebviewView) => void): void, - onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => void): void, + getActiveExperiment(): Experiment | undefined; + getActiveWebviewPanels(): { [key: string]: TraceViewerPanel | undefined }; + getActiveWebviews(): vscode.WebviewView[]; + onWebviewCreated(listener: (data: vscode.WebviewView) => void): void; + onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => void): void; } export const traceExtensionAPI: ExternalAPI = { @@ -31,7 +31,7 @@ export const traceExtensionAPI: ExternalAPI = { * * @returns Key value pairs where the value is of TraceViewerPanel type if panel with that key is active, otherwise undefined */ - getActiveWebviewPanels(): { [key: string]: TraceViewerPanel | undefined; } { + getActiveWebviewPanels(): { [key: string]: TraceViewerPanel | undefined } { return TraceViewerPanel.activePanels; }, diff --git a/vscode-trace-extension/src/test/extension-test.spec.ts b/vscode-trace-extension/src/test/extension-test.spec.ts index e9a64966..df2d714d 100644 --- a/vscode-trace-extension/src/test/extension-test.spec.ts +++ b/vscode-trace-extension/src/test/extension-test.spec.ts @@ -11,7 +11,7 @@ test('Open Trace from Explorer', async ({ page }) => { await page.getByRole('treeitem', { name: 'cat-kernel' }).locator('a').click({ button: 'right' }); await page.getByRole('menuitem', { name: 'Open with Trace Viewer' }).hover(); await page.getByRole('menuitem', { name: 'Open with Trace Viewer' }).click(); - await expect(page.getByRole('tab', { name: 'cat-kernel'})).toBeVisible(); + await expect(page.getByRole('tab', { name: 'cat-kernel' })).toBeVisible(); }); test('Open Trace from Trace Viewer', async ({ page }) => { @@ -22,5 +22,5 @@ test('Open Trace from Trace Viewer', async ({ page }) => { await page.getByRole('option', { name: 'cat-kernel' }).locator('a').click(); await page.waitForTimeout(1000); await page.getByRole('button', { name: 'OK' }).click(); - await expect(page.getByRole('tab', { name: 'cat-kernel'})).toBeVisible(); + await expect(page.getByRole('tab', { name: 'cat-kernel' })).toBeVisible(); }); diff --git a/vscode-trace-extension/src/trace-explorer/analysis-tree.ts b/vscode-trace-extension/src/trace-explorer/analysis-tree.ts index 65379fad..23e0ae02 100644 --- a/vscode-trace-extension/src/trace-explorer/analysis-tree.ts +++ b/vscode-trace-extension/src/trace-explorer/analysis-tree.ts @@ -4,47 +4,46 @@ import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descri import { TraceViewerPanel } from '../trace-viewer-panel/trace-viewer-webview-panel'; export class AnalysisProvider implements vscode.TreeDataProvider { - - private descriptors: OutputDescriptor[] = []; - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - - getTreeItem(element: Analysis): vscode.TreeItem { - return element; - } - - getChildren(element?: Analysis): Thenable { - if (element) { - return Promise.resolve([]); - } else { - if (this.descriptors.length === 0) { - return Promise.resolve([]); - } else { - return Promise.resolve(this.descriptors.map(d => new Analysis(d))); - } - } - } - - refresh(descriptors: OutputDescriptor[]): void { - this.descriptors = descriptors; - this._onDidChangeTreeData.fire(undefined); - } + private descriptors: OutputDescriptor[] = []; + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter< + Analysis | undefined + >(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + getTreeItem(element: Analysis): vscode.TreeItem { + return element; + } + + getChildren(element?: Analysis): Thenable { + if (element) { + return Promise.resolve([]); + } else { + if (this.descriptors.length === 0) { + return Promise.resolve([]); + } else { + return Promise.resolve(this.descriptors.map(d => new Analysis(d))); + } + } + } + + refresh(descriptors: OutputDescriptor[]): void { + this.descriptors = descriptors; + this._onDidChangeTreeData.fire(undefined); + } } class Analysis extends vscode.TreeItem { - _descriptor: OutputDescriptor; - constructor( - public readonly descriptor: OutputDescriptor, - ) { - super(descriptor.name); - this._descriptor = descriptor; - this.tooltip = `${this._descriptor.description}`; - } - - iconPath = { - light: path.join(__dirname, '..', '..', '..', 'resources', 'light', 'refresh.svg'), - dark: path.join(__dirname, '..', '..', '..', 'resources', 'dark', 'refresh.svg') - }; + _descriptor: OutputDescriptor; + constructor(public readonly descriptor: OutputDescriptor) { + super(descriptor.name); + this._descriptor = descriptor; + this.tooltip = `${this._descriptor.description}`; + } + + iconPath = { + light: path.join(__dirname, '..', '..', '..', 'resources', 'light', 'refresh.svg'), + dark: path.join(__dirname, '..', '..', '..', 'resources', 'dark', 'refresh.svg') + }; } export const analysisHandler = (context: vscode.ExtensionContext, analysis: Analysis): void => { diff --git a/vscode-trace-extension/src/trace-explorer/available-views/trace-explorer-available-views-webview-provider.ts b/vscode-trace-extension/src/trace-explorer/available-views/trace-explorer-available-views-webview-provider.ts index d21581b4..1a5f6d8c 100644 --- a/vscode-trace-extension/src/trace-explorer/available-views/trace-explorer-available-views-webview-provider.ts +++ b/vscode-trace-extension/src/trace-explorer/available-views/trace-explorer-available-views-webview-provider.ts @@ -11,121 +11,134 @@ import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message import { traceExtensionWebviewManager } from 'vscode-trace-extension/src/extension'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); export class TraceExplorerAvailableViewsProvider implements vscode.WebviewViewProvider { + public static readonly viewType = 'traceExplorer.availableViews'; - public static readonly viewType = 'traceExplorer.availableViews'; - - private _view?: vscode.WebviewView; - private _disposables: vscode.Disposable[] = []; - private _selectionOngoing = false; - private _selectedExperiment: Experiment | undefined; - - private _onExperimentSelected = (experiment: Experiment | undefined): void => this.doHandleExperimentSelectedSignal(experiment); - - constructor( - private readonly _extensionUri: vscode.Uri, - private readonly _statusService: TraceServerConnectionStatusService, - ) { } - - public updateTraceServerUrl(newUrl: string): void { - if (this._view) { - this._view.webview.postMessage({command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, data: newUrl}); - } - } - - public resolveWebviewView( - webviewView: vscode.WebviewView, - _context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken, - ): void { - this._view = webviewView; - - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - - localResourceRoots: [ - vscode.Uri.joinPath(this._extensionUri, 'pack'), - vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') - ] - }; - - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - traceExtensionWebviewManager.fireWebviewCreated(webviewView); - - // Handle messages from the webview - webviewView.webview.onDidReceiveMessage(message => { - switch (message.command) { - case VSCODE_MESSAGES.CONNECTION_STATUS: - if (message.data && message.data.status) { - const status: boolean = JSON.parse(message.data.status); - this._statusService.render(status); - } - return; - case VSCODE_MESSAGES.WEBVIEW_READY: - // Post the tspTypescriptClient - webviewView.webview.postMessage({command: VSCODE_MESSAGES.SET_TSP_CLIENT, data: getTspClientUrl()}); - if (this._selectedExperiment !== undefined) { - signalManager().fireExperimentSelectedSignal(this._selectedExperiment); - } - return; - case VSCODE_MESSAGES.OUTPUT_ADDED: - if (message.data && message.data.descriptor) { - // FIXME: JSONBig.parse() created bigint if numbers are small. - // Not an issue right now for output descriptors. - const descriptor = JSONBig.parse(message.data.descriptor) as OutputDescriptor; - // TODO: Don't use static current panel, i.e. find better design to add output... - - TraceViewerPanel.addOutputToCurrent(descriptor); - // const panel = TraceViewerPanel.createOrShow(this._extensionUri, message.data.experiment.name); - // panel.setExperiment(message.data.experiment); - } - return; - case VSCODE_MESSAGES.EXPERIMENT_SELECTED: { - try { - this._selectionOngoing = true; - if (message.data && message.data.wrapper) { - // Avoid endless forwarding of signal - this._selectedExperiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); - } else { - this._selectedExperiment = undefined; - } - signalManager().fireExperimentSelectedSignal(this._selectedExperiment); - } finally { - this._selectionOngoing = false; - } - } - } - }, undefined, this._disposables); - - signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - webviewView.onDidDispose(_event => { - signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - }, undefined, this._disposables); - } - - protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - if (!this._selectionOngoing && this._view) { - this._selectedExperiment = experiment; - const wrapper: string = JSONBig.stringify(experiment); - this._view.webview.postMessage({command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data: wrapper}); - } - } - - /* eslint-disable max-len */ - private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'analysisPanel.js')); - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); - - return ` + private _view?: vscode.WebviewView; + private _disposables: vscode.Disposable[] = []; + private _selectionOngoing = false; + private _selectedExperiment: Experiment | undefined; + + private _onExperimentSelected = (experiment: Experiment | undefined): void => + this.doHandleExperimentSelectedSignal(experiment); + + constructor( + private readonly _extensionUri: vscode.Uri, + private readonly _statusService: TraceServerConnectionStatusService + ) {} + + public updateTraceServerUrl(newUrl: string): void { + if (this._view) { + this._view.webview.postMessage({ command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, data: newUrl }); + } + } + + public resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ): void { + this._view = webviewView; + + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + + localResourceRoots: [ + vscode.Uri.joinPath(this._extensionUri, 'pack'), + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') + ] + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + traceExtensionWebviewManager.fireWebviewCreated(webviewView); + + // Handle messages from the webview + webviewView.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case VSCODE_MESSAGES.CONNECTION_STATUS: + if (message.data && message.data.status) { + const status: boolean = JSON.parse(message.data.status); + this._statusService.render(status); + } + return; + case VSCODE_MESSAGES.WEBVIEW_READY: + // Post the tspTypescriptClient + webviewView.webview.postMessage({ + command: VSCODE_MESSAGES.SET_TSP_CLIENT, + data: getTspClientUrl() + }); + if (this._selectedExperiment !== undefined) { + signalManager().fireExperimentSelectedSignal(this._selectedExperiment); + } + return; + case VSCODE_MESSAGES.OUTPUT_ADDED: + if (message.data && message.data.descriptor) { + // FIXME: JSONBig.parse() created bigint if numbers are small. + // Not an issue right now for output descriptors. + const descriptor = JSONBig.parse(message.data.descriptor) as OutputDescriptor; + // TODO: Don't use static current panel, i.e. find better design to add output... + + TraceViewerPanel.addOutputToCurrent(descriptor); + // const panel = TraceViewerPanel.createOrShow(this._extensionUri, message.data.experiment.name); + // panel.setExperiment(message.data.experiment); + } + return; + case VSCODE_MESSAGES.EXPERIMENT_SELECTED: { + try { + this._selectionOngoing = true; + if (message.data && message.data.wrapper) { + // Avoid endless forwarding of signal + this._selectedExperiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); + } else { + this._selectedExperiment = undefined; + } + signalManager().fireExperimentSelectedSignal(this._selectedExperiment); + } finally { + this._selectionOngoing = false; + } + } + } + }, + undefined, + this._disposables + ); + + signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + webviewView.onDidDispose( + _event => { + signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + }, + undefined, + this._disposables + ); + } + + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + if (!this._selectionOngoing && this._view) { + this._selectedExperiment = experiment; + const wrapper: string = JSONBig.stringify(experiment); + this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data: wrapper }); + } + } + + /* eslint-disable max-len */ + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'analysisPanel.js')); + const codiconsUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css') + ); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + + return ` @@ -153,7 +166,7 @@ export class TraceExplorerAvailableViewsProvider implements vscode.WebviewViewPr `; - } + } } function getNonce() { diff --git a/vscode-trace-extension/src/trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider.ts b/vscode-trace-extension/src/trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider.ts index 6ca0b9b7..e3e67519 100644 --- a/vscode-trace-extension/src/trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider.ts +++ b/vscode-trace-extension/src/trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider.ts @@ -10,161 +10,179 @@ import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message import { traceExtensionWebviewManager } from 'vscode-trace-extension/src/extension'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = 'traceExplorer.openedTracesView'; + + private _view?: vscode.WebviewView; + private _disposables: vscode.Disposable[] = []; + private _selectedExperiment: Experiment | undefined; + + constructor( + private readonly _extensionUri: vscode.Uri, + private readonly _statusService: TraceServerConnectionStatusService + ) {} + + private _onOpenedTracesWidgetActivated = (experiment: Experiment): void => + this.doHandleTracesWidgetActivatedSignal(experiment); + private _onExperimentSelected = (experiment: Experiment | undefined): void => + this.doHandleExperimentSelectedSignal(experiment); + private _onExperimentOpened = (experiment: Experiment): void => this.doHandleExperimentOpenedSignal(experiment); + + protected doHandleExperimentOpenedSignal(experiment: Experiment): void { + if (this._view && experiment) { + const wrapper: string = JSONBig.stringify(experiment); + this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_OPENED, data: wrapper }); + } + } + + protected doHandleTracesWidgetActivatedSignal(experiment: Experiment): void { + if (this._view && experiment) { + this._selectedExperiment = experiment; + const wrapper: string = JSONBig.stringify(experiment); + this._view.webview.postMessage({ command: VSCODE_MESSAGES.TRACE_VIEWER_TAB_ACTIVATED, data: wrapper }); + signalManager().fireExperimentSelectedSignal(this._selectedExperiment); + } + } + + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + if (this._view) { + this._selectedExperiment = experiment; + } + } + + public updateTraceServerUrl(newUrl: string): void { + if (this._view) { + this._view.webview.postMessage({ command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, data: newUrl }); + } + } - public static readonly viewType = 'traceExplorer.openedTracesView'; - - private _view?: vscode.WebviewView; - private _disposables: vscode.Disposable[] = []; - private _selectedExperiment: Experiment | undefined; - - constructor( - private readonly _extensionUri: vscode.Uri, - private readonly _statusService: TraceServerConnectionStatusService, - ) {} - - private _onOpenedTracesWidgetActivated = (experiment: Experiment): void => this.doHandleTracesWidgetActivatedSignal(experiment); - private _onExperimentSelected = (experiment: Experiment | undefined): void => this.doHandleExperimentSelectedSignal(experiment); - private _onExperimentOpened = (experiment: Experiment): void => this.doHandleExperimentOpenedSignal(experiment); - - protected doHandleExperimentOpenedSignal(experiment: Experiment): void { - if (this._view && experiment) { - const wrapper: string = JSONBig.stringify(experiment); - this._view.webview.postMessage({command: VSCODE_MESSAGES.EXPERIMENT_OPENED, data: wrapper}); - } - } - - protected doHandleTracesWidgetActivatedSignal(experiment: Experiment): void { - if (this._view && experiment) { - this._selectedExperiment = experiment; - const wrapper: string = JSONBig.stringify(experiment); - this._view.webview.postMessage({command: VSCODE_MESSAGES.TRACE_VIEWER_TAB_ACTIVATED, data: wrapper}); - signalManager().fireExperimentSelectedSignal(this._selectedExperiment); - } - } - - protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - if (this._view) { - this._selectedExperiment = experiment; - } - } - - public updateTraceServerUrl(newUrl: string): void { - if (this._view) { - this._view.webview.postMessage({command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, data: newUrl}); - } - } - - public resolveWebviewView( - webviewView: vscode.WebviewView, - _context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken, - ): void { - this._view = webviewView; - - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - - localResourceRoots: [ - vscode.Uri.joinPath(this._extensionUri, 'pack'), - vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') - ] - }; - - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - traceExtensionWebviewManager.fireWebviewCreated(webviewView); - - // Handle messages from the webview - webviewView.webview.onDidReceiveMessage(message => { - switch (message.command) { - case VSCODE_MESSAGES.CONNECTION_STATUS: - if (message.data && message.data.status) { - const status: boolean = JSON.parse(message.data.status); - this._statusService.render(status); - } - return; - case VSCODE_MESSAGES.WEBVIEW_READY: - // Post the tspTypescriptClient - webviewView.webview.postMessage({command: VSCODE_MESSAGES.SET_TSP_CLIENT, data: getTspClientUrl()}); - if (this._selectedExperiment !== undefined) { - // tabActivatedSignal will select the experiment in the open traces widget - signalManager().fireTraceViewerTabActivatedSignal(this._selectedExperiment); - // experimentSelectedSignal will update available views widget - signalManager().fireExperimentSelectedSignal(this._selectedExperiment); - } - return; - case VSCODE_MESSAGES.RE_OPEN_TRACE: - if (message.data && message.data.wrapper) { - const experiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); - const panel = TraceViewerPanel.createOrShow(this._extensionUri, experiment.name, this._statusService); - panel.setExperiment(experiment); - signalManager().fireExperimentSelectedSignal(experiment); - } - return; - case VSCODE_MESSAGES.CLOSE_TRACE: - case VSCODE_MESSAGES.DELETE_TRACE: - if (message.data && message.data.wrapper) { - // just remove the panel here - TraceViewerPanel.disposePanel(this._extensionUri, JSONBig.parse(message.data.wrapper).name); - signalManager().fireExperimentSelectedSignal(undefined); - } - return; - case VSCODE_MESSAGES.OPENED_TRACES_UPDATED: - if (message.numberOfOpenedTraces > 0) { - vscode.commands.executeCommand('setContext', 'trace-explorer.noExperiments', false); - } else if (message.numberOfOpenedTraces === 0){ - vscode.commands.executeCommand('setContext', 'trace-explorer.noExperiments', true); - } - return; - case VSCODE_MESSAGES.OPEN_TRACE: - vscode.commands.executeCommand('openedTraces.openTraceFolder'); - return; - case VSCODE_MESSAGES.EXPERIMENT_SELECTED: { - let experiment: Experiment | undefined; - if (message.data && message.data.wrapper) { - experiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); - } else { - experiment = undefined; - } - signalManager().fireExperimentSelectedSignal(experiment); - } - } - }, undefined, this._disposables); - - signalManager().on(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated); - signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().on(Signals.EXPERIMENT_OPENED, this._onExperimentOpened); - webviewView.onDidDispose(_event => { - signalManager().off(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated); - signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - }, undefined, this._disposables); - } - - postMessagetoWebview(_command: string, _data: unknown): void { - if (this._view && _command) { - if (_data) { - this._view.webview.postMessage({command: _command, data: _data}); - } else { - this._view.webview.postMessage({command: _command}); - } - } - } - - /* eslint-disable max-len */ - private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'openedTracesPanel.js')); - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); - - return ` + public resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ): void { + this._view = webviewView; + + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + + localResourceRoots: [ + vscode.Uri.joinPath(this._extensionUri, 'pack'), + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') + ] + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + traceExtensionWebviewManager.fireWebviewCreated(webviewView); + + // Handle messages from the webview + webviewView.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case VSCODE_MESSAGES.CONNECTION_STATUS: + if (message.data && message.data.status) { + const status: boolean = JSON.parse(message.data.status); + this._statusService.render(status); + } + return; + case VSCODE_MESSAGES.WEBVIEW_READY: + // Post the tspTypescriptClient + webviewView.webview.postMessage({ + command: VSCODE_MESSAGES.SET_TSP_CLIENT, + data: getTspClientUrl() + }); + if (this._selectedExperiment !== undefined) { + // tabActivatedSignal will select the experiment in the open traces widget + signalManager().fireTraceViewerTabActivatedSignal(this._selectedExperiment); + // experimentSelectedSignal will update available views widget + signalManager().fireExperimentSelectedSignal(this._selectedExperiment); + } + return; + case VSCODE_MESSAGES.RE_OPEN_TRACE: + if (message.data && message.data.wrapper) { + const experiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); + const panel = TraceViewerPanel.createOrShow( + this._extensionUri, + experiment.name, + this._statusService + ); + panel.setExperiment(experiment); + signalManager().fireExperimentSelectedSignal(experiment); + } + return; + case VSCODE_MESSAGES.CLOSE_TRACE: + case VSCODE_MESSAGES.DELETE_TRACE: + if (message.data && message.data.wrapper) { + // just remove the panel here + TraceViewerPanel.disposePanel(this._extensionUri, JSONBig.parse(message.data.wrapper).name); + signalManager().fireExperimentSelectedSignal(undefined); + } + return; + case VSCODE_MESSAGES.OPENED_TRACES_UPDATED: + if (message.numberOfOpenedTraces > 0) { + vscode.commands.executeCommand('setContext', 'trace-explorer.noExperiments', false); + } else if (message.numberOfOpenedTraces === 0) { + vscode.commands.executeCommand('setContext', 'trace-explorer.noExperiments', true); + } + return; + case VSCODE_MESSAGES.OPEN_TRACE: + vscode.commands.executeCommand('openedTraces.openTraceFolder'); + return; + case VSCODE_MESSAGES.EXPERIMENT_SELECTED: { + let experiment: Experiment | undefined; + if (message.data && message.data.wrapper) { + experiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper)); + } else { + experiment = undefined; + } + signalManager().fireExperimentSelectedSignal(experiment); + } + } + }, + undefined, + this._disposables + ); + + signalManager().on(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated); + signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().on(Signals.EXPERIMENT_OPENED, this._onExperimentOpened); + webviewView.onDidDispose( + _event => { + signalManager().off(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated); + signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + }, + undefined, + this._disposables + ); + } + + postMessagetoWebview(_command: string, _data: unknown): void { + if (this._view && _command) { + if (_data) { + this._view.webview.postMessage({ command: _command, data: _data }); + } else { + this._view.webview.postMessage({ command: _command }); + } + } + } + + /* eslint-disable max-len */ + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'openedTracesPanel.js')); + const codiconsUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css') + ); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + + return ` @@ -192,7 +210,7 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView `; - } + } } function getNonce() { diff --git a/vscode-trace-extension/src/trace-explorer/properties/trace-explorer-properties-view-webview-provider.ts b/vscode-trace-extension/src/trace-explorer/properties/trace-explorer-properties-view-webview-provider.ts index 8eeeee5c..413bdd13 100644 --- a/vscode-trace-extension/src/trace-explorer/properties/trace-explorer-properties-view-webview-provider.ts +++ b/vscode-trace-extension/src/trace-explorer/properties/trace-explorer-properties-view-webview-provider.ts @@ -8,45 +8,47 @@ import { traceExtensionWebviewManager } from 'vscode-trace-extension/src/extensi import { getTraceServerUrl } from 'vscode-trace-extension/src/utils/tspClient'; export class TraceExplorerItemPropertiesProvider implements vscode.WebviewViewProvider { - public static readonly viewType = 'traceExplorer.itemPropertiesView'; - private _view?: vscode.WebviewView; - constructor(private readonly _extensionUri: vscode.Uri) {} + public static readonly viewType = 'traceExplorer.itemPropertiesView'; + private _view?: vscode.WebviewView; + constructor(private readonly _extensionUri: vscode.Uri) {} - resolveWebviewView( - webviewView: vscode.WebviewView, - _context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken - ): void { - this._view = webviewView; - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - localResourceRoots: [ - vscode.Uri.joinPath(this._extensionUri, 'pack'), - vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') - ] - }; - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - traceExtensionWebviewManager.fireWebviewCreated(webviewView); - } + resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ): void { + this._view = webviewView; + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + localResourceRoots: [ + vscode.Uri.joinPath(this._extensionUri, 'pack'), + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') + ] + }; + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + traceExtensionWebviewManager.fireWebviewCreated(webviewView); + } - postMessagetoWebview(_command: string, _data: unknown): void { - if (this._view && _command && _data) { - this._view.webview.postMessage({command: _command, data: _data}); - } - } + postMessagetoWebview(_command: string, _data: unknown): void { + if (this._view && _command && _data) { + this._view.webview.postMessage({ command: _command, data: _data }); + } + } - /* eslint-disable max-len */ - private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'propertiesPanel.js')); - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + /* eslint-disable max-len */ + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'propertiesPanel.js')); + const codiconsUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css') + ); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); - return ` + return ` @@ -74,7 +76,7 @@ export class TraceExplorerItemPropertiesProvider implements vscode.WebviewViewPr `; - } + } } function getNonce() { diff --git a/vscode-trace-extension/src/trace-explorer/time-range/trace-explorer-time-range-data-webview-provider.ts b/vscode-trace-extension/src/trace-explorer/time-range/trace-explorer-time-range-data-webview-provider.ts index 31ac7f96..5e2c933d 100644 --- a/vscode-trace-extension/src/trace-explorer/time-range/trace-explorer-time-range-data-webview-provider.ts +++ b/vscode-trace-extension/src/trace-explorer/time-range/trace-explorer-time-range-data-webview-provider.ts @@ -8,118 +8,132 @@ import { Experiment } from 'tsp-typescript-client/lib/models/experiment'; import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message-manager'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); export class TraceExplorerTimeRangeDataProvider implements vscode.WebviewViewProvider { - public static readonly viewType = 'traceExplorer.timeRangeDataView'; - private _view: vscode.WebviewView; - private _disposables: vscode.Disposable[] = []; - private _experimentDataMap: TimeRangeDataMap; - constructor(private readonly _extensionUri: vscode.Uri) { - this._experimentDataMap = new TimeRangeDataMap(); - } - - resolveWebviewView( - webviewView: vscode.WebviewView, - _context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken - ): void { - this._view = webviewView; - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - localResourceRoots: [vscode.Uri.joinPath(this._extensionUri, 'pack')], - }; - - webviewView.webview.onDidReceiveMessage(message => { - const command = message?.command; - const parsedData = message?.data ? JSONBig.parse(message.data) : undefined; - - switch (command) { - case VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE: - signalManager().fireRequestSelectionRangeChange(parsedData); - break; - } - }, undefined, this._disposables); - - webviewView.onDidChangeVisibility(() => { - if (this._view.visible) { - const data = { - mapArray: Array.from(this._experimentDataMap.experimentDataMap.values()), - activeData: this._experimentDataMap.activeData, - }; - this._view.webview.postMessage({ command: VSCODE_MESSAGES.RESTORE_VIEW, data: JSONBig.stringify(data) }); - } - }); - - signalManager().on(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); - signalManager().on(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); - signalManager().on(Signals.EXPERIMENT_SELECTED, this.onExperimentSelected); - signalManager().on(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); - signalManager().on(Signals.EXPERIMENT_CLOSED, this.onExperimentClosed); - signalManager().on(Signals.CLOSE_TRACEVIEWERTAB, this.onExperimentTabClosed); - - webviewView.onDidDispose(_event => { - signalManager().off(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); - signalManager().off(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); - signalManager().off(Signals.EXPERIMENT_SELECTED, this.onExperimentSelected); - signalManager().off(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); - signalManager().off(Signals.EXPERIMENT_CLOSED, this.onExperimentClosed); - signalManager().off(Signals.CLOSE_TRACEVIEWERTAB, this.onExperimentTabClosed); - }, undefined, this._disposables); - } - - private onViewRangeUpdated = (update: TimeRangeUpdatePayload) => { - this._view.webview.postMessage({ command: VSCODE_MESSAGES.VIEW_RANGE_UPDATED, data: JSONBig.stringify(update) }); - this._experimentDataMap.updateViewRange(update); - }; - - private onSelectionRangeUpdated = (update: TimeRangeUpdatePayload) => { - this._view.webview.postMessage({ command: VSCODE_MESSAGES.SELECTION_RANGE_UPDATED, data: JSONBig.stringify(update) }); - this._experimentDataMap.updateSelectionRange(update); - }; - - private onExperimentSelected = (experiment: Experiment | undefined) => { - const data = { wrapper: experiment ? JSONBig.stringify(experiment) : undefined }; - this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data }); - if (experiment) { - this._experimentDataMap.updateAbsoluteRange(experiment); - } - this._experimentDataMap.setActiveExperiment(experiment); - - }; - - private onExperimentUpdated = (experiment: Experiment) => { - const data = { wrapper: JSONBig.stringify(experiment) }; - this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_UPDATED, data }); - this._experimentDataMap.updateAbsoluteRange(experiment); - - }; - - private onExperimentClosed = (experiment: Experiment) => { - const data = { wrapper: JSONBig.stringify(experiment) }; - this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_CLOSED, data }); - this._experimentDataMap.delete(experiment); - - }; - - private onExperimentTabClosed = (experimentUUID: string) => { - this._view.webview.postMessage({ command: VSCODE_MESSAGES.TRACE_VIEWER_TAB_CLOSED, data: experimentUUID }); - this._experimentDataMap.delete(experimentUUID); - }; - - /* eslint-disable max-len */ - private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'timeRangePanel.js')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); - - return ` + public static readonly viewType = 'traceExplorer.timeRangeDataView'; + private _view: vscode.WebviewView; + private _disposables: vscode.Disposable[] = []; + private _experimentDataMap: TimeRangeDataMap; + constructor(private readonly _extensionUri: vscode.Uri) { + this._experimentDataMap = new TimeRangeDataMap(); + } + + resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ): void { + this._view = webviewView; + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + localResourceRoots: [vscode.Uri.joinPath(this._extensionUri, 'pack')] + }; + + webviewView.webview.onDidReceiveMessage( + message => { + const command = message?.command; + const parsedData = message?.data ? JSONBig.parse(message.data) : undefined; + + switch (command) { + case VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE: + signalManager().fireRequestSelectionRangeChange(parsedData); + break; + } + }, + undefined, + this._disposables + ); + + webviewView.onDidChangeVisibility(() => { + if (this._view.visible) { + const data = { + mapArray: Array.from(this._experimentDataMap.experimentDataMap.values()), + activeData: this._experimentDataMap.activeData + }; + this._view.webview.postMessage({ + command: VSCODE_MESSAGES.RESTORE_VIEW, + data: JSONBig.stringify(data) + }); + } + }); + + signalManager().on(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); + signalManager().on(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); + signalManager().on(Signals.EXPERIMENT_SELECTED, this.onExperimentSelected); + signalManager().on(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); + signalManager().on(Signals.EXPERIMENT_CLOSED, this.onExperimentClosed); + signalManager().on(Signals.CLOSE_TRACEVIEWERTAB, this.onExperimentTabClosed); + + webviewView.onDidDispose( + _event => { + signalManager().off(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); + signalManager().off(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); + signalManager().off(Signals.EXPERIMENT_SELECTED, this.onExperimentSelected); + signalManager().off(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); + signalManager().off(Signals.EXPERIMENT_CLOSED, this.onExperimentClosed); + signalManager().off(Signals.CLOSE_TRACEVIEWERTAB, this.onExperimentTabClosed); + }, + undefined, + this._disposables + ); + } + + private onViewRangeUpdated = (update: TimeRangeUpdatePayload) => { + this._view.webview.postMessage({ + command: VSCODE_MESSAGES.VIEW_RANGE_UPDATED, + data: JSONBig.stringify(update) + }); + this._experimentDataMap.updateViewRange(update); + }; + + private onSelectionRangeUpdated = (update: TimeRangeUpdatePayload) => { + this._view.webview.postMessage({ + command: VSCODE_MESSAGES.SELECTION_RANGE_UPDATED, + data: JSONBig.stringify(update) + }); + this._experimentDataMap.updateSelectionRange(update); + }; + + private onExperimentSelected = (experiment: Experiment | undefined) => { + const data = { wrapper: experiment ? JSONBig.stringify(experiment) : undefined }; + this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data }); + if (experiment) { + this._experimentDataMap.updateAbsoluteRange(experiment); + } + this._experimentDataMap.setActiveExperiment(experiment); + }; + + private onExperimentUpdated = (experiment: Experiment) => { + const data = { wrapper: JSONBig.stringify(experiment) }; + this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_UPDATED, data }); + this._experimentDataMap.updateAbsoluteRange(experiment); + }; + + private onExperimentClosed = (experiment: Experiment) => { + const data = { wrapper: JSONBig.stringify(experiment) }; + this._view.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_CLOSED, data }); + this._experimentDataMap.delete(experiment); + }; + + private onExperimentTabClosed = (experimentUUID: string) => { + this._view.webview.postMessage({ command: VSCODE_MESSAGES.TRACE_VIEWER_TAB_CLOSED, data: experimentUUID }); + this._experimentDataMap.delete(experimentUUID); + }; + + /* eslint-disable max-len */ + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'timeRangePanel.js')); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + + return ` @@ -140,7 +154,7 @@ export class TraceExplorerTimeRangeDataProvider implements vscode.WebviewViewPro `; - } + } } function getNonce() { @@ -151,4 +165,3 @@ function getNonce() { } return text; } - diff --git a/vscode-trace-extension/src/trace-explorer/trace-tree.ts b/vscode-trace-extension/src/trace-explorer/trace-tree.ts index 5a1a70ae..5b869da2 100644 --- a/vscode-trace-extension/src/trace-explorer/trace-tree.ts +++ b/vscode-trace-extension/src/trace-explorer/trace-tree.ts @@ -45,17 +45,21 @@ export class TracesProvider implements vscode.TreeDataProvider { return Promise.resolve([]); } else { return Promise.resolve( - fs.readdirSync(this.workspaceRoot, { withFileTypes: true }) + fs + .readdirSync(this.workspaceRoot, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name).map(dir => this.getTrace(this.workspaceRoot, dir)) + .map(dirent => dirent.name) + .map(dir => this.getTrace(this.workspaceRoot, dir)) ); } } private getTrace(source: string, trace: string) { const uri = path.resolve(source, trace); - const children = fs.readdirSync(uri, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()).map(dirent => dirent.name); + const children = fs + .readdirSync(uri, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name); if (children.length > 0) { // return new Experiment(trace, vscode.TreeItemCollapsibleState.Collapsed, uri, children); return new Trace(trace, vscode.TreeItemCollapsibleState.None, uri, children); @@ -67,37 +71,41 @@ export class TracesProvider implements vscode.TreeDataProvider { export class Trace extends vscode.TreeItem { constructor( - public readonly name: string, - public readonly collapsibleState: vscode.TreeItemCollapsibleState, - public readonly uri: string, - public readonly children: string[] + public readonly name: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly uri: string, + public readonly children: string[] ) { super(name, collapsibleState); this.tooltip = `${this.name} ${this.uri}`; } - iconPath = { - light: path.resolve(rootPath, 'assets', 'resources', 'light', 'dependency.svg'), - dark: path.resolve(rootPath, 'assets', 'resources', 'dark', 'dependency.svg') - }; + iconPath = { + light: path.resolve(rootPath, 'assets', 'resources', 'light', 'dependency.svg'), + dark: path.resolve(rootPath, 'assets', 'resources', 'dark', 'dependency.svg') + }; } -export const traceHandler = (analysisTree: AnalysisProvider) => (context: vscode.ExtensionContext, trace: Trace): void => { - const panel = TraceViewerPanel.createOrShow(context.extensionUri, trace.name, undefined); - (async () => { - const traces = new Array(); - const t = await traceManager.openTrace(trace.uri, trace.name); - if (t) { traces.push(t); } - const experiment = await experimentManager.openExperiment(trace.name, traces); - if (experiment) { - panel.setExperiment(experiment); - const descriptors = await experimentManager.getAvailableOutputs(experiment.UUID); - if (descriptors && descriptors.length) { - analysisTree.refresh(descriptors); +export const traceHandler = + (analysisTree: AnalysisProvider) => + (context: vscode.ExtensionContext, trace: Trace): void => { + const panel = TraceViewerPanel.createOrShow(context.extensionUri, trace.name, undefined); + (async () => { + const traces = new Array(); + const t = await traceManager.openTrace(trace.uri, trace.name); + if (t) { + traces.push(t); } - } - })(); -}; + const experiment = await experimentManager.openExperiment(trace.name, traces); + if (experiment) { + panel.setExperiment(experiment); + const descriptors = await experimentManager.getAvailableOutputs(experiment.UUID); + if (descriptors && descriptors.length) { + analysisTree.refresh(descriptors); + } + } + })(); + }; export const openOverviewHandler = () => (): void => { TraceViewerPanel.showOverviewToCurrent(); @@ -134,133 +142,151 @@ const openDialog = async (): Promise => { return undefined; }; -export const fileHandler = (analysisTree: AnalysisProvider) => async (context: vscode.ExtensionContext, traceUri: vscode.Uri | undefined): Promise => { - // We need to resolve trace URI before starting the progress dialog because we rely on trace URI for dialog title - if (!traceUri) { - traceUri = await openDialog(); +export const fileHandler = + (analysisTree: AnalysisProvider) => + async (context: vscode.ExtensionContext, traceUri: vscode.Uri | undefined): Promise => { + // We need to resolve trace URI before starting the progress dialog because we rely on trace URI for dialog title if (!traceUri) { - return; - } - } - - const resolvedTraceURI: vscode.Uri = traceUri; - vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: getProgressBarTitle(resolvedTraceURI), - cancellable: true - }, async (progress, token) => { - if (token.isCancellationRequested) { - progress.report({ message: ProgressMessages.COMPLETE, increment: 100 }); - return; - } - - const uri: string = resolvedTraceURI.path; - if (!uri) { - traceLogger.addLogMessage('Cannot open trace: could not retrieve path from URI for trace ' + resolvedTraceURI, fileHandler.name); - return; - } - - const name = uri.substring(uri.lastIndexOf('/') + 1); - const panel = TraceViewerPanel.createOrShow(context.extensionUri, name, undefined); - - progress.report({ message: ProgressMessages.FINDING_TRACES, increment: 10}); - /* - * TODO: use backend service to find traces - */ - const tracesArray: string[] = []; - const fileStat = await vscode.workspace.fs.stat(resolvedTraceURI); - if (fileStat) { - if (fileStat.type === vscode.FileType.Directory) { - // Find recursively CTF traces - const foundTraces = await findTraces(uri); - foundTraces.forEach(trace => tracesArray.push(trace)); - } else { - // Open single trace file - tracesArray.push(uri); + traceUri = await openDialog(); + if (!traceUri) { + return; } } - if (tracesArray.length === 0) { - progress.report({ message: ProgressMessages.COMPLETE, increment: 100 }); - traceLogger.addLogMessage('No valid traces found in the selected directory: ' + resolvedTraceURI, fileHandler.name); - panel.dispose(); - return; - } - - progress.report({ message: ProgressMessages.OPENING_TRACES, increment: 20 }); - const traces = new Array(); - for (let i = 0; i < tracesArray.length; i++) { - const traceName = tracesArray[i].substring(tracesArray[i].lastIndexOf('/') + 1); - const trace = await traceManager.openTrace(tracesArray[i], traceName); - if (trace) { - traces.push(trace); - } else { - traceLogger.addLogMessage('Failed to open trace: ' + traceName, fileHandler.name); - traceLogger.addLogMessage('There may be an issue with the server or the trace is invalid.', fileHandler.name); + const resolvedTraceURI: vscode.Uri = traceUri; + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: getProgressBarTitle(resolvedTraceURI), + cancellable: true + }, + async (progress, token) => { + if (token.isCancellationRequested) { + progress.report({ message: ProgressMessages.COMPLETE, increment: 100 }); + return; + } + + const uri: string = resolvedTraceURI.path; + if (!uri) { + traceLogger.addLogMessage( + 'Cannot open trace: could not retrieve path from URI for trace ' + resolvedTraceURI, + fileHandler.name + ); + return; + } + + const name = uri.substring(uri.lastIndexOf('/') + 1); + const panel = TraceViewerPanel.createOrShow(context.extensionUri, name, undefined); + + progress.report({ message: ProgressMessages.FINDING_TRACES, increment: 10 }); + /* + * TODO: use backend service to find traces + */ + const tracesArray: string[] = []; + const fileStat = await vscode.workspace.fs.stat(resolvedTraceURI); + if (fileStat) { + if (fileStat.type === vscode.FileType.Directory) { + // Find recursively CTF traces + const foundTraces = await findTraces(uri); + foundTraces.forEach(trace => tracesArray.push(trace)); + } else { + // Open single trace file + tracesArray.push(uri); + } + } + + if (tracesArray.length === 0) { + progress.report({ message: ProgressMessages.COMPLETE, increment: 100 }); + traceLogger.addLogMessage( + 'No valid traces found in the selected directory: ' + resolvedTraceURI, + fileHandler.name + ); + panel.dispose(); + return; + } + + progress.report({ message: ProgressMessages.OPENING_TRACES, increment: 20 }); + const traces = new Array(); + for (let i = 0; i < tracesArray.length; i++) { + const traceName = tracesArray[i].substring(tracesArray[i].lastIndexOf('/') + 1); + const trace = await traceManager.openTrace(tracesArray[i], traceName); + if (trace) { + traces.push(trace); + } else { + traceLogger.addLogMessage('Failed to open trace: ' + traceName, fileHandler.name); + traceLogger.addLogMessage( + 'There may be an issue with the server or the trace is invalid.', + fileHandler.name + ); + } + } + + if (token.isCancellationRequested) { + rollbackTraces(traces, 20, progress); + progress.report({ message: ProgressMessages.COMPLETE, increment: 50 }); + panel.dispose(); + return; + } + + progress.report({ message: ProgressMessages.MERGING_TRACES, increment: 40 }); + if (traces === undefined || traces.length === 0) { + progress.report({ message: ProgressMessages.COMPLETE, increment: 30 }); + panel.dispose(); + return; + } + + const experiment = await experimentManager.openExperiment(name, traces); + if (experiment) { + panel.setExperiment(experiment); + const descriptors = await experimentManager.getAvailableOutputs(experiment.UUID); + if (descriptors && descriptors.length) { + analysisTree.refresh(descriptors); + } + } + + if (token.isCancellationRequested) { + if (experiment) { + experimentManager.deleteExperiment(experiment.UUID); + } + rollbackTraces(traces, 20, progress); + progress.report({ message: ProgressMessages.COMPLETE, increment: 10 }); + panel.dispose(); + return; + } + progress.report({ message: ProgressMessages.COMPLETE, increment: 30 }); } - } - - if (token.isCancellationRequested) { - rollbackTraces(traces, 20, progress); - progress.report({ message: ProgressMessages.COMPLETE, increment: 50 }); - panel.dispose(); - return; - } - - progress.report({ message: ProgressMessages.MERGING_TRACES, increment: 40 }); - if (traces === undefined || traces.length === 0) { - progress.report({ message: ProgressMessages.COMPLETE, increment: 30 }); - panel.dispose(); - return; - } - - const experiment = await experimentManager.openExperiment(name, traces); - if (experiment) { - panel.setExperiment(experiment); - const descriptors = await experimentManager.getAvailableOutputs(experiment.UUID); - if (descriptors && descriptors.length) { - analysisTree.refresh(descriptors); - } - } - - if (token.isCancellationRequested) { - if (experiment) { - experimentManager.deleteExperiment(experiment.UUID); - } - rollbackTraces(traces, 20, progress); - progress.report({ message: ProgressMessages.COMPLETE, increment: 10 }); - panel.dispose(); - return; - } - progress.report({ message: ProgressMessages.COMPLETE, increment: 30 }); - }); -}; + ); + }; -const rollbackTraces = async (traces: Array, progressIncrement: number, progress: vscode.Progress<{ - message: string | undefined; - increment: number | undefined; -}>) => { - progress.report({ message: ProgressMessages.ROLLING_BACK_TRACES, increment: progressIncrement}); +const rollbackTraces = async ( + traces: Array, + progressIncrement: number, + progress: vscode.Progress<{ + message: string | undefined; + increment: number | undefined; + }> +) => { + progress.report({ message: ProgressMessages.ROLLING_BACK_TRACES, increment: progressIncrement }); for (let i = 0; i < traces.length; i++) { await traceManager.deleteTrace(traces[i].UUID); } }; /* -* TODO: Make a proper trace finder, not just CTF -*/ + * TODO: Make a proper trace finder, not just CTF + */ const findTraces = async (directory: string): Promise => { const traces: string[] = []; const uri = vscode.Uri.file(directory); /** - * If single file selection then return single trace in traces, if directory then find - * recursively CTF traces in starting from root directory. - */ + * If single file selection then return single trace in traces, if directory then find + * recursively CTF traces in starting from root directory. + */ const ctf = await isCtf(directory); if (ctf) { traces.push(directory); } else { - // Look at the sub-directories of this + // Look at the sub-directories of this await vscode.workspace.fs.stat(uri); const childrenArr = await vscode.workspace.fs.readDirectory(uri); for (const child of childrenArr) { diff --git a/vscode-trace-extension/src/trace-viewer-panel/keyboard-shortcuts-panel.ts b/vscode-trace-extension/src/trace-viewer-panel/keyboard-shortcuts-panel.ts index fdf61173..94450270 100644 --- a/vscode-trace-extension/src/trace-viewer-panel/keyboard-shortcuts-panel.ts +++ b/vscode-trace-extension/src/trace-viewer-panel/keyboard-shortcuts-panel.ts @@ -5,43 +5,46 @@ import { getTraceServerUrl } from '../utils/tspClient'; * Manages the keyboard and mouse shortcuts panel */ export class KeyboardShortcutsPanel { - private static readonly viewType = 'trace.viewer.shortcuts'; - private static _panel: vscode.WebviewPanel | undefined = undefined; - - public static createOrShow(extensionUri: vscode.Uri, name: string): void { - const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + private static _panel: vscode.WebviewPanel | undefined = undefined; - if (this._panel) { - this._panel.reveal(column); - } else { - this._panel = vscode.window.createWebviewPanel(KeyboardShortcutsPanel.viewType, name, column || vscode.ViewColumn.One, { - // Enable javascript in the webview - enableScripts: true, - }); + public static createOrShow(extensionUri: vscode.Uri, name: string): void { + const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; - // Set the webview's initial html content - this._panel.webview.html = this._getHtmlForWebview(this._panel.webview, extensionUri); + if (this._panel) { + this._panel.reveal(column); + } else { + this._panel = vscode.window.createWebviewPanel( + KeyboardShortcutsPanel.viewType, + name, + column || vscode.ViewColumn.One, + { + // Enable javascript in the webview + enableScripts: true + } + ); - // Listen for when the panel is disposed - // This happens when the user closes the panel or when the panel is closed programmatically - this._panel.onDidDispose(() => { - this._panel = undefined; - }); + // Set the webview's initial html content + this._panel.webview.html = this._getHtmlForWebview(this._panel.webview, extensionUri); - } - } + // Listen for when the panel is disposed + // This happens when the user closes the panel or when the panel is closed programmatically + this._panel.onDidDispose(() => { + this._panel = undefined; + }); + } + } - /* eslint-disable max-len */ - private static _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri): string { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'pack', 'shortcutsPanel.js')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'pack')); + /* eslint-disable max-len */ + private static _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri): string { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'pack', 'shortcutsPanel.js')); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'pack')); - // Use a nonce to whitelist which scripts can be run - const nonce = getNonce(); + // Use a nonce to whitelist which scripts can be run + const nonce = getNonce(); - return ` + return ` @@ -68,7 +71,7 @@ export class KeyboardShortcutsPanel { `; - } + } } function getNonce() { diff --git a/vscode-trace-extension/src/trace-viewer-panel/trace-viewer-webview-panel.ts b/vscode-trace-extension/src/trace-viewer-panel/trace-viewer-webview-panel.ts index 85a01f16..912c372d 100644 --- a/vscode-trace-extension/src/trace-viewer-panel/trace-viewer-webview-panel.ts +++ b/vscode-trace-extension/src/trace-viewer-panel/trace-viewer-webview-panel.ts @@ -14,11 +14,11 @@ import { TimeRangeUpdatePayload } from 'traceviewer-base/lib/signals/time-range- import { convertSignalExperiment } from 'vscode-trace-common/lib/signals/vscode-signal-converter'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); interface QuickPickItem extends vscode.QuickPickItem { - id: string; + id: string; } // TODO: manage multiple panels (currently just a hack around, need to be fixed) @@ -27,411 +27,457 @@ interface QuickPickItem extends vscode.QuickPickItem { * Manages react webview panels */ export class TraceViewerPanel { - /** - * Track the currently panels. Only allow a single panel to exist at a time. - */ - public static activePanels = {} as { - [key: string]: TraceViewerPanel | undefined; - }; - - private static readonly viewType = 'react'; - private static currentPanel: TraceViewerPanel | undefined; - - private readonly _panel: vscode.WebviewPanel; - private readonly _extensionUri: vscode.Uri; - private readonly _statusService: TraceServerConnectionStatusService | undefined; - - private _disposables: vscode.Disposable[] = []; - private _experiment: Experiment | undefined = undefined; - private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => this.doHandleExperimentSelectedSignal(openedExperiment); - private _onRequestSelectionRangeChange = (payload: TimeRangeUpdatePayload): void => this.doHandleRequestSelectionRangeChange(payload); - - public static createOrShow(extensionUri: vscode.Uri, name: string, statusService: TraceServerConnectionStatusService | undefined): TraceViewerPanel { - - const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; - - // If we already have a panel, show it. - // Otherwise, create a new panel. - let openedPanel = TraceViewerPanel.activePanels[name]; - if (openedPanel) { - openedPanel._panel.reveal(column); - } else { - openedPanel = new TraceViewerPanel(extensionUri, column || vscode.ViewColumn.One, name, statusService); - TraceViewerPanel.activePanels[name] = openedPanel; - setStatusFromPanel(name); - } - TraceViewerPanel.currentPanel = openedPanel; - return openedPanel; - } - - public static disposePanel(extensionUri: vscode.Uri, name: string): void { - // If we already have a panel, show it. - // Otherwise, create a new panel. - const openedPanel = TraceViewerPanel.activePanels[name]; - if (openedPanel) { - openedPanel._panel.dispose(); - TraceViewerPanel.activePanels[name] = undefined; - TraceViewerPanel.currentPanel = undefined; - } - } - - public static addOutputToCurrent(descriptor: OutputDescriptor): void { - TraceViewerPanel.currentPanel?.addOutput(descriptor); - } - - public static showOverviewToCurrent(): void { - TraceViewerPanel.currentPanel?.showOverview(); - } - - public static resetZoomOnCurrent(): void { - TraceViewerPanel.currentPanel?.resetZoom(); - } - - public static undoRedoOnCurrent(undo: boolean): void { - TraceViewerPanel.currentPanel?.undoRedo(undo); - } - - public static zoomOnCurrent(hasZoomedIn: boolean): void { - TraceViewerPanel.currentPanel?.updateZoom(hasZoomedIn); - } - - public static showMarkerSetsOnCurrent(): void { - TraceViewerPanel.currentPanel?.showMarkerSets(); - } - - public static showMarkersFilterOnCurrent(): void { - TraceViewerPanel.currentPanel?.showMarkersFilter(); - } - - public static getCurrentExperiment(): Experiment | undefined { - return TraceViewerPanel.currentPanel?._experiment; - } - - private static async saveTraceCsv(csvData: string, defaultFileName: string) { - const saveDialogOptions = { - defaultUri: vscode.workspace.workspaceFolders - ? vscode.Uri.file(vscode.workspace.workspaceFolders[0].uri.path + '/' + defaultFileName) - : undefined, - saveLabel: 'Save as CSV', - filters: { - 'CSV Files': ['csv'] - } - }; - const uri = await vscode.window.showSaveDialog(saveDialogOptions); - if (uri) { - fs.writeFile(uri.fsPath, csvData, err => { - if (err) { - vscode.window.showErrorMessage(`Failed to save CSV: ${err.message}`); - } else { - vscode.window.showInformationMessage('CSV saved successfully'); - } - }); - } - } - - public static updateTraceServerUrl(newUrl: string): void { - Object.values(TraceViewerPanel.activePanels) - .forEach(trace => trace?._panel.webview.postMessage({ - command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, data: newUrl - })); - } - - private constructor(extensionUri: vscode.Uri, column: vscode.ViewColumn, name: string, statusService: TraceServerConnectionStatusService | undefined) { - this._extensionUri = extensionUri; - this._statusService = statusService; - - // Create and show a new webview panel - this._panel = vscode.window.createWebviewPanel(TraceViewerPanel.viewType, name, column, { - // Enable javascript in the webview - enableScripts: true, - - // Do not destroy the content when hidden - retainContextWhenHidden: true, - enableCommandUris: true, - - // And restrict the webview to only loading content from our extension's `media` directory. - localResourceRoots: [ - vscode.Uri.joinPath(this._extensionUri, 'pack'), - vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons'), - ] - }); - - // Set the webview's initial html content - this._panel.webview.html = this._getHtmlForWebview(); - traceExtensionWebviewManager.fireWebviewPanelCreated(this._panel); - - // Listen for when the panel is disposed - // This happens when the user closes the panel or when the panel is closed programmatically - this._panel.onDidDispose(() => { - const isActivePanel = TraceViewerPanel.activePanels[name] === TraceViewerPanel.currentPanel; - const traceUUID = TraceViewerPanel.activePanels[name]?._experiment?.UUID; - this.dispose(); - TraceViewerPanel.activePanels[name] = undefined; - if (traceUUID) { - signalManager().fireCloseTraceViewerTabSignal(traceUUID); - } - if (isActivePanel) { - signalManager().fireExperimentSelectedSignal(undefined); - } - return this._disposables; - }); - - this._panel.onDidChangeViewState(e => { - if (e.webviewPanel.active) { - TraceViewerPanel.currentPanel = this; - setStatusFromPanel(name); - if (this._experiment) { - signalManager().fireTraceViewerTabActivatedSignal(this._experiment); - signalManager().fireExperimentSelectedSignal(this._experiment); - } - } - }); - - vscode.window.onDidChangeActiveColorTheme(e => { - const wrapper = e.kind === 1 ? 'light' : 'dark'; - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.SET_THEME, data: wrapper }); - }); - - // Handle messages from the webview - this._panel.webview.onDidReceiveMessage(message => { - switch (message.command) { - case VSCODE_MESSAGES.ALERT: - vscode.window.showErrorMessage(message.text); - return; - case VSCODE_MESSAGES.NEW_STATUS: - handleStatusMessage(name, message.data); - return; - case VSCODE_MESSAGES.REMOVE_STATUS: - handleRemoveMessage(name, message.data); - return; - case VSCODE_MESSAGES.WEBVIEW_READY: - // Post the tspTypescriptClient - if (this._experiment) { - const wrapper: string = JSONBig.stringify(this._experiment); - this._panel.webview.postMessage({command: VSCODE_MESSAGES.SET_TSP_CLIENT, data: getTspClientUrl(), experiment: wrapper}); - } else { - this._panel.webview.postMessage({command: VSCODE_MESSAGES.SET_TSP_CLIENT, data: getTspClientUrl()}); - } - this.loadTheme(); - return; - case VSCODE_MESSAGES.UPDATE_PROPERTIES: - vscode.commands.executeCommand('messages.post.propertiespanel', 'receivedProperties', message.data); - return; - case VSCODE_MESSAGES.SAVE_AS_CSV: - if (message.payload.data && typeof message.payload.data === 'string') { - TraceViewerPanel.saveTraceCsv(message.payload.data, ((this._experiment !== undefined) ? this._experiment.name : 'trace')+'.csv'); - } - return; - case VSCODE_MESSAGES.CONNECTION_STATUS: - if (message.data?.status && this._statusService) { - const status: boolean = JSON.parse(message.data.status); - this._statusService.render(status); - } - return; - case VSCODE_MESSAGES.SHOW_MARKER_CATEGORIES: - if (message.data?.wrapper) { - const markerCategories = new Map(JSON.parse(message.data.wrapper)); - TraceViewerPanel.currentPanel?.renderMarkersFilter(markerCategories); - } - return; - case VSCODE_MESSAGES.SEND_MARKER_SETS: - if (message.data?.wrapper) { - const markerSetsMap = new Map(JSON.parse(message.data.wrapper)); - TraceViewerPanel.currentPanel?.renderMarkerSets(markerSetsMap); - } - return; - case VSCODE_MESSAGES.MARKER_SETS_CONTEXT: - if (message.data?.status) { - const status: boolean = JSON.parse(message.data.status); - vscode.commands.executeCommand('setContext', 'traceViewer.markerSetsPresent', status); - } - return; - case VSCODE_MESSAGES.MARKER_CATEGORIES_CONTEXT: - if (message.data?.status) { - const status: boolean = JSON.parse(message.data.status); - vscode.commands.executeCommand('setContext', 'traceViewer.markerCategoriesPresent', status); - } - return; - case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: - signalManager().fireViewRangeUpdated(JSONBig.parse(message.data)); - break; - case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: - signalManager().fireSelectionRangeUpdated(JSONBig.parse(message.data)); - break; - case VSCODE_MESSAGES.EXPERIMENT_UPDATED: - const experiment = convertSignalExperiment(JSONBig.parse(message.data)); - signalManager().fireExperimentUpdatedSignal(experiment); - break; - } - }, undefined, this._disposables); - signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().on(Signals.REQUEST_SELECTION_RANGE_CHANGE, this._onRequestSelectionRangeChange); - } - - public doRefactor(): void { - // Send a message to the webview webview. - // You can send any JSON serializable data. - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.REFACTOR }); - } - - public dispose(): void { - // ReactPanel.currentPanel = undefined; - - // Clean up our resources - this._panel.dispose(); - - while (this._disposables.length) { - const x = this._disposables.pop(); - if (x) { - x.dispose(); - } - } - signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().off(Signals.REQUEST_SELECTION_RANGE_CHANGE, this._onRequestSelectionRangeChange); - } - - protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - if (this._experiment && experiment && this._experiment.UUID === experiment.UUID) { - this._panel.reveal(); - const wrapper: string = JSONBig.stringify(experiment); - this._panel.webview.postMessage({command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data: wrapper}); - } - } - - protected doHandleExperimentUpdatedSignal(experiment: Experiment): void { - signalManager().fireExperimentUpdatedSignal(experiment); - } - - protected doHandleRequestSelectionRangeChange(payload: TimeRangeUpdatePayload): void { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE, data: JSONBig.stringify(payload)}); - } - setExperiment(experiment: Experiment): void { - this._experiment = experiment; - const wrapper: string = JSONBig.stringify(experiment); - this._panel.webview.postMessage({command: VSCODE_MESSAGES.SET_EXPERIMENT, data: wrapper}); - signalManager().fireExperimentOpenedSignal(experiment); - signalManager().fireTraceViewerTabActivatedSignal(experiment); - } - - addOutput(descriptor: OutputDescriptor): void { - const wrapper: string = JSONBig.stringify(descriptor); - this._panel.webview.postMessage({command: VSCODE_MESSAGES.ADD_OUTPUT, data: wrapper}); - } - - showOverview(): void { - this._panel.webview.postMessage({command: VSCODE_MESSAGES.OPEN_OVERVIEW}); - } - - resetZoom(): void { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.RESET_ZOOM }); - } - - undoRedo(undo: boolean): void { - if (undo) { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UNDO }); - } else { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.REDO }); - } - } - - updateZoom(hasZoomedIn: boolean): void { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UPDATE_ZOOM, data: hasZoomedIn}); - } - - showMarkersFilter(): void { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.GET_MARKER_CATEGORIES}); - } - - showMarkerSets(): void { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.GET_MARKER_SETS}); - } - - renderMarkersFilter(markerCategories: Map): void { - const items: vscode.QuickPickItem[] = []; - - markerCategories.forEach((categoryInfo, categoryName) => { - items.push({ - label: categoryName, - picked: categoryInfo.toggleInd - }); - }); - - vscode.window.showQuickPick( - items, - { - title: 'Select Markers Filter', - placeHolder: 'Filter', - canPickMany: true - } - - ).then(selection => { - // the user canceled the selection - if (!selection) { - return; - } - - const selectedCategories: string[] = []; - for (const category of selection) { - selectedCategories.push(category.label); - } - const wrapper = JSON.stringify(selectedCategories); - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UPDATE_MARKER_CATEGORY_STATE, data: wrapper }); - }); - - } - - renderMarkerSets(markerSetsMap: Map): void { - const items: QuickPickItem[] = []; - - markerSetsMap.forEach((value: { marker: MarkerSet, enabled: boolean }, key: string) => { - const item: QuickPickItem = { - id: key, - label: value.marker.name, - picked: value.enabled - }; - if (value.enabled) { - item.detail = 'Selected'; - } - items.push(item); - }); - - vscode.window.showQuickPick( - items, - { - title: 'Select Marker Set', - placeHolder: 'Filter', - } - - ).then(selection => { - // the user canceled the selection - if (!selection) { - return; - } - - if (markerSetsMap.has(selection.id)) { - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UPDATE_MARKER_SET_STATE, data: selection.id }); - } - }); - } - - loadTheme(): void { - const wrapper = vscode.window.activeColorTheme.kind === 1 ? 'light' : 'dark'; - this._panel.webview.postMessage({ command: VSCODE_MESSAGES.SET_THEME, data: wrapper }); - } - - private _getHtmlForWebview() { - const webview = this._panel.webview; - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - const nonce = getNonce(); - - try { - return this._getReactHtmlForWebview(); - } catch (e) { - return ` + /** + * Track the currently panels. Only allow a single panel to exist at a time. + */ + public static activePanels = {} as { + [key: string]: TraceViewerPanel | undefined; + }; + + private static readonly viewType = 'react'; + private static currentPanel: TraceViewerPanel | undefined; + + private readonly _panel: vscode.WebviewPanel; + private readonly _extensionUri: vscode.Uri; + private readonly _statusService: TraceServerConnectionStatusService | undefined; + + private _disposables: vscode.Disposable[] = []; + private _experiment: Experiment | undefined = undefined; + private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => + this.doHandleExperimentSelectedSignal(openedExperiment); + private _onRequestSelectionRangeChange = (payload: TimeRangeUpdatePayload): void => + this.doHandleRequestSelectionRangeChange(payload); + + public static createOrShow( + extensionUri: vscode.Uri, + name: string, + statusService: TraceServerConnectionStatusService | undefined + ): TraceViewerPanel { + const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + + // If we already have a panel, show it. + // Otherwise, create a new panel. + let openedPanel = TraceViewerPanel.activePanels[name]; + if (openedPanel) { + openedPanel._panel.reveal(column); + } else { + openedPanel = new TraceViewerPanel(extensionUri, column || vscode.ViewColumn.One, name, statusService); + TraceViewerPanel.activePanels[name] = openedPanel; + setStatusFromPanel(name); + } + TraceViewerPanel.currentPanel = openedPanel; + return openedPanel; + } + + public static disposePanel(extensionUri: vscode.Uri, name: string): void { + // If we already have a panel, show it. + // Otherwise, create a new panel. + const openedPanel = TraceViewerPanel.activePanels[name]; + if (openedPanel) { + openedPanel._panel.dispose(); + TraceViewerPanel.activePanels[name] = undefined; + TraceViewerPanel.currentPanel = undefined; + } + } + + public static addOutputToCurrent(descriptor: OutputDescriptor): void { + TraceViewerPanel.currentPanel?.addOutput(descriptor); + } + + public static showOverviewToCurrent(): void { + TraceViewerPanel.currentPanel?.showOverview(); + } + + public static resetZoomOnCurrent(): void { + TraceViewerPanel.currentPanel?.resetZoom(); + } + + public static undoRedoOnCurrent(undo: boolean): void { + TraceViewerPanel.currentPanel?.undoRedo(undo); + } + + public static zoomOnCurrent(hasZoomedIn: boolean): void { + TraceViewerPanel.currentPanel?.updateZoom(hasZoomedIn); + } + + public static showMarkerSetsOnCurrent(): void { + TraceViewerPanel.currentPanel?.showMarkerSets(); + } + + public static showMarkersFilterOnCurrent(): void { + TraceViewerPanel.currentPanel?.showMarkersFilter(); + } + + public static getCurrentExperiment(): Experiment | undefined { + return TraceViewerPanel.currentPanel?._experiment; + } + + private static async saveTraceCsv(csvData: string, defaultFileName: string) { + const saveDialogOptions = { + defaultUri: vscode.workspace.workspaceFolders + ? vscode.Uri.file(vscode.workspace.workspaceFolders[0].uri.path + '/' + defaultFileName) + : undefined, + saveLabel: 'Save as CSV', + filters: { + 'CSV Files': ['csv'] + } + }; + const uri = await vscode.window.showSaveDialog(saveDialogOptions); + if (uri) { + fs.writeFile(uri.fsPath, csvData, err => { + if (err) { + vscode.window.showErrorMessage(`Failed to save CSV: ${err.message}`); + } else { + vscode.window.showInformationMessage('CSV saved successfully'); + } + }); + } + } + + public static updateTraceServerUrl(newUrl: string): void { + Object.values(TraceViewerPanel.activePanels).forEach( + trace => + trace?._panel.webview.postMessage({ + command: VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED, + data: newUrl + }) + ); + } + + private constructor( + extensionUri: vscode.Uri, + column: vscode.ViewColumn, + name: string, + statusService: TraceServerConnectionStatusService | undefined + ) { + this._extensionUri = extensionUri; + this._statusService = statusService; + + // Create and show a new webview panel + this._panel = vscode.window.createWebviewPanel(TraceViewerPanel.viewType, name, column, { + // Enable javascript in the webview + enableScripts: true, + + // Do not destroy the content when hidden + retainContextWhenHidden: true, + enableCommandUris: true, + + // And restrict the webview to only loading content from our extension's `media` directory. + localResourceRoots: [ + vscode.Uri.joinPath(this._extensionUri, 'pack'), + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons') + ] + }); + + // Set the webview's initial html content + this._panel.webview.html = this._getHtmlForWebview(); + traceExtensionWebviewManager.fireWebviewPanelCreated(this._panel); + + // Listen for when the panel is disposed + // This happens when the user closes the panel or when the panel is closed programmatically + this._panel.onDidDispose(() => { + const isActivePanel = TraceViewerPanel.activePanels[name] === TraceViewerPanel.currentPanel; + const traceUUID = TraceViewerPanel.activePanels[name]?._experiment?.UUID; + this.dispose(); + TraceViewerPanel.activePanels[name] = undefined; + if (traceUUID) { + signalManager().fireCloseTraceViewerTabSignal(traceUUID); + } + if (isActivePanel) { + signalManager().fireExperimentSelectedSignal(undefined); + } + return this._disposables; + }); + + this._panel.onDidChangeViewState(e => { + if (e.webviewPanel.active) { + TraceViewerPanel.currentPanel = this; + setStatusFromPanel(name); + if (this._experiment) { + signalManager().fireTraceViewerTabActivatedSignal(this._experiment); + signalManager().fireExperimentSelectedSignal(this._experiment); + } + } + }); + + vscode.window.onDidChangeActiveColorTheme(e => { + const wrapper = e.kind === 1 ? 'light' : 'dark'; + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.SET_THEME, data: wrapper }); + }); + + // Handle messages from the webview + this._panel.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case VSCODE_MESSAGES.ALERT: + vscode.window.showErrorMessage(message.text); + return; + case VSCODE_MESSAGES.NEW_STATUS: + handleStatusMessage(name, message.data); + return; + case VSCODE_MESSAGES.REMOVE_STATUS: + handleRemoveMessage(name, message.data); + return; + case VSCODE_MESSAGES.WEBVIEW_READY: + // Post the tspTypescriptClient + if (this._experiment) { + const wrapper: string = JSONBig.stringify(this._experiment); + this._panel.webview.postMessage({ + command: VSCODE_MESSAGES.SET_TSP_CLIENT, + data: getTspClientUrl(), + experiment: wrapper + }); + } else { + this._panel.webview.postMessage({ + command: VSCODE_MESSAGES.SET_TSP_CLIENT, + data: getTspClientUrl() + }); + } + this.loadTheme(); + return; + case VSCODE_MESSAGES.UPDATE_PROPERTIES: + vscode.commands.executeCommand( + 'messages.post.propertiespanel', + 'receivedProperties', + message.data + ); + return; + case VSCODE_MESSAGES.SAVE_AS_CSV: + if (message.payload.data && typeof message.payload.data === 'string') { + TraceViewerPanel.saveTraceCsv( + message.payload.data, + (this._experiment !== undefined ? this._experiment.name : 'trace') + '.csv' + ); + } + return; + case VSCODE_MESSAGES.CONNECTION_STATUS: + if (message.data?.status && this._statusService) { + const status: boolean = JSON.parse(message.data.status); + this._statusService.render(status); + } + return; + case VSCODE_MESSAGES.SHOW_MARKER_CATEGORIES: + if (message.data?.wrapper) { + const markerCategories = new Map( + JSON.parse(message.data.wrapper) + ); + TraceViewerPanel.currentPanel?.renderMarkersFilter(markerCategories); + } + return; + case VSCODE_MESSAGES.SEND_MARKER_SETS: + if (message.data?.wrapper) { + const markerSetsMap = new Map( + JSON.parse(message.data.wrapper) + ); + TraceViewerPanel.currentPanel?.renderMarkerSets(markerSetsMap); + } + return; + case VSCODE_MESSAGES.MARKER_SETS_CONTEXT: + if (message.data?.status) { + const status: boolean = JSON.parse(message.data.status); + vscode.commands.executeCommand('setContext', 'traceViewer.markerSetsPresent', status); + } + return; + case VSCODE_MESSAGES.MARKER_CATEGORIES_CONTEXT: + if (message.data?.status) { + const status: boolean = JSON.parse(message.data.status); + vscode.commands.executeCommand('setContext', 'traceViewer.markerCategoriesPresent', status); + } + return; + case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: + signalManager().fireViewRangeUpdated(JSONBig.parse(message.data)); + break; + case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: + signalManager().fireSelectionRangeUpdated(JSONBig.parse(message.data)); + break; + case VSCODE_MESSAGES.EXPERIMENT_UPDATED: + const experiment = convertSignalExperiment(JSONBig.parse(message.data)); + signalManager().fireExperimentUpdatedSignal(experiment); + break; + } + }, + undefined, + this._disposables + ); + signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().on(Signals.REQUEST_SELECTION_RANGE_CHANGE, this._onRequestSelectionRangeChange); + } + + public doRefactor(): void { + // Send a message to the webview webview. + // You can send any JSON serializable data. + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.REFACTOR }); + } + + public dispose(): void { + // ReactPanel.currentPanel = undefined; + + // Clean up our resources + this._panel.dispose(); + + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().off(Signals.REQUEST_SELECTION_RANGE_CHANGE, this._onRequestSelectionRangeChange); + } + + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + if (this._experiment && experiment && this._experiment.UUID === experiment.UUID) { + this._panel.reveal(); + const wrapper: string = JSONBig.stringify(experiment); + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.EXPERIMENT_SELECTED, data: wrapper }); + } + } + + protected doHandleExperimentUpdatedSignal(experiment: Experiment): void { + signalManager().fireExperimentUpdatedSignal(experiment); + } + + protected doHandleRequestSelectionRangeChange(payload: TimeRangeUpdatePayload): void { + this._panel.webview.postMessage({ + command: VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE, + data: JSONBig.stringify(payload) + }); + } + setExperiment(experiment: Experiment): void { + this._experiment = experiment; + const wrapper: string = JSONBig.stringify(experiment); + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.SET_EXPERIMENT, data: wrapper }); + signalManager().fireExperimentOpenedSignal(experiment); + signalManager().fireTraceViewerTabActivatedSignal(experiment); + } + + addOutput(descriptor: OutputDescriptor): void { + const wrapper: string = JSONBig.stringify(descriptor); + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.ADD_OUTPUT, data: wrapper }); + } + + showOverview(): void { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.OPEN_OVERVIEW }); + } + + resetZoom(): void { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.RESET_ZOOM }); + } + + undoRedo(undo: boolean): void { + if (undo) { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UNDO }); + } else { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.REDO }); + } + } + + updateZoom(hasZoomedIn: boolean): void { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.UPDATE_ZOOM, data: hasZoomedIn }); + } + + showMarkersFilter(): void { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.GET_MARKER_CATEGORIES }); + } + + showMarkerSets(): void { + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.GET_MARKER_SETS }); + } + + renderMarkersFilter( + markerCategories: Map< + string, + { + categoryCount: number; + toggleInd: boolean; + } + > + ): void { + const items: vscode.QuickPickItem[] = []; + + markerCategories.forEach((categoryInfo, categoryName) => { + items.push({ + label: categoryName, + picked: categoryInfo.toggleInd + }); + }); + + vscode.window + .showQuickPick(items, { + title: 'Select Markers Filter', + placeHolder: 'Filter', + canPickMany: true + }) + .then(selection => { + // the user canceled the selection + if (!selection) { + return; + } + + const selectedCategories: string[] = []; + for (const category of selection) { + selectedCategories.push(category.label); + } + const wrapper = JSON.stringify(selectedCategories); + this._panel.webview.postMessage({ + command: VSCODE_MESSAGES.UPDATE_MARKER_CATEGORY_STATE, + data: wrapper + }); + }); + } + + renderMarkerSets(markerSetsMap: Map): void { + const items: QuickPickItem[] = []; + + markerSetsMap.forEach((value: { marker: MarkerSet; enabled: boolean }, key: string) => { + const item: QuickPickItem = { + id: key, + label: value.marker.name, + picked: value.enabled + }; + if (value.enabled) { + item.detail = 'Selected'; + } + items.push(item); + }); + + vscode.window + .showQuickPick(items, { + title: 'Select Marker Set', + placeHolder: 'Filter' + }) + .then(selection => { + // the user canceled the selection + if (!selection) { + return; + } + + if (markerSetsMap.has(selection.id)) { + this._panel.webview.postMessage({ + command: VSCODE_MESSAGES.UPDATE_MARKER_SET_STATE, + data: selection.id + }); + } + }); + } + + loadTheme(): void { + const wrapper = vscode.window.activeColorTheme.kind === 1 ? 'light' : 'dark'; + this._panel.webview.postMessage({ command: VSCODE_MESSAGES.SET_THEME, data: wrapper }); + } + + private _getHtmlForWebview() { + const webview = this._panel.webview; + const codiconsUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css') + ); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + const nonce = getNonce(); + + try { + return this._getReactHtmlForWebview(); + } catch (e) { + return ` @@ -454,22 +500,23 @@ export class TraceViewerPanel {
${'Error initializing trace viewer'}
`; - } - } - - /* eslint-disable max-len */ - private _getReactHtmlForWebview(): string { + } + } - // Fetching codicons styles - const webview = this._panel.webview; - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'trace_panel.js')); - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css')); - const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); + /* eslint-disable max-len */ + private _getReactHtmlForWebview(): string { + // Fetching codicons styles + const webview = this._panel.webview; + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack', 'trace_panel.js')); + const codiconsUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'lib', 'codicons', 'codicon.css') + ); + const packUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'pack')); - // Use a nonce to whitelist which scripts can be run - const nonce = getNonce(); + // Use a nonce to whitelist which scripts can be run + const nonce = getNonce(); - return ` + return ` @@ -497,7 +544,7 @@ export class TraceViewerPanel { `; - } + } } function getNonce() { diff --git a/vscode-trace-extension/src/utils/trace-extension-webview-manager.ts b/vscode-trace-extension/src/utils/trace-extension-webview-manager.ts index 3a8374f6..53e27945 100644 --- a/vscode-trace-extension/src/utils/trace-extension-webview-manager.ts +++ b/vscode-trace-extension/src/utils/trace-extension-webview-manager.ts @@ -40,7 +40,7 @@ export class TraceExtensionWebviewManager { } } - onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => unknown): void { + onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => unknown): void { if (!this.isDisposed()) { this.webviewPanelCreated.event(listener); } @@ -68,11 +68,13 @@ export class TraceExtensionWebviewManager { } private removeWebview(_webview: vscode.WebviewView): void { - this.webviews.filter(webview => webview === _webview).forEach(webview => { - const index = this.webviews.indexOf(webview); - if (index !== -1) { - this.webviews.splice(index, 1); - } - }); + this.webviews + .filter(webview => webview === _webview) + .forEach(webview => { + const index = this.webviews.indexOf(webview); + if (index !== -1) { + this.webviews.splice(index, 1); + } + }); } } diff --git a/vscode-trace-extension/src/utils/trace-server-status.ts b/vscode-trace-extension/src/utils/trace-server-status.ts index 5de4f8fa..ec79c999 100644 --- a/vscode-trace-extension/src/utils/trace-server-status.ts +++ b/vscode-trace-extension/src/utils/trace-server-status.ts @@ -1,7 +1,6 @@ import { StatusBarItem, ThemeColor } from 'vscode'; export class TraceServerConnectionStatusService { - private statusBarItem: StatusBarItem; public constructor(statusBarItem: StatusBarItem) { diff --git a/vscode-trace-extension/src/utils/tspClient.ts b/vscode-trace-extension/src/utils/tspClient.ts index 1a2e4797..71d67ab4 100644 --- a/vscode-trace-extension/src/utils/tspClient.ts +++ b/vscode-trace-extension/src/utils/tspClient.ts @@ -14,7 +14,7 @@ export function getTraceServerUrl(): string { if (!traceServerUrl) { return 'http://localhost:8080/'; } - return traceServerUrl.endsWith('/') ? traceServerUrl : traceServerUrl + '/' ; + return traceServerUrl.endsWith('/') ? traceServerUrl : traceServerUrl + '/'; } export function getTspClientUrl(): string { diff --git a/vscode-trace-webviews/src/style/react-contextify.css b/vscode-trace-webviews/src/style/react-contextify.css index 05ca7970..9ea9bdbf 100644 --- a/vscode-trace-webviews/src/style/react-contextify.css +++ b/vscode-trace-webviews/src/style/react-contextify.css @@ -2,9 +2,9 @@ position: fixed; opacity: 0; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; background-color: #ffffff; box-sizing: border-box; box-shadow: 0px 10px 30px -5px rgba(0, 0, 0, 0.3); @@ -12,220 +12,227 @@ padding: 6px 0; min-width: 200px; z-index: 100; - } - .react-contexify__submenu--is-open, .react-contexify__submenu--is-open > .react-contexify__item__content { +} +.react-contexify__submenu--is-open, +.react-contexify__submenu--is-open > .react-contexify__item__content { color: white; background-color: #4393e6; - } - .react-contexify__submenu--is-open > .react-contexify__submenu { +} +.react-contexify__submenu--is-open > .react-contexify__submenu { pointer-events: initial; opacity: 1; - } - .react-contexify .react-contexify__submenu { +} +.react-contexify .react-contexify__submenu { position: absolute; /* negate padding */ top: -6px; pointer-events: none; transition: opacity 0.275s; - } - .react-contexify__submenu-arrow { +} +.react-contexify__submenu-arrow { margin-left: auto; font-size: 12px; - } - .react-contexify__separator { +} +.react-contexify__separator { width: 100%; height: 1px; cursor: default; margin: 4px 0; background-color: rgba(0, 0, 0, 0.2); - } - .react-contexify__will-leave--disabled { +} +.react-contexify__will-leave--disabled { pointer-events: none; - } - .react-contexify__item { +} +.react-contexify__item { cursor: pointer; position: relative; - } - .react-contexify__item:focus { +} +.react-contexify__item:focus { outline: 0; - } - .react-contexify__item:not(.react-contexify__item--disabled):hover > .react-contexify__item__content, .react-contexify__item:not(.react-contexify__item--disabled):focus > .react-contexify__item__content { +} +.react-contexify__item:not(.react-contexify__item--disabled):hover > .react-contexify__item__content, +.react-contexify__item:not(.react-contexify__item--disabled):focus > .react-contexify__item__content { color: white; background-color: #4393e6; - } - .react-contexify__item:not(.react-contexify__item--disabled):hover > .react-contexify__submenu { +} +.react-contexify__item:not(.react-contexify__item--disabled):hover > .react-contexify__submenu { pointer-events: initial; opacity: 1; - } - .react-contexify__item--disabled { +} +.react-contexify__item--disabled { cursor: default; opacity: 0.5; - } - .react-contexify__item__content { +} +.react-contexify__item__content { padding: 6px 12px; display: -ms-flexbox; display: flex; -ms-flex-align: center; - align-items: center; + align-items: center; white-space: nowrap; color: #333; position: relative; - } - - .react-contexify__theme--dark { +} + +.react-contexify__theme--dark { background-color: rgba(40, 40, 40, 0.98); - } - .react-contexify__theme--dark .react-contexify__submenu { +} +.react-contexify__theme--dark .react-contexify__submenu { background-color: rgba(40, 40, 40, 0.98); - } - .react-contexify__theme--dark .react-contexify__separator { +} +.react-contexify__theme--dark .react-contexify__separator { background-color: #eee; - } - .react-contexify__theme--dark .react-contexify__item__content { +} +.react-contexify__theme--dark .react-contexify__item__content { color: #ffffff; - } - - .react-contexify__theme--light .react-contexify__separator { +} + +.react-contexify__theme--light .react-contexify__separator { background-color: #eee; - } - .react-contexify__theme--light .react-contexify__submenu--is-open, - .react-contexify__theme--light .react-contexify__submenu--is-open > .react-contexify__item__content { +} +.react-contexify__theme--light .react-contexify__submenu--is-open, +.react-contexify__theme--light .react-contexify__submenu--is-open > .react-contexify__item__content { color: #4393e6; background-color: #e0eefd; - } - .react-contexify__theme--light .react-contexify__item:not(.react-contexify__item--disabled):hover > .react-contexify__item__content, .react-contexify__theme--light .react-contexify__item:not(.react-contexify__item--disabled):focus > .react-contexify__item__content { +} +.react-contexify__theme--light + .react-contexify__item:not(.react-contexify__item--disabled):hover + > .react-contexify__item__content, +.react-contexify__theme--light + .react-contexify__item:not(.react-contexify__item--disabled):focus + > .react-contexify__item__content { color: #4393e6; background-color: #e0eefd; - } - .react-contexify__theme--light .react-contexify__item__content { +} +.react-contexify__theme--light .react-contexify__item__content { color: #666; - } - - @keyframes react-contexify__scaleIn { +} + +@keyframes react-contexify__scaleIn { from { - opacity: 0; - transform: scale3d(0.3, 0.3, 0.3); + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); } to { - opacity: 1; + opacity: 1; } - } - @keyframes react-contexify__scaleOut { +} +@keyframes react-contexify__scaleOut { from { - opacity: 1; + opacity: 1; } to { - opacity: 0; - transform: scale3d(0.3, 0.3, 0.3); + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); } - } - .react-contexify__will-enter--scale { +} +.react-contexify__will-enter--scale { transform-origin: top left; animation: react-contexify__scaleIn 0.3s; - } - - .react-contexify__will-leave--scale { +} + +.react-contexify__will-leave--scale { transform-origin: top left; animation: react-contexify__scaleOut 0.3s; - } - - @keyframes react-contexify__fadeIn { +} + +@keyframes react-contexify__fadeIn { from { - opacity: 0; - transform: translateY(10px); + opacity: 0; + transform: translateY(10px); } to { - opacity: 1; - transform: translateY(0); + opacity: 1; + transform: translateY(0); } - } - @keyframes react-contexify__fadeOut { +} +@keyframes react-contexify__fadeOut { from { - opacity: 1; - transform: translateY(0); + opacity: 1; + transform: translateY(0); } to { - opacity: 0; - transform: translateY(10px); + opacity: 0; + transform: translateY(10px); } - } - .react-contexify__will-enter--fade { +} +.react-contexify__will-enter--fade { animation: react-contexify__fadeIn 0.3s ease; - } - - .react-contexify__will-leave--fade { +} + +.react-contexify__will-leave--fade { animation: react-contexify__fadeOut 0.3s ease; - } - - @keyframes react-contexify__flipInX { +} + +@keyframes react-contexify__flipInX { from { - transform: perspective(800px) rotate3d(1, 0, 0, 45deg); + transform: perspective(800px) rotate3d(1, 0, 0, 45deg); } to { - transform: perspective(800px); + transform: perspective(800px); } - } - @keyframes react-contexify__flipOutX { +} +@keyframes react-contexify__flipOutX { from { - transform: perspective(800px); + transform: perspective(800px); } to { - transform: perspective(800px) rotate3d(1, 0, 0, 45deg); - opacity: 0; + transform: perspective(800px) rotate3d(1, 0, 0, 45deg); + opacity: 0; } - } - .react-contexify__will-enter--flip { +} +.react-contexify__will-enter--flip { -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; + backface-visibility: visible !important; transform-origin: top center; animation: react-contexify__flipInX 0.3s; - } - - .react-contexify__will-leave--flip { +} + +.react-contexify__will-leave--flip { transform-origin: top center; animation: react-contexify__flipOutX 0.3s; -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - } - - @keyframes swing-in-top-fwd { + backface-visibility: visible !important; +} + +@keyframes swing-in-top-fwd { 0% { - transform: rotateX(-100deg); - transform-origin: top; - opacity: 0; + transform: rotateX(-100deg); + transform-origin: top; + opacity: 0; } 100% { - transform: rotateX(0deg); - transform-origin: top; - opacity: 1; + transform: rotateX(0deg); + transform-origin: top; + opacity: 1; } - } - @keyframes react-contexify__slideIn { +} +@keyframes react-contexify__slideIn { from { - opacity: 0; - transform: scale3d(1, 0.3, 1); + opacity: 0; + transform: scale3d(1, 0.3, 1); } to { - opacity: 1; + opacity: 1; } - } - @keyframes react-contexify__slideOut { +} +@keyframes react-contexify__slideOut { from { - opacity: 1; + opacity: 1; } to { - opacity: 0; - transform: scale3d(1, 0.3, 1); + opacity: 0; + transform: scale3d(1, 0.3, 1); } - } - .react-contexify__will-enter--slide { +} +.react-contexify__will-enter--slide { transform-origin: top center; animation: react-contexify__slideIn 0.3s; - } - - .react-contexify__will-leave--slide { +} + +.react-contexify__will-leave--slide { transform-origin: top center; animation: react-contexify__slideOut 0.3s; - } - - /*# sourceMappingURL=ReactContexify.css.map */ \ No newline at end of file +} + +/*# sourceMappingURL=ReactContexify.css.map */ diff --git a/vscode-trace-webviews/src/trace-explorer/available-views/index.css b/vscode-trace-webviews/src/trace-explorer/available-views/index.css index b4cc7250..22f8f189 100644 --- a/vscode-trace-webviews/src/trace-explorer/available-views/index.css +++ b/vscode-trace-webviews/src/trace-explorer/available-views/index.css @@ -1,5 +1,5 @@ body { - margin: 0; - padding: 0; - font-family: sans-serif; + margin: 0; + padding: 0; + font-family: sans-serif; } diff --git a/vscode-trace-webviews/src/trace-explorer/available-views/index.tsx b/vscode-trace-webviews/src/trace-explorer/available-views/index.tsx index c3b653c7..3735ce84 100644 --- a/vscode-trace-webviews/src/trace-explorer/available-views/index.tsx +++ b/vscode-trace-webviews/src/trace-explorer/available-views/index.tsx @@ -4,6 +4,4 @@ import './index.css'; import TraceExplorerViewsWidget from './vscode-trace-explorer-views-widget'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-explorer/available-views/vscode-trace-explorer-views-widget.tsx b/vscode-trace-webviews/src/trace-explorer/available-views/vscode-trace-explorer-views-widget.tsx index 46b3c9ce..283c34a1 100644 --- a/vscode-trace-webviews/src/trace-explorer/available-views/vscode-trace-explorer-views-widget.tsx +++ b/vscode-trace-webviews/src/trace-explorer/available-views/vscode-trace-explorer-views-widget.tsx @@ -15,85 +15,89 @@ import { convertSignalExperiment } from 'vscode-trace-common/lib/signals/vscode- import { TraceServerUrlProvider } from 'vscode-trace-common/lib/server/trace-server-url-provider'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); interface AvailableViewsAppState { - tspClientProvider: ITspClientProvider | undefined; + tspClientProvider: ITspClientProvider | undefined; } -class TraceExplorerViewsWidget extends React.Component<{}, AvailableViewsAppState> { - private _signalHandler: VsCodeMessageManager; - private _urlProvider: TraceServerUrlProvider; +class TraceExplorerViewsWidget extends React.Component<{}, AvailableViewsAppState> { + private _signalHandler: VsCodeMessageManager; + private _urlProvider: TraceServerUrlProvider; - static ID = 'trace-explorer-analysis-widget'; - static LABEL = 'Available Analyses'; + static ID = 'trace-explorer-analysis-widget'; + static LABEL = 'Available Analyses'; - private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => this.doHandleExperimentSelectedSignal(openedExperiment); - private _onOutputAdded = (payload: OutputAddedSignalPayload): void => this.doHandleOutputAddedSignal(payload); + private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => + this.doHandleExperimentSelectedSignal(openedExperiment); + private _onOutputAdded = (payload: OutputAddedSignalPayload): void => this.doHandleOutputAddedSignal(payload); - constructor(props: {}) { - super(props); - this.state = { - tspClientProvider: undefined, - }; - this._signalHandler = new VsCodeMessageManager(); - window.addEventListener('message', event => { + constructor(props: {}) { + super(props); + this.state = { + tspClientProvider: undefined + }; + this._signalHandler = new VsCodeMessageManager(); + window.addEventListener('message', event => { + const message = event.data; // The JSON data our extension sent + switch (message.command) { + case VSCODE_MESSAGES.SET_TSP_CLIENT: + this._urlProvider = new TraceServerUrlProvider(); + this.setState({ + tspClientProvider: new TspClientProvider(message.data, this._signalHandler, this._urlProvider) + }); + break; + case VSCODE_MESSAGES.EXPERIMENT_SELECTED: + let experiment: Experiment | undefined = undefined; + if (message.data) { + experiment = convertSignalExperiment(JSONBig.parse(message.data)); + } + signalManager().fireExperimentSelectedSignal(experiment); + break; + case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: + if (message.data && this.state.tspClientProvider && this._urlProvider) { + this._urlProvider.updateTraceServerUrl(message.data); + } + break; + } + }); + } - const message = event.data; // The JSON data our extension sent - switch (message.command) { - case VSCODE_MESSAGES.SET_TSP_CLIENT: - this._urlProvider = new TraceServerUrlProvider(); - this.setState({ tspClientProvider: new TspClientProvider(message.data, this._signalHandler, this._urlProvider) }); - break; - case VSCODE_MESSAGES.EXPERIMENT_SELECTED: - let experiment: Experiment | undefined = undefined; - if (message.data) { - experiment = convertSignalExperiment(JSONBig.parse(message.data)); - } - signalManager().fireExperimentSelectedSignal(experiment); - break; - case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: - if (message.data && this.state.tspClientProvider && this._urlProvider) { - this._urlProvider.updateTraceServerUrl(message.data); - } - break; - } - }); - } + componentDidMount(): void { + this._signalHandler.notifyReady(); + signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().on(Signals.OUTPUT_ADDED, this._onOutputAdded); + } - componentDidMount(): void { - this._signalHandler.notifyReady(); - signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().on(Signals.OUTPUT_ADDED, this._onOutputAdded); - } + componentWillUnmount(): void { + signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().off(Signals.OUTPUT_ADDED, this._onOutputAdded); + } - componentWillUnmount(): void { - signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().off(Signals.OUTPUT_ADDED, this._onOutputAdded); - } + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + this._signalHandler.experimentSelected(experiment); + } - protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - this._signalHandler.experimentSelected(experiment); - } + protected doHandleOutputAddedSignal(payload: OutputAddedSignalPayload): void { + if (payload) { + this._signalHandler.outputAdded(payload); + } + } - protected doHandleOutputAddedSignal(payload: OutputAddedSignalPayload): void { - if (payload) { - this._signalHandler.outputAdded(payload); - } - } - - public render(): React.ReactNode { - return (
- {this.state.tspClientProvider && - } -
- ); - } + public render(): React.ReactNode { + return ( +
+ {this.state.tspClientProvider && ( + + )} +
+ ); + } } export default TraceExplorerViewsWidget; diff --git a/vscode-trace-webviews/src/trace-explorer/opened-traces/index.css b/vscode-trace-webviews/src/trace-explorer/opened-traces/index.css index b4cc7250..22f8f189 100644 --- a/vscode-trace-webviews/src/trace-explorer/opened-traces/index.css +++ b/vscode-trace-webviews/src/trace-explorer/opened-traces/index.css @@ -1,5 +1,5 @@ body { - margin: 0; - padding: 0; - font-family: sans-serif; + margin: 0; + padding: 0; + font-family: sans-serif; } diff --git a/vscode-trace-webviews/src/trace-explorer/opened-traces/index.tsx b/vscode-trace-webviews/src/trace-explorer/opened-traces/index.tsx index ea543973..7a451eaf 100644 --- a/vscode-trace-webviews/src/trace-explorer/opened-traces/index.tsx +++ b/vscode-trace-webviews/src/trace-explorer/opened-traces/index.tsx @@ -5,6 +5,4 @@ import TraceExplorerOpenedTraces from './vscode-trace-explorer-opened-traces-wid const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-explorer/opened-traces/vscode-trace-explorer-opened-traces-widget.tsx b/vscode-trace-webviews/src/trace-explorer/opened-traces/vscode-trace-explorer-opened-traces-widget.tsx index 05ba73cc..ff693ed6 100644 --- a/vscode-trace-webviews/src/trace-explorer/opened-traces/vscode-trace-explorer-opened-traces-widget.tsx +++ b/vscode-trace-webviews/src/trace-explorer/opened-traces/vscode-trace-explorer-opened-traces-widget.tsx @@ -18,196 +18,220 @@ import { ReactExplorerPlaceholderWidget } from 'traceviewer-react-components/lib import { TraceServerUrlProvider } from 'vscode-trace-common/lib/server/trace-server-url-provider'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); interface OpenedTracesAppState { - tspClientProvider: ITspClientProvider | undefined; - experimentsOpened: boolean; + tspClientProvider: ITspClientProvider | undefined; + experimentsOpened: boolean; } const MENU_ID = 'traceExplorer.openedTraces.menuId'; -class TraceExplorerOpenedTraces extends React.Component<{}, OpenedTracesAppState> { - private _signalHandler: VsCodeMessageManager; - private _experimentManager: ExperimentManager; - private _urlProvider: TraceServerUrlProvider; - - static ID = 'trace-explorer-opened-traces-widget'; - static LABEL = 'Opened Traces'; - - private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => this.doHandleExperimentSelectedSignal(openedExperiment); - private _onRemoveTraceButton = (traceUUID: string): void => this.doHandleRemoveTraceSignal(traceUUID); - protected onUpdateSignal = (payload: OpenedTracesUpdatedSignalPayload): void => this.doHandleOpenedTracesChanged(payload); - private loading = false; - - private doHandleRemoveTraceSignal(traceUUID: string) { - this._experimentManager.getExperiment(traceUUID).then( experimentOpen => { - if (experimentOpen) { - this._signalHandler.deleteTrace(experimentOpen); - } - }).catch( error => { - console.error('Error: Unable to find experiment for the trace UUID, ', error); - }); - } - - constructor(props: {}) { - super(props); - this.state = { - tspClientProvider: undefined, - experimentsOpened: true, - }; - this._signalHandler = new VsCodeMessageManager(); - window.addEventListener('message', event => { - const message = event.data; // The JSON data our extension sent - switch (message.command) { - case VSCODE_MESSAGES.SET_TSP_CLIENT: - this._urlProvider = new TraceServerUrlProvider(); - const tspClientProvider: ITspClientProvider = new TspClientProvider(message.data, this._signalHandler, this._urlProvider); - this._experimentManager = tspClientProvider.getExperimentManager(); - - tspClientProvider.addTspClientChangeListener(() => { - if (this.state.tspClientProvider) { - this._experimentManager = this.state.tspClientProvider.getExperimentManager(); - } - }); - - this.setState({ tspClientProvider: tspClientProvider }); - break; - case VSCODE_MESSAGES.TRACE_VIEWER_TAB_ACTIVATED: - if (message.data) { - const experiment = convertSignalExperiment(JSONBig.parse(message.data)); - signalManager().fireTraceViewerTabActivatedSignal(experiment); - } - break; - case VSCODE_MESSAGES.EXPERIMENT_OPENED: - if (message.data) { - const experiment = convertSignalExperiment(JSONBig.parse(message.data)); - signalManager().fireExperimentOpenedSignal(experiment); - if (!this.state.experimentsOpened) { - this.setState({experimentsOpened: true}); - } - } - break; - case VSCODE_MESSAGES.TRACE_SERVER_STARTED: - signalManager().fireTraceServerStartedSignal(); - this.setState({experimentsOpened: true}); - case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: - if (message.data && this.state.tspClientProvider && this._urlProvider) { - this._urlProvider.updateTraceServerUrl(message.data); - } - break; - } - }); - // this.onOutputRemoved = this.onOutputRemoved.bind(this); - } - - componentDidMount(): void { - this._signalHandler.notifyReady(); - // ExperimentSelected handler is registered in the constructor (upstream code), but it's - // better to register it here when the react component gets mounted. - signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().on(Signals.CLOSE_TRACEVIEWERTAB, this._onRemoveTraceButton); - signalManager().on(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal); - } - - componentWillUnmount(): void { - signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); - signalManager().off(Signals.CLOSE_TRACEVIEWERTAB, this._onRemoveTraceButton); - signalManager().off(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal); - } - - protected doHandleOpenedTracesChanged(payload: OpenedTracesUpdatedSignalPayload): void { - this._signalHandler.updateOpenedTraces(payload.getNumberOfOpenedTraces()); - if (payload.getNumberOfOpenedTraces()>0) { - this.setState({experimentsOpened: true}); - } else if (payload.getNumberOfOpenedTraces()===0){ - this.setState({experimentsOpened: false}); - } - } - - protected doHandleContextMenuEvent(event: React.MouseEvent, experiment: Experiment): void { - const { show } = useContextMenu({ - id: MENU_ID, - }); - - show(event, { - props: { - experiment: experiment, - } - }); - } - - protected doHandleClickEvent(event: React.MouseEvent, experiment: Experiment): void { - this.doHandleReOpenTrace(experiment); - } - - protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - this._signalHandler.experimentSelected(experiment); - } - - public render(): React.ReactNode { - return ( this.state.experimentsOpened ? <>
- {this.state.tspClientProvider && , experiment: Experiment) => this.doHandleContextMenuEvent(event, experiment)} - onClick={(event: React.MouseEvent, experiment: Experiment) => this.doHandleClickEvent(event, experiment) } - > - } -
- - Open Trace - Close Trace - Remove Trace - - : - - ); - } - - protected handleOpenTrace = async (): Promise => this.doHandleOpenTrace(); - - private async doHandleOpenTrace() { - this.loading = true; - this._signalHandler.openTrace(); - this.loading = false; - } - - protected async doHandleReOpenTrace(experiment: Experiment): Promise { - let myExperiment: Experiment | undefined = experiment; - if (this.state.tspClientProvider) { - const exp = await this.state.tspClientProvider.getExperimentManager().updateExperiment(experiment.UUID); - if (exp) { - myExperiment = exp; - } - } - this._signalHandler.reOpenTrace(myExperiment); - } - - protected handleItemClick = (args: ItemParams): void => { - switch (args.event.currentTarget.id) { - case 'open-id': - this.doHandleReOpenTrace(args.props.experiment as Experiment); - return; - case 'close-id': - this._signalHandler.closeTrace(args.props.experiment as Experiment); - return; - case 'remove-id': - this._signalHandler.deleteTrace(args.props.experiment as Experiment); - if (this._experimentManager) { - this._experimentManager.deleteExperiment((args.props.experiment as Experiment).UUID); - } - - return; - default: - // Do nothing - } - }; +class TraceExplorerOpenedTraces extends React.Component<{}, OpenedTracesAppState> { + private _signalHandler: VsCodeMessageManager; + private _experimentManager: ExperimentManager; + private _urlProvider: TraceServerUrlProvider; + + static ID = 'trace-explorer-opened-traces-widget'; + static LABEL = 'Opened Traces'; + + private _onExperimentSelected = (openedExperiment: Experiment | undefined): void => + this.doHandleExperimentSelectedSignal(openedExperiment); + private _onRemoveTraceButton = (traceUUID: string): void => this.doHandleRemoveTraceSignal(traceUUID); + protected onUpdateSignal = (payload: OpenedTracesUpdatedSignalPayload): void => + this.doHandleOpenedTracesChanged(payload); + private loading = false; + + private doHandleRemoveTraceSignal(traceUUID: string) { + this._experimentManager + .getExperiment(traceUUID) + .then(experimentOpen => { + if (experimentOpen) { + this._signalHandler.deleteTrace(experimentOpen); + } + }) + .catch(error => { + console.error('Error: Unable to find experiment for the trace UUID, ', error); + }); + } + + constructor(props: {}) { + super(props); + this.state = { + tspClientProvider: undefined, + experimentsOpened: true + }; + this._signalHandler = new VsCodeMessageManager(); + window.addEventListener('message', event => { + const message = event.data; // The JSON data our extension sent + switch (message.command) { + case VSCODE_MESSAGES.SET_TSP_CLIENT: + this._urlProvider = new TraceServerUrlProvider(); + const tspClientProvider: ITspClientProvider = new TspClientProvider( + message.data, + this._signalHandler, + this._urlProvider + ); + this._experimentManager = tspClientProvider.getExperimentManager(); + + tspClientProvider.addTspClientChangeListener(() => { + if (this.state.tspClientProvider) { + this._experimentManager = this.state.tspClientProvider.getExperimentManager(); + } + }); + + this.setState({ tspClientProvider: tspClientProvider }); + break; + case VSCODE_MESSAGES.TRACE_VIEWER_TAB_ACTIVATED: + if (message.data) { + const experiment = convertSignalExperiment(JSONBig.parse(message.data)); + signalManager().fireTraceViewerTabActivatedSignal(experiment); + } + break; + case VSCODE_MESSAGES.EXPERIMENT_OPENED: + if (message.data) { + const experiment = convertSignalExperiment(JSONBig.parse(message.data)); + signalManager().fireExperimentOpenedSignal(experiment); + if (!this.state.experimentsOpened) { + this.setState({ experimentsOpened: true }); + } + } + break; + case VSCODE_MESSAGES.TRACE_SERVER_STARTED: + signalManager().fireTraceServerStartedSignal(); + this.setState({ experimentsOpened: true }); + case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: + if (message.data && this.state.tspClientProvider && this._urlProvider) { + this._urlProvider.updateTraceServerUrl(message.data); + } + break; + } + }); + // this.onOutputRemoved = this.onOutputRemoved.bind(this); + } + + componentDidMount(): void { + this._signalHandler.notifyReady(); + // ExperimentSelected handler is registered in the constructor (upstream code), but it's + // better to register it here when the react component gets mounted. + signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().on(Signals.CLOSE_TRACEVIEWERTAB, this._onRemoveTraceButton); + signalManager().on(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal); + } + + componentWillUnmount(): void { + signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); + signalManager().off(Signals.CLOSE_TRACEVIEWERTAB, this._onRemoveTraceButton); + signalManager().off(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal); + } + + protected doHandleOpenedTracesChanged(payload: OpenedTracesUpdatedSignalPayload): void { + this._signalHandler.updateOpenedTraces(payload.getNumberOfOpenedTraces()); + if (payload.getNumberOfOpenedTraces() > 0) { + this.setState({ experimentsOpened: true }); + } else if (payload.getNumberOfOpenedTraces() === 0) { + this.setState({ experimentsOpened: false }); + } + } + + protected doHandleContextMenuEvent(event: React.MouseEvent, experiment: Experiment): void { + const { show } = useContextMenu({ + id: MENU_ID + }); + + show(event, { + props: { + experiment: experiment + } + }); + } + + protected doHandleClickEvent(event: React.MouseEvent, experiment: Experiment): void { + this.doHandleReOpenTrace(experiment); + } + + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + this._signalHandler.experimentSelected(experiment); + } + + public render(): React.ReactNode { + return this.state.experimentsOpened ? ( + <> +
+ {this.state.tspClientProvider && ( + , + experiment: Experiment + ) => this.doHandleContextMenuEvent(event, experiment)} + onClick={(event: React.MouseEvent, experiment: Experiment) => + this.doHandleClickEvent(event, experiment) + } + > + )} +
+ + + Open Trace + + + Close Trace + + + Remove Trace + + + + ) : ( + + ); + } + + protected handleOpenTrace = async (): Promise => this.doHandleOpenTrace(); + + private async doHandleOpenTrace() { + this.loading = true; + this._signalHandler.openTrace(); + this.loading = false; + } + + protected async doHandleReOpenTrace(experiment: Experiment): Promise { + let myExperiment: Experiment | undefined = experiment; + if (this.state.tspClientProvider) { + const exp = await this.state.tspClientProvider.getExperimentManager().updateExperiment(experiment.UUID); + if (exp) { + myExperiment = exp; + } + } + this._signalHandler.reOpenTrace(myExperiment); + } + + protected handleItemClick = (args: ItemParams): void => { + switch (args.event.currentTarget.id) { + case 'open-id': + this.doHandleReOpenTrace(args.props.experiment as Experiment); + return; + case 'close-id': + this._signalHandler.closeTrace(args.props.experiment as Experiment); + return; + case 'remove-id': + this._signalHandler.deleteTrace(args.props.experiment as Experiment); + if (this._experimentManager) { + this._experimentManager.deleteExperiment((args.props.experiment as Experiment).UUID); + } + + return; + default: + // Do nothing + } + }; } export default TraceExplorerOpenedTraces; diff --git a/vscode-trace-webviews/src/trace-explorer/properties/index.tsx b/vscode-trace-webviews/src/trace-explorer/properties/index.tsx index d566598e..6256cf7d 100644 --- a/vscode-trace-webviews/src/trace-explorer/properties/index.tsx +++ b/vscode-trace-webviews/src/trace-explorer/properties/index.tsx @@ -10,6 +10,4 @@ import TraceExplorerProperties from './vscode-trace-explorer-properties-widget'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-explorer/properties/vscode-trace-explorer-properties-widget.tsx b/vscode-trace-webviews/src/trace-explorer/properties/vscode-trace-explorer-properties-widget.tsx index 21951629..a2e4717e 100644 --- a/vscode-trace-webviews/src/trace-explorer/properties/vscode-trace-explorer-properties-widget.tsx +++ b/vscode-trace-webviews/src/trace-explorer/properties/vscode-trace-explorer-properties-widget.tsx @@ -28,9 +28,9 @@ class TraceExplorerProperties extends React.Component<{}, PropertiesViewState> { window.addEventListener('message', event => { const message = event.data; // The JSON data our extension sent switch (message.command) { - case 'receivedProperties': - signalManager().fireItemPropertiesSignalUpdated(message.data.properties); - break; + case 'receivedProperties': + signalManager().fireItemPropertiesSignalUpdated(message.data.properties); + break; } }); } @@ -51,14 +51,16 @@ class TraceExplorerProperties extends React.Component<{}, PropertiesViewState> { ); } - protected handleSourcecodeLookup = (e: React.MouseEvent): void => this.doHandleSourcecodeLookup(e); + protected handleSourcecodeLookup = (e: React.MouseEvent): void => + this.doHandleSourcecodeLookup(e); private doHandleSourcecodeLookup(e: React.MouseEvent) { - const { fileLocation, line }: { fileLocation: string, line: string } = JSON.parse(`${e.currentTarget.getAttribute('data-id')}`); + const { fileLocation, line }: { fileLocation: string; line: string } = JSON.parse( + `${e.currentTarget.getAttribute('data-id')}` + ); console.log('filename: ' + fileLocation + ':' + line); console.log('Source lookup method not implemented'); } - } export default TraceExplorerProperties; diff --git a/vscode-trace-webviews/src/trace-explorer/shortcuts/index.css b/vscode-trace-webviews/src/trace-explorer/shortcuts/index.css index 9474b155..c4179c9f 100644 --- a/vscode-trace-webviews/src/trace-explorer/shortcuts/index.css +++ b/vscode-trace-webviews/src/trace-explorer/shortcuts/index.css @@ -1,12 +1,12 @@ body { - margin: 0; - padding: 0; - font-family: sans-serif; - padding: calc(var(--trace-viewer-ui-padding)*2) + margin: 0; + padding: 0; + font-family: sans-serif; + padding: calc(var(--trace-viewer-ui-padding) * 2); } .componentBody { - background-color: var(--trace-viewer-editorWidget-background); - color: var(--trace-viewer-foreground); - padding: calc(var(--trace-viewer-ui-padding)*2) + background-color: var(--trace-viewer-editorWidget-background); + color: var(--trace-viewer-foreground); + padding: calc(var(--trace-viewer-ui-padding) * 2); } diff --git a/vscode-trace-webviews/src/trace-explorer/shortcuts/index.tsx b/vscode-trace-webviews/src/trace-explorer/shortcuts/index.tsx index 8b0274fc..12ec420c 100644 --- a/vscode-trace-webviews/src/trace-explorer/shortcuts/index.tsx +++ b/vscode-trace-webviews/src/trace-explorer/shortcuts/index.tsx @@ -5,6 +5,4 @@ import ChartShortcutsComponent from './vscode-trace-explorer-shortcuts-widget'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-explorer/time-range/index.css b/vscode-trace-webviews/src/trace-explorer/time-range/index.css index b4cc7250..22f8f189 100644 --- a/vscode-trace-webviews/src/trace-explorer/time-range/index.css +++ b/vscode-trace-webviews/src/trace-explorer/time-range/index.css @@ -1,5 +1,5 @@ body { - margin: 0; - padding: 0; - font-family: sans-serif; + margin: 0; + padding: 0; + font-family: sans-serif; } diff --git a/vscode-trace-webviews/src/trace-explorer/time-range/index.tsx b/vscode-trace-webviews/src/trace-explorer/time-range/index.tsx index cc55b98b..074af96a 100644 --- a/vscode-trace-webviews/src/trace-explorer/time-range/index.tsx +++ b/vscode-trace-webviews/src/trace-explorer/time-range/index.tsx @@ -4,6 +4,4 @@ import './index.css'; import TimeRangeDataWidget from './vscode-time-range-data-widget'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-explorer/time-range/vscode-time-range-data-widget.tsx b/vscode-trace-webviews/src/trace-explorer/time-range/vscode-time-range-data-widget.tsx index 0471259b..6fd5692e 100644 --- a/vscode-trace-webviews/src/trace-explorer/time-range/vscode-time-range-data-widget.tsx +++ b/vscode-trace-webviews/src/trace-explorer/time-range/vscode-time-range-data-widget.tsx @@ -1,6 +1,9 @@ import * as React from 'react'; import { Signals, signalManager } from 'traceviewer-base/lib/signals/signal-manager'; -import { ExperimentTimeRangeData, ReactTimeRangeDataWidget } from 'traceviewer-react-components/lib/trace-explorer/trace-explorer-time-range-data-widget'; +import { + ExperimentTimeRangeData, + ReactTimeRangeDataWidget +} from 'traceviewer-react-components/lib/trace-explorer/trace-explorer-time-range-data-widget'; import 'traceviewer-react-components/style/trace-explorer.css'; import '../../style/react-contextify.css'; import '../../style/trace-viewer.css'; @@ -10,7 +13,7 @@ import { VSCODE_MESSAGES, VsCodeMessageManager } from 'vscode-trace-common/lib/m import { TimeRangeUpdatePayload } from 'traceviewer-base/lib/signals/time-range-data-signal-payloads'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); interface TimeRangeDataWidgetProps { @@ -20,82 +23,77 @@ interface TimeRangeDataWidgetProps { // declare const vscode: vscode; class TimeRangeDataWidget extends React.Component { - private _signalHandler: VsCodeMessageManager; - private _reactRef: React.RefObject; + private _signalHandler: VsCodeMessageManager; + private _reactRef: React.RefObject; - static ID = 'trace-explorer-time-range-data-widget'; - static LABEL = 'Time Range Data'; + static ID = 'trace-explorer-time-range-data-widget'; + static LABEL = 'Time Range Data'; - constructor(props: TimeRangeDataWidgetProps) { - super(props); - this._signalHandler = new VsCodeMessageManager(); - this._reactRef = React.createRef(); + constructor(props: TimeRangeDataWidgetProps) { + super(props); + this._signalHandler = new VsCodeMessageManager(); + this._reactRef = React.createRef(); - window.addEventListener('message', event => { + window.addEventListener('message', event => { + const { command, data } = event.data; - const { command, data } = event.data; + switch (command) { + case VSCODE_MESSAGES.RESTORE_VIEW: + const { mapArray, activeData } = JSONBig.parse(data); + this.restoreState(mapArray, activeData); + return; + case VSCODE_MESSAGES.TRACE_VIEWER_TAB_CLOSED: + signalManager().fireCloseTraceViewerTabSignal(data); + break; + case VSCODE_MESSAGES.EXPERIMENT_SELECTED: + signalManager().fireExperimentSelectedSignal( + data?.wrapper ? convertSignalExperiment(JSONBig.parse(data.wrapper)) : undefined + ); + break; + case VSCODE_MESSAGES.EXPERIMENT_UPDATED: + signalManager().fireExperimentUpdatedSignal(convertSignalExperiment(JSONBig.parse(data.wrapper))); + break; + case VSCODE_MESSAGES.EXPERIMENT_CLOSED: + signalManager().fireExperimentClosedSignal(convertSignalExperiment(JSONBig.parse(data.wrapper))); + break; + case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: + signalManager().fireSelectionRangeUpdated(JSONBig.parse(data)); + break; + case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: + signalManager().fireViewRangeUpdated(JSONBig.parse(data)); + break; + } + }); + } - switch (command) { - case VSCODE_MESSAGES.RESTORE_VIEW: - const { mapArray, activeData } = JSONBig.parse(data); - this.restoreState(mapArray, activeData); - return; - case VSCODE_MESSAGES.TRACE_VIEWER_TAB_CLOSED: - signalManager().fireCloseTraceViewerTabSignal(data); - break; - case VSCODE_MESSAGES.EXPERIMENT_SELECTED: - signalManager().fireExperimentSelectedSignal( - data?.wrapper ? convertSignalExperiment(JSONBig.parse(data.wrapper)) : undefined - ); - break; - case VSCODE_MESSAGES.EXPERIMENT_UPDATED: - signalManager().fireExperimentUpdatedSignal( - convertSignalExperiment(JSONBig.parse(data.wrapper)) - ); - break; - case VSCODE_MESSAGES.EXPERIMENT_CLOSED: - signalManager().fireExperimentClosedSignal( - convertSignalExperiment(JSONBig.parse(data.wrapper)) - ); - break; - case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: - signalManager().fireSelectionRangeUpdated(JSONBig.parse(data)); - break; - case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: - signalManager().fireViewRangeUpdated(JSONBig.parse(data)); - break; - } - }); - } + componentWillUnmount = (): void => { + signalManager().off(Signals.REQUEST_SELECTION_RANGE_CHANGE, this.onRequestSelectionChange); + }; - componentWillUnmount = (): void => { - signalManager().off(Signals.REQUEST_SELECTION_RANGE_CHANGE, this.onRequestSelectionChange); - }; + onRequestSelectionChange = (payload: TimeRangeUpdatePayload): void => { + this._signalHandler.requestSelectionRangeChange(payload); + }; - onRequestSelectionChange = (payload: TimeRangeUpdatePayload): void => { - this._signalHandler.requestSelectionRangeChange(payload); - }; + componentDidMount = (): void => { + this._signalHandler.notifyReady(); + signalManager().on(Signals.REQUEST_SELECTION_RANGE_CHANGE, this.onRequestSelectionChange); + }; - componentDidMount = (): void => { - this._signalHandler.notifyReady(); - signalManager().on(Signals.REQUEST_SELECTION_RANGE_CHANGE, this.onRequestSelectionChange); - }; + restoreState = (mapArray: Array, activeData: ExperimentTimeRangeData): void => { + this._reactRef.current?.restoreData(mapArray, activeData); + }; - restoreState = (mapArray: Array, activeData: ExperimentTimeRangeData): void => { - this._reactRef.current?.restoreData(mapArray, activeData); - }; - - public render(): React.ReactNode { - return ( -
- -
- ); - } + public render(): React.ReactNode { + return ( +
+ +
+ ); + } } export default TimeRangeDataWidget; diff --git a/vscode-trace-webviews/src/trace-viewer/index.css b/vscode-trace-webviews/src/trace-viewer/index.css index 0dbc9258..4ab67c74 100644 --- a/vscode-trace-webviews/src/trace-viewer/index.css +++ b/vscode-trace-webviews/src/trace-viewer/index.css @@ -1,12 +1,12 @@ body { - height: 100%; - margin: 0; - padding: 0; - font-family: sans-serif; + height: 100%; + margin: 0; + padding: 0; + font-family: sans-serif; } #root { - height: 100%; - width: 100%; - position: absolute; -} \ No newline at end of file + height: 100%; + width: 100%; + position: absolute; +} diff --git a/vscode-trace-webviews/src/trace-viewer/index.tsx b/vscode-trace-webviews/src/trace-viewer/index.tsx index 9222cbaf..1087bcd6 100644 --- a/vscode-trace-webviews/src/trace-viewer/index.tsx +++ b/vscode-trace-webviews/src/trace-viewer/index.tsx @@ -5,6 +5,4 @@ import './index.css'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - -); +root.render(); diff --git a/vscode-trace-webviews/src/trace-viewer/vscode-trace-viewer-container.tsx b/vscode-trace-webviews/src/trace-viewer/vscode-trace-viewer-container.tsx index b76daacb..15d2f31e 100644 --- a/vscode-trace-webviews/src/trace-viewer/vscode-trace-viewer-container.tsx +++ b/vscode-trace-webviews/src/trace-viewer/vscode-trace-viewer-container.tsx @@ -22,416 +22,455 @@ import { TimeRangeUpdatePayload } from 'traceviewer-base/lib/signals/time-range- import { TimeRange } from 'traceviewer-base/lib/utils/time-range'; const JSONBig = JSONBigConfig({ - useNativeBigInt: true, + useNativeBigInt: true }); interface VscodeAppState { - experiment: Experiment | undefined; - tspClientProvider: TspClientProvider | undefined; - outputs: OutputDescriptor[]; - overviewOutputDescriptor: OutputDescriptor| undefined; - theme: string; + experiment: Experiment | undefined; + tspClientProvider: TspClientProvider | undefined; + outputs: OutputDescriptor[]; + overviewOutputDescriptor: OutputDescriptor | undefined; + theme: string; } -class TraceViewerContainer extends React.Component<{}, VscodeAppState> { - private DEFAULT_OVERVIEW_DATA_PROVIDER_ID = 'org.eclipse.tracecompass.internal.tmf.core.histogram.HistogramDataProvider'; - - private _signalHandler: VsCodeMessageManager; - private _urlProvider: TraceServerUrlProvider; - - private onViewRangeUpdated = (payload: TimeRangeUpdatePayload): void => this._signalHandler.viewRangeUpdated(payload); - private onSelectionRangeUpdated = (payload: TimeRangeUpdatePayload): void => this._signalHandler.selectionRangeUpdated(payload); - private onExperimentUpdated = (payload: Experiment): void => this._signalHandler.experimentUpdated(payload); - - private _onProperties = (properties: { [key: string]: string }): void => this.doHandlePropertiesSignal(properties); - private _onSaveAsCSV = (payload: {traceId: string, data: string}): void => this.doHandleSaveAsCSVSignal(payload); - /** Signal Handlers */ - private doHandlePropertiesSignal(properties: { [key: string]: string }) { - this._signalHandler.propertiesUpdated(properties); - } - - private doHandleSaveAsCSVSignal(payload: {traceId: string, data: string}) { - this._signalHandler.saveAsCSV(payload); - } - - private _onOverviewSelected = (payload: {traceId: string, outputDescriptor: OutputDescriptor}): void => this.doHandleOverviewSelectedSignal(payload); - private onMarkerCategoryClosedSignal = (payload: { traceViewerId: string, markerCategory: string }) => this.doHandleMarkerCategoryClosedSignal(payload); - - protected resizeHandlers: (() => void)[] = []; - protected readonly addResizeHandler = (h: () => void): void => { - this.resizeHandlers.push(h); - }; - protected readonly removeResizeHandler = (h: () => void): void => { - const index = this.resizeHandlers.indexOf(h, 0); - if (index > -1) { - this.resizeHandlers.splice(index, 1); - } - }; - - private markerCategoriesMap = new Map(); - private toolbarMarkerCategoriesMap = new Map(); - private selectedMarkerCategoriesMap = new Map(); - private markerSetsMap = new Map(); - private selectedMarkerSetId = ''; - - constructor(props: {}) { - super(props); - this.state = { - experiment: undefined, - tspClientProvider: undefined, - outputs: [], - overviewOutputDescriptor: undefined, - theme: 'light' - }; - this._signalHandler = new VsCodeMessageManager(); - - window.addEventListener('message', event => { - - const message = event.data; // The JSON data our extension sent - switch (message.command) { - case VSCODE_MESSAGES.SET_EXPERIMENT: - this.doHandleExperimentSetSignal(convertSignalExperiment(JSONBig.parse(message.data)), false); - break; - case VSCODE_MESSAGES.SET_TSP_CLIENT: - this._urlProvider = new TraceServerUrlProvider(); - this.setState({tspClientProvider: new TspClientProvider(message.data, this._signalHandler, this._urlProvider)}, () => { - if (message.experiment) { - this.doHandleExperimentSetSignal(convertSignalExperiment(JSONBig.parse(message.experiment)), true); - } - }); - break; - case VSCODE_MESSAGES.ADD_OUTPUT: - // FIXME: JSONBig.parse() create bigint if numbers are small - // Not an issue right now for output descriptors. - if (message?.data) { - const descriptor: OutputDescriptor = JSONBig.parse(message.data); - this.doHandleOutputAddedMessage(descriptor); - } - break; - case VSCODE_MESSAGES.OPEN_OVERVIEW: - this.doHandleExperimentSetSignal(this.state.experiment, false); - break; - case VSCODE_MESSAGES.SET_THEME: - this.doHandleThemeChanged(message.data); - break; - case VSCODE_MESSAGES.RESET_ZOOM: - this.resetZoom(); - break; - case VSCODE_MESSAGES.UNDO: - this.undo(); - break; - case VSCODE_MESSAGES.REDO: - this.redo(); - break; - case VSCODE_MESSAGES.UPDATE_ZOOM: - this.updateZoom(message.data); - case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: - signalManager().fireViewRangeUpdated(JSONBig.parse(message.data)); - break; - case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: - signalManager().fireSelectionRangeUpdated(JSONBig.parse(message.data)); - break; - case VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE: - const { experimentUUID, timeRange } = JSONBig.parse(message.data); - const payload = { - experimentUUID, - timeRange: new TimeRange(BigInt(timeRange.start),BigInt(timeRange.end)) - } as TimeRangeUpdatePayload; - signalManager().fireRequestSelectionRangeChange(payload); - break; - case VSCODE_MESSAGES.UPDATE_MARKER_CATEGORY_STATE: - if (message?.data) { - const selection: string[] = JSON.parse(message.data); - this.updateAllMarkerCategoryState(selection); - } - break; - case VSCODE_MESSAGES.UPDATE_MARKER_SET_STATE: - if (message?.data) { - this.updateMarkerSetState(message.data); - } - break; - case VSCODE_MESSAGES.GET_MARKER_CATEGORIES: - this._signalHandler.fetchMarkerCategories(this.toolbarMarkerCategoriesMap); - break; - case VSCODE_MESSAGES.GET_MARKER_SETS: - this._signalHandler.fetchMarkerSets(this.markerSetsMap); - break; - case VSCODE_MESSAGES.EXPERIMENT_SELECTED: - this.doHandleExperimentSelectedSignal(convertSignalExperiment(JSONBig.parse(message.data))); - break; - case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: - if (message.data && this.state.tspClientProvider && this._urlProvider) { - this._urlProvider.updateTraceServerUrl(message.data); - } - break; - } - }); - window.addEventListener('resize', this.onResize); - this.onOutputRemoved = this.onOutputRemoved.bind(this); - this.onOverviewRemoved = this.onOverviewRemoved.bind(this); - signalManager().on(Signals.OVERVIEW_OUTPUT_SELECTED, this._onOverviewSelected); - } - - componentDidMount(): void { - this._signalHandler.notifyReady(); - signalManager().on(Signals.ITEM_PROPERTIES_UPDATED, this._onProperties); - signalManager().on(Signals.SAVE_AS_CSV, this._onSaveAsCSV); - signalManager().on(Signals.MARKER_CATEGORY_CLOSED, this.onMarkerCategoryClosedSignal); - signalManager().on(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); - signalManager().on(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); - signalManager().on(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); - } - - componentWillUnmount(): void { - signalManager().off(Signals.ITEM_PROPERTIES_UPDATED, this._onProperties); - signalManager().off(Signals.OVERVIEW_OUTPUT_SELECTED, this._onOverviewSelected); - signalManager().off(Signals.SAVE_AS_CSV, this._onSaveAsCSV); - signalManager().off(Signals.MARKER_CATEGORY_CLOSED, this.onMarkerCategoryClosedSignal); - signalManager().off(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); - signalManager().off(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); - signalManager().off(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); - window.removeEventListener('resize', this.onResize); - } - - private onResize = (): void => { - this.resizeHandlers.forEach(h => h()); - }; - - private onOutputRemoved(outputId: string) { - const outputToKeep = this.state.outputs.filter(output => output.id !== outputId); - this.removeMarkerCategories(outputId); - this.setState({outputs: outputToKeep}); - } - - protected onOverviewRemoved(): void { - this.setState({overviewOutputDescriptor: undefined}); - } - - protected resetZoom(): void { - signalManager().fireResetZoomSignal(); - } - - protected undo(): void { - signalManager().fireUndoSignal(); - } - - protected redo(): void { - signalManager().fireRedoSignal(); - } - - protected updateZoom(hasZoomedIn: boolean): void { - signalManager().fireUpdateZoomSignal(hasZoomedIn); - } - - protected async doHandleExperimentSetSignal(experiment: Experiment| undefined, fetchMarkerSets: boolean): Promise { - if (experiment) { - if (fetchMarkerSets) { - await this.fetchMarkerSets(experiment.UUID); - } - const defaultOverviewDescriptor: OutputDescriptor | undefined = await this.getDefaultTraceOverviewOutputDescriptor(experiment); - this.setState({ - experiment: experiment, - overviewOutputDescriptor: defaultOverviewDescriptor}); - this._signalHandler.setMarkerSetsContext(this.markerSetsMap.size > 0); - this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); - } - } - - protected doHandleExperimentSelectedSignal(experiment: Experiment| undefined): void { - if (experiment?.UUID === this.state.experiment?.UUID) { - this._signalHandler.setMarkerSetsContext(this.markerSetsMap.size > 0); - this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); - } - } - - protected async doHandleOutputAddedMessage(descriptor: OutputDescriptor): Promise { - if (!this.state.outputs.find(output => output.id === descriptor.id)) { - await this.fetchAnnotationCategories(descriptor); - this.setState({outputs: [...this.state.outputs, descriptor] }); - } - } - - private async fetchMarkerSets(expUUID: string): Promise { - if (this.state.tspClientProvider) { - const markers = await this.state.tspClientProvider.getTspClient().fetchMarkerSets(expUUID); - const markersResponse = markers.getModel(); - if (markersResponse && markers.isOk()) { - const markerSets = markersResponse.model; - this.markerSetsMap.clear(); - if (markerSets.length) { - this.markerSetsMap.set('-1', { marker: { name: 'None', id: '-1' } as MarkerSet, enabled: true }); - } - markerSets.forEach(markerSet => { - if (!this.markerSetsMap.has(markerSet.id)) { - this.markerSetsMap.set(markerSet.id, { marker: markerSet, enabled: false }); - } - }); - } - } - } - - private async fetchAnnotationCategories(output: OutputDescriptor) { - if (this.state.experiment && this.state.tspClientProvider) { - const annotationCategories = await this.state.tspClientProvider.getTspClient(). - fetchAnnotationsCategories(this.state.experiment.UUID, output.id, this.selectedMarkerSetId); - const annotationCategoriesResponse = annotationCategories.getModel(); - if (annotationCategories.isOk() && annotationCategoriesResponse) { - const markerCategories = annotationCategoriesResponse.model ? annotationCategoriesResponse.model.annotationCategories : []; - this.addMarkerCategories(output.id, markerCategories); - } - } - } - - private addMarkerCategories(outputId: string, markerCategories: string[]) { - this.removeMarkerCategories(outputId); - const selectedMarkerCategories: string[] = []; - markerCategories.forEach(category => { - const categoryInfo = this.toolbarMarkerCategoriesMap.get(category); - const categoryCount = categoryInfo ? categoryInfo.categoryCount + 1 : 1; - const toggleInd = categoryInfo ? categoryInfo.toggleInd : true; - this.toolbarMarkerCategoriesMap.set(category, { categoryCount, toggleInd }); - if (toggleInd) { - selectedMarkerCategories.push(category); - } - }); - this.selectedMarkerCategoriesMap.set(outputId, selectedMarkerCategories); - this.markerCategoriesMap.set(outputId, markerCategories); - this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); - } - - private removeMarkerCategories(outputId: string) { - const categoriesToRemove = this.markerCategoriesMap.get(outputId); - if (categoriesToRemove) { - categoriesToRemove.forEach(category => { - const categoryInfo = this.toolbarMarkerCategoriesMap.get(category); - const categoryCount = categoryInfo ? categoryInfo.categoryCount - 1 : 0; - const toggleInd = categoryInfo ? categoryInfo.toggleInd : true; - if (categoryCount === 0) { - this.toolbarMarkerCategoriesMap.delete(category); - } else { - this.toolbarMarkerCategoriesMap.set(category, { categoryCount, toggleInd }); - } - }); - } - this.markerCategoriesMap.delete(outputId); - this.selectedMarkerCategoriesMap.delete(outputId); - this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); - } - - private doHandleMarkerCategoryClosedSignal(payload: { traceViewerId: string, markerCategory: string }) { - const traceViewerId = payload.traceViewerId; - const markerCategory = payload.markerCategory; - if (traceViewerId === this.state.experiment?.UUID) { - this.updateMarkerCategoryState(markerCategory, false); - } - } - - updateMarkerCategoryState(categoryName: string, toggleInd: boolean, skipUpdate?: boolean): void { - const toggledmarkerCategory = this.toolbarMarkerCategoriesMap.get(categoryName); - if (toggledmarkerCategory) { - const categoryCount = toggledmarkerCategory?.categoryCount; - this.toolbarMarkerCategoriesMap.set(categoryName, { categoryCount, toggleInd }); - this.markerCategoriesMap.forEach((categoriesList, outputId) => { - const selectedMarkerCategories = categoriesList.filter(category => { - const currCategoryInfo = this.toolbarMarkerCategoriesMap.get(category); - return currCategoryInfo ? currCategoryInfo.toggleInd : false; - }); - this.selectedMarkerCategoriesMap.set(outputId, selectedMarkerCategories); - }); - } - if (!skipUpdate) { - this.forceUpdate(); - } - } - - updateAllMarkerCategoryState(selection: string[]): void { - const set: Set = new Set(); - - for (const categoryName of selection) { - set.add(categoryName); - } - - const markerCategories = this.toolbarMarkerCategoriesMap; - for (const [key] of markerCategories) { - if (set.has(key)) { - this.updateMarkerCategoryState(key, true, true); - } else { - this.updateMarkerCategoryState(key, false, true); - } - } - this.forceUpdate(); - } - - async updateMarkerSetState(markerSetId: string): Promise { - if (this.markerSetsMap.get(markerSetId)?.enabled) { - return; - } - this.selectedMarkerSetId = markerSetId; - const prevSelectedMarkerSet = Array.from(this.markerSetsMap.values()).find(markerSetItem => markerSetItem.enabled); - if (prevSelectedMarkerSet) { - prevSelectedMarkerSet.enabled = false; - } - - const markerSetEntry = this.markerSetsMap.get(markerSetId); - if (markerSetEntry) { - this.markerSetsMap.set(markerSetId, { ...markerSetEntry, enabled: true }); - } - - if (await Promise.all(this.state.outputs.map(output => this.fetchAnnotationCategories(output)))) { - this.forceUpdate(); - } - } - - protected doHandleOverviewSelectedSignal(payload: {traceId: string, outputDescriptor: OutputDescriptor}): void { - if (this.state.experiment && payload && payload?.traceId === this.state.experiment.UUID && payload.outputDescriptor){ - this.setState({overviewOutputDescriptor: payload.outputDescriptor}); - } - } - - protected doHandleThemeChanged(theme: string): void { - this.setState({ theme }, () => { - signalManager().fireThemeChangedSignal(theme); - }); - } - - protected async getDefaultTraceOverviewOutputDescriptor(experiment: Experiment| undefined): Promise { - const availableDescriptors = await this.getAvailableTraceOverviewOutputDescriptor(experiment); - return availableDescriptors?.find(output => output.id === this.DEFAULT_OVERVIEW_DATA_PROVIDER_ID); - } - - protected async getAvailableTraceOverviewOutputDescriptor(experiment: Experiment| undefined): Promise { - let descriptors: OutputDescriptor[] | undefined; - if (experiment && this.state.tspClientProvider) { - const outputsResponse = await this.state.tspClientProvider.getTspClient().experimentOutputs(experiment.UUID); - if (outputsResponse && outputsResponse.isOk()) { - descriptors = outputsResponse.getModel(); - } - const overviewOutputDescriptors = descriptors?.filter(output => output.type === 'TREE_TIME_XY'); - return overviewOutputDescriptors; - } - } - - public render(): React.ReactNode { - return ( -
- { this.state.experiment && this.state.tspClientProvider && - } -
- ); - } +class TraceViewerContainer extends React.Component<{}, VscodeAppState> { + private DEFAULT_OVERVIEW_DATA_PROVIDER_ID = + 'org.eclipse.tracecompass.internal.tmf.core.histogram.HistogramDataProvider'; + + private _signalHandler: VsCodeMessageManager; + private _urlProvider: TraceServerUrlProvider; + + private onViewRangeUpdated = (payload: TimeRangeUpdatePayload): void => + this._signalHandler.viewRangeUpdated(payload); + private onSelectionRangeUpdated = (payload: TimeRangeUpdatePayload): void => + this._signalHandler.selectionRangeUpdated(payload); + private onExperimentUpdated = (payload: Experiment): void => this._signalHandler.experimentUpdated(payload); + + private _onProperties = (properties: { [key: string]: string }): void => this.doHandlePropertiesSignal(properties); + private _onSaveAsCSV = (payload: { traceId: string; data: string }): void => this.doHandleSaveAsCSVSignal(payload); + /** Signal Handlers */ + private doHandlePropertiesSignal(properties: { [key: string]: string }) { + this._signalHandler.propertiesUpdated(properties); + } + + private doHandleSaveAsCSVSignal(payload: { traceId: string; data: string }) { + this._signalHandler.saveAsCSV(payload); + } + + private _onOverviewSelected = (payload: { traceId: string; outputDescriptor: OutputDescriptor }): void => + this.doHandleOverviewSelectedSignal(payload); + private onMarkerCategoryClosedSignal = (payload: { traceViewerId: string; markerCategory: string }) => + this.doHandleMarkerCategoryClosedSignal(payload); + + protected resizeHandlers: (() => void)[] = []; + protected readonly addResizeHandler = (h: () => void): void => { + this.resizeHandlers.push(h); + }; + protected readonly removeResizeHandler = (h: () => void): void => { + const index = this.resizeHandlers.indexOf(h, 0); + if (index > -1) { + this.resizeHandlers.splice(index, 1); + } + }; + + private markerCategoriesMap = new Map(); + private toolbarMarkerCategoriesMap = new Map(); + private selectedMarkerCategoriesMap = new Map(); + private markerSetsMap = new Map(); + private selectedMarkerSetId = ''; + + constructor(props: {}) { + super(props); + this.state = { + experiment: undefined, + tspClientProvider: undefined, + outputs: [], + overviewOutputDescriptor: undefined, + theme: 'light' + }; + this._signalHandler = new VsCodeMessageManager(); + + window.addEventListener('message', event => { + const message = event.data; // The JSON data our extension sent + switch (message.command) { + case VSCODE_MESSAGES.SET_EXPERIMENT: + this.doHandleExperimentSetSignal(convertSignalExperiment(JSONBig.parse(message.data)), false); + break; + case VSCODE_MESSAGES.SET_TSP_CLIENT: + this._urlProvider = new TraceServerUrlProvider(); + this.setState( + { + tspClientProvider: new TspClientProvider( + message.data, + this._signalHandler, + this._urlProvider + ) + }, + () => { + if (message.experiment) { + this.doHandleExperimentSetSignal( + convertSignalExperiment(JSONBig.parse(message.experiment)), + true + ); + } + } + ); + break; + case VSCODE_MESSAGES.ADD_OUTPUT: + // FIXME: JSONBig.parse() create bigint if numbers are small + // Not an issue right now for output descriptors. + if (message?.data) { + const descriptor: OutputDescriptor = JSONBig.parse(message.data); + this.doHandleOutputAddedMessage(descriptor); + } + break; + case VSCODE_MESSAGES.OPEN_OVERVIEW: + this.doHandleExperimentSetSignal(this.state.experiment, false); + break; + case VSCODE_MESSAGES.SET_THEME: + this.doHandleThemeChanged(message.data); + break; + case VSCODE_MESSAGES.RESET_ZOOM: + this.resetZoom(); + break; + case VSCODE_MESSAGES.UNDO: + this.undo(); + break; + case VSCODE_MESSAGES.REDO: + this.redo(); + break; + case VSCODE_MESSAGES.UPDATE_ZOOM: + this.updateZoom(message.data); + case VSCODE_MESSAGES.VIEW_RANGE_UPDATED: + signalManager().fireViewRangeUpdated(JSONBig.parse(message.data)); + break; + case VSCODE_MESSAGES.SELECTION_RANGE_UPDATED: + signalManager().fireSelectionRangeUpdated(JSONBig.parse(message.data)); + break; + case VSCODE_MESSAGES.REQUEST_SELECTION_RANGE_CHANGE: + const { experimentUUID, timeRange } = JSONBig.parse(message.data); + const payload = { + experimentUUID, + timeRange: new TimeRange(BigInt(timeRange.start), BigInt(timeRange.end)) + } as TimeRangeUpdatePayload; + signalManager().fireRequestSelectionRangeChange(payload); + break; + case VSCODE_MESSAGES.UPDATE_MARKER_CATEGORY_STATE: + if (message?.data) { + const selection: string[] = JSON.parse(message.data); + this.updateAllMarkerCategoryState(selection); + } + break; + case VSCODE_MESSAGES.UPDATE_MARKER_SET_STATE: + if (message?.data) { + this.updateMarkerSetState(message.data); + } + break; + case VSCODE_MESSAGES.GET_MARKER_CATEGORIES: + this._signalHandler.fetchMarkerCategories(this.toolbarMarkerCategoriesMap); + break; + case VSCODE_MESSAGES.GET_MARKER_SETS: + this._signalHandler.fetchMarkerSets(this.markerSetsMap); + break; + case VSCODE_MESSAGES.EXPERIMENT_SELECTED: + this.doHandleExperimentSelectedSignal(convertSignalExperiment(JSONBig.parse(message.data))); + break; + case VSCODE_MESSAGES.TRACE_SERVER_URL_CHANGED: + if (message.data && this.state.tspClientProvider && this._urlProvider) { + this._urlProvider.updateTraceServerUrl(message.data); + } + break; + } + }); + window.addEventListener('resize', this.onResize); + this.onOutputRemoved = this.onOutputRemoved.bind(this); + this.onOverviewRemoved = this.onOverviewRemoved.bind(this); + signalManager().on(Signals.OVERVIEW_OUTPUT_SELECTED, this._onOverviewSelected); + } + + componentDidMount(): void { + this._signalHandler.notifyReady(); + signalManager().on(Signals.ITEM_PROPERTIES_UPDATED, this._onProperties); + signalManager().on(Signals.SAVE_AS_CSV, this._onSaveAsCSV); + signalManager().on(Signals.MARKER_CATEGORY_CLOSED, this.onMarkerCategoryClosedSignal); + signalManager().on(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); + signalManager().on(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); + signalManager().on(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); + } + + componentWillUnmount(): void { + signalManager().off(Signals.ITEM_PROPERTIES_UPDATED, this._onProperties); + signalManager().off(Signals.OVERVIEW_OUTPUT_SELECTED, this._onOverviewSelected); + signalManager().off(Signals.SAVE_AS_CSV, this._onSaveAsCSV); + signalManager().off(Signals.MARKER_CATEGORY_CLOSED, this.onMarkerCategoryClosedSignal); + signalManager().off(Signals.VIEW_RANGE_UPDATED, this.onViewRangeUpdated); + signalManager().off(Signals.SELECTION_RANGE_UPDATED, this.onSelectionRangeUpdated); + signalManager().off(Signals.EXPERIMENT_UPDATED, this.onExperimentUpdated); + window.removeEventListener('resize', this.onResize); + } + + private onResize = (): void => { + this.resizeHandlers.forEach(h => h()); + }; + + private onOutputRemoved(outputId: string) { + const outputToKeep = this.state.outputs.filter(output => output.id !== outputId); + this.removeMarkerCategories(outputId); + this.setState({ outputs: outputToKeep }); + } + + protected onOverviewRemoved(): void { + this.setState({ overviewOutputDescriptor: undefined }); + } + + protected resetZoom(): void { + signalManager().fireResetZoomSignal(); + } + + protected undo(): void { + signalManager().fireUndoSignal(); + } + + protected redo(): void { + signalManager().fireRedoSignal(); + } + + protected updateZoom(hasZoomedIn: boolean): void { + signalManager().fireUpdateZoomSignal(hasZoomedIn); + } + + protected async doHandleExperimentSetSignal( + experiment: Experiment | undefined, + fetchMarkerSets: boolean + ): Promise { + if (experiment) { + if (fetchMarkerSets) { + await this.fetchMarkerSets(experiment.UUID); + } + const defaultOverviewDescriptor: OutputDescriptor | undefined = + await this.getDefaultTraceOverviewOutputDescriptor(experiment); + this.setState({ + experiment: experiment, + overviewOutputDescriptor: defaultOverviewDescriptor + }); + this._signalHandler.setMarkerSetsContext(this.markerSetsMap.size > 0); + this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); + } + } + + protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { + if (experiment?.UUID === this.state.experiment?.UUID) { + this._signalHandler.setMarkerSetsContext(this.markerSetsMap.size > 0); + this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); + } + } + + protected async doHandleOutputAddedMessage(descriptor: OutputDescriptor): Promise { + if (!this.state.outputs.find(output => output.id === descriptor.id)) { + await this.fetchAnnotationCategories(descriptor); + this.setState({ outputs: [...this.state.outputs, descriptor] }); + } + } + + private async fetchMarkerSets(expUUID: string): Promise { + if (this.state.tspClientProvider) { + const markers = await this.state.tspClientProvider.getTspClient().fetchMarkerSets(expUUID); + const markersResponse = markers.getModel(); + if (markersResponse && markers.isOk()) { + const markerSets = markersResponse.model; + this.markerSetsMap.clear(); + if (markerSets.length) { + this.markerSetsMap.set('-1', { marker: { name: 'None', id: '-1' } as MarkerSet, enabled: true }); + } + markerSets.forEach(markerSet => { + if (!this.markerSetsMap.has(markerSet.id)) { + this.markerSetsMap.set(markerSet.id, { marker: markerSet, enabled: false }); + } + }); + } + } + } + + private async fetchAnnotationCategories(output: OutputDescriptor) { + if (this.state.experiment && this.state.tspClientProvider) { + const annotationCategories = await this.state.tspClientProvider + .getTspClient() + .fetchAnnotationsCategories(this.state.experiment.UUID, output.id, this.selectedMarkerSetId); + const annotationCategoriesResponse = annotationCategories.getModel(); + if (annotationCategories.isOk() && annotationCategoriesResponse) { + const markerCategories = annotationCategoriesResponse.model + ? annotationCategoriesResponse.model.annotationCategories + : []; + this.addMarkerCategories(output.id, markerCategories); + } + } + } + + private addMarkerCategories(outputId: string, markerCategories: string[]) { + this.removeMarkerCategories(outputId); + const selectedMarkerCategories: string[] = []; + markerCategories.forEach(category => { + const categoryInfo = this.toolbarMarkerCategoriesMap.get(category); + const categoryCount = categoryInfo ? categoryInfo.categoryCount + 1 : 1; + const toggleInd = categoryInfo ? categoryInfo.toggleInd : true; + this.toolbarMarkerCategoriesMap.set(category, { categoryCount, toggleInd }); + if (toggleInd) { + selectedMarkerCategories.push(category); + } + }); + this.selectedMarkerCategoriesMap.set(outputId, selectedMarkerCategories); + this.markerCategoriesMap.set(outputId, markerCategories); + this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); + } + + private removeMarkerCategories(outputId: string) { + const categoriesToRemove = this.markerCategoriesMap.get(outputId); + if (categoriesToRemove) { + categoriesToRemove.forEach(category => { + const categoryInfo = this.toolbarMarkerCategoriesMap.get(category); + const categoryCount = categoryInfo ? categoryInfo.categoryCount - 1 : 0; + const toggleInd = categoryInfo ? categoryInfo.toggleInd : true; + if (categoryCount === 0) { + this.toolbarMarkerCategoriesMap.delete(category); + } else { + this.toolbarMarkerCategoriesMap.set(category, { categoryCount, toggleInd }); + } + }); + } + this.markerCategoriesMap.delete(outputId); + this.selectedMarkerCategoriesMap.delete(outputId); + this._signalHandler.setMarkerCategoriesContext(this.toolbarMarkerCategoriesMap.size > 0); + } + + private doHandleMarkerCategoryClosedSignal(payload: { traceViewerId: string; markerCategory: string }) { + const traceViewerId = payload.traceViewerId; + const markerCategory = payload.markerCategory; + if (traceViewerId === this.state.experiment?.UUID) { + this.updateMarkerCategoryState(markerCategory, false); + } + } + + updateMarkerCategoryState(categoryName: string, toggleInd: boolean, skipUpdate?: boolean): void { + const toggledmarkerCategory = this.toolbarMarkerCategoriesMap.get(categoryName); + if (toggledmarkerCategory) { + const categoryCount = toggledmarkerCategory?.categoryCount; + this.toolbarMarkerCategoriesMap.set(categoryName, { categoryCount, toggleInd }); + this.markerCategoriesMap.forEach((categoriesList, outputId) => { + const selectedMarkerCategories = categoriesList.filter(category => { + const currCategoryInfo = this.toolbarMarkerCategoriesMap.get(category); + return currCategoryInfo ? currCategoryInfo.toggleInd : false; + }); + this.selectedMarkerCategoriesMap.set(outputId, selectedMarkerCategories); + }); + } + if (!skipUpdate) { + this.forceUpdate(); + } + } + + updateAllMarkerCategoryState(selection: string[]): void { + const set: Set = new Set(); + + for (const categoryName of selection) { + set.add(categoryName); + } + + const markerCategories = this.toolbarMarkerCategoriesMap; + for (const [key] of markerCategories) { + if (set.has(key)) { + this.updateMarkerCategoryState(key, true, true); + } else { + this.updateMarkerCategoryState(key, false, true); + } + } + this.forceUpdate(); + } + + async updateMarkerSetState(markerSetId: string): Promise { + if (this.markerSetsMap.get(markerSetId)?.enabled) { + return; + } + this.selectedMarkerSetId = markerSetId; + const prevSelectedMarkerSet = Array.from(this.markerSetsMap.values()).find( + markerSetItem => markerSetItem.enabled + ); + if (prevSelectedMarkerSet) { + prevSelectedMarkerSet.enabled = false; + } + + const markerSetEntry = this.markerSetsMap.get(markerSetId); + if (markerSetEntry) { + this.markerSetsMap.set(markerSetId, { ...markerSetEntry, enabled: true }); + } + + if (await Promise.all(this.state.outputs.map(output => this.fetchAnnotationCategories(output)))) { + this.forceUpdate(); + } + } + + protected doHandleOverviewSelectedSignal(payload: { traceId: string; outputDescriptor: OutputDescriptor }): void { + if ( + this.state.experiment && + payload && + payload?.traceId === this.state.experiment.UUID && + payload.outputDescriptor + ) { + this.setState({ overviewOutputDescriptor: payload.outputDescriptor }); + } + } + + protected doHandleThemeChanged(theme: string): void { + this.setState({ theme }, () => { + signalManager().fireThemeChangedSignal(theme); + }); + } + + protected async getDefaultTraceOverviewOutputDescriptor( + experiment: Experiment | undefined + ): Promise { + const availableDescriptors = await this.getAvailableTraceOverviewOutputDescriptor(experiment); + return availableDescriptors?.find(output => output.id === this.DEFAULT_OVERVIEW_DATA_PROVIDER_ID); + } + + protected async getAvailableTraceOverviewOutputDescriptor( + experiment: Experiment | undefined + ): Promise { + let descriptors: OutputDescriptor[] | undefined; + if (experiment && this.state.tspClientProvider) { + const outputsResponse = await this.state.tspClientProvider + .getTspClient() + .experimentOutputs(experiment.UUID); + if (outputsResponse && outputsResponse.isOk()) { + descriptors = outputsResponse.getModel(); + } + const overviewOutputDescriptors = descriptors?.filter(output => output.type === 'TREE_TIME_XY'); + return overviewOutputDescriptors; + } + } + + public render(): React.ReactNode { + return ( +
+ {this.state.experiment && this.state.tspClientProvider && ( + + )} +
+ ); + } } export default TraceViewerContainer;