Skip to content

Commit a6d6bb8

Browse files
committed
Implemented external API
This commit provides an external API that can be used by other extensions to communicate with the base trace extension. The current API provides a way to retrieve the active experiment as well as fire events and register callbacks. This is the initial API and will be further expanded in future commits. Fixes #140 Signed-off-by: Neel Gondalia <[email protected]>
1 parent 9372436 commit a6d6bb8

File tree

6 files changed

+350
-2
lines changed

6 files changed

+350
-2
lines changed

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,78 @@ yarn playwright test --retries <retries>
215215
[vscode-messages]: https://code.visualstudio.com/api/extension-guides/webview#passing-messages-from-an-extension-to-a-webview
216216
[vscode-webview]: https://github.com/rebornix/vscode-webview-react
217217
[vscode-webview-react]: https://github.com/rebornix/vscode-webview-react
218+
219+
## Using the External API
220+
221+
VSCode Trace Extension provides an external API that adopter extensions can rely on for communication. Currently the API is limited to the following:
222+
223+
```javascript
224+
getActiveExperiment(): Experiment | undefined,
225+
getEvents(): RegisteredEventEmitter<unknown>[],
226+
fireEvent(registeredEvent: RegisteredEventEmitter<unknown>, data: unknown): void,
227+
createSignalCallbackInterface(signal: string, callback: (data: unknown) => void): SignalCallbackInterface;
228+
onEventRegistered(listener: (event: RegisteredEventEmitter<unknown>) => void): void,
229+
onEventDisposed(listener: (event: RegisteredEventEmitter<unknown>) => void): void
230+
```
231+
232+
### Using the API from Adopter Extensions
233+
```javascript
234+
//The following retrieves the API object from the vscode-trace-extension
235+
const ext = vscode.extensions.getExtension("tracecompass-community.vscode-trace-extension");
236+
const importedApi = ext.exports;
237+
```
238+
Once you have the API object, you can proceed to make API calls. For example, if you wish to retrieve the active experiment in the Trace Viewer, the following API call can be used:
239+
```javascript
240+
const experiment = importedApi.getActiveExperiment();
241+
```
242+
243+
The API also provides a signaling mechanism so that the adopter extensions can fire/subscribe to events.
244+
```javascript
245+
/**
246+
* Returns the registered events that can be fired by the adopter extensions.
247+
*/
248+
const events = importedApi.getEvents();
249+
250+
for (const event of events) {
251+
const data = importedApi.createSignalCallbackInterface('experimentSelected', () => {
252+
console.log("experiment selected signal fired");
253+
});
254+
importedApi.fireEvent(event, data);
255+
}
256+
257+
/**
258+
* Adopter extensions can also attach listeners for when new events are registered/disposed
259+
*/
260+
importedApi.onEventRegistered((event) => {
261+
//handle new event registered
262+
});
263+
264+
onNewEventRegistered.onEventDisposed(event => {
265+
//handle disposed events
266+
})
267+
```
268+
269+
Currently, the only registered events in the base extension is "traceExplorer.openedTracesView.register_callback". Upon firing this event, you can register a callback for a given signal to the OpenedTracesView. Note that this event is registered/disposed with the webview, so it is recommended to add listeners for 'onEventRegistered' and 'onEventDisposed'.
270+
271+
### RegisteredEventEmitter Class
272+
273+
The 'RegisteredEventEmitter' class has the following properties:
274+
```javascript
275+
//Unique Identifier
276+
id: string;
277+
//Event Emitter
278+
eventEmitter: vscode.EventEmitter<T>;
279+
//Event Handler for the event
280+
onEventHandler: (data: unknown) => void;
281+
//Type of data the event handler expects
282+
type: DataType;
283+
```
284+
When firing events from the adopter extensions, it is recommended to inspect the 'type' property and use the API to create objects based on the type.
285+
```javascript
286+
if (event.type === 'SignalCallbackInterface') {
287+
const data = importedApi.createSignalCallbackInterface('experimentSelected', () => {
288+
console.log("experiment selected signal fired");
289+
});
290+
importedApi.fireEvent(event, data);
291+
}
292+
```

vscode-trace-extension/src/extension.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import { fileHandler, openOverviewHandler, resetZoomHandler, undoRedoHandler, zo
88
import { TraceServerConnectionStatusService } from './utils/trace-server-status';
99
import { updateTspClient } from './utils/tspClient';
1010
import { TraceExtensionLogger } from './utils/trace-extension-logger';
11+
import { ExternalAPI, traceExtensionAPI} from './external-api/external-api';
12+
import { TraceExtensionEventManager } from './utils/trace-extension-event-manager';
1113

1214
export let traceLogger: TraceExtensionLogger;
15+
export const traceExtensionEventManager: TraceExtensionEventManager = new TraceExtensionEventManager();
1316

14-
export function activate(context: vscode.ExtensionContext): void {
17+
export function activate(context: vscode.ExtensionContext): ExternalAPI {
1518
traceLogger = new TraceExtensionLogger('Trace Extension');
1619

1720
const serverStatusBarItemPriority = 1;
@@ -93,6 +96,7 @@ export function activate(context: vscode.ExtensionContext): void {
9396
context.subscriptions.push(vscode.commands.registerCommand('serverStatus.stopped', () => {
9497
serverStatusService.render(false);
9598
}));
99+
return traceExtensionAPI;
96100
}
97101

98102
export function deactivate(): void {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/***************************************************************************************
2+
* Copyright (c) 2023 BlackBerry Limited and others.
3+
*
4+
* Licensed under the MIT license. See LICENSE file in the project root for details.
5+
***************************************************************************************/
6+
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
7+
import { TraceViewerPanel } from '../trace-viewer-panel/trace-viewer-webview-panel';
8+
import { traceExtensionEventManager } from '../extension';
9+
import { RegisteredEventEmitter, SignalCallbackInterface } from '../utils/trace-extension-event-manager';
10+
11+
export interface ExternalAPI {
12+
getActiveExperiment(): Experiment | undefined,
13+
getEvents(): RegisteredEventEmitter<unknown>[],
14+
fireEvent(registeredEvent: RegisteredEventEmitter<unknown>, data: unknown): void,
15+
createSignalCallbackInterface(signal: string, callback: (data: unknown) => void): SignalCallbackInterface;
16+
onEventRegistered(listener: (event: RegisteredEventEmitter<unknown>) => void): void,
17+
onEventDisposed(listener: (event: RegisteredEventEmitter<unknown>) => void): void
18+
}
19+
20+
export const traceExtensionAPI: ExternalAPI = {
21+
/**
22+
* Retrieves the currently active experiement
23+
*
24+
* @returns Experiment if one is currently active, otherwise undefined
25+
*/
26+
getActiveExperiment(): Experiment | undefined {
27+
return TraceViewerPanel.getCurrentExperiment();
28+
},
29+
30+
/**
31+
* Retrieves the list of event emitters registered with the Event Manager
32+
*
33+
* @returns list of event emitter IDs
34+
*/
35+
getEvents(): RegisteredEventEmitter<unknown>[] {
36+
return traceExtensionEventManager.getRegisteredEvents();
37+
},
38+
39+
/**
40+
* Fires the event emitter with the provided ID
41+
*
42+
* @param eventId ID of the event emitter to fire
43+
* @param data event object
44+
*/
45+
fireEvent(registeredEvent: RegisteredEventEmitter<unknown>, data: unknown): void {
46+
traceExtensionEventManager.fireEvent(registeredEvent, data);
47+
},
48+
49+
/**
50+
* Creates a SignalCallbackInterface which is passed as a data for certain Event Emitters
51+
*
52+
* @param _signal signal to attach the callback to
53+
* @param _callback function to call when the signal is fired
54+
* @returns SignalCallbackInterface object
55+
*/
56+
createSignalCallbackInterface(_signal: string, _callback: (data: unknown) => void): SignalCallbackInterface {
57+
return {
58+
_signal: _signal,
59+
_callback: _callback
60+
};
61+
},
62+
63+
/**
64+
* Attaches listener for when an event is registered
65+
*
66+
* @param listener event listener
67+
*/
68+
onEventRegistered(listener: (event: RegisteredEventEmitter<unknown>) => unknown): void {
69+
traceExtensionEventManager.onNewEventRegistered(listener);
70+
},
71+
72+
/**
73+
* Attaches listener for when events are disposed
74+
*
75+
* @param listener event listener
76+
*/
77+
onEventDisposed(listener: (event: RegisteredEventEmitter<unknown>) => unknown): void {
78+
traceExtensionEventManager.onEventDisposed(listener);
79+
},
80+
};

vscode-trace-extension/src/trace-explorer/opened-traces/trace-explorer-opened-traces-webview-provider.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { TraceViewerPanel } from '../../trace-viewer-panel/trace-viewer-webview-
88
import { TraceServerConnectionStatusService } from '../../utils/trace-server-status';
99
import { getTraceServerUrl, getTspClientUrl } from '../../utils/tspClient';
1010
import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message-manager';
11+
import { traceExtensionEventManager } from 'vscode-trace-extension/src/extension';
12+
import { DataType, SignalCallbackInterface } from '../../utils/trace-extension-event-manager';
1113

1214
const JSONBig = JSONBigConfig({
1315
useNativeBigInt: true,
@@ -16,12 +18,13 @@ const JSONBig = JSONBigConfig({
1618
export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewViewProvider {
1719

1820
public static readonly viewType = 'traceExplorer.openedTracesView';
21+
public static readonly eventEmitter_id_callback = this.viewType + '.register_callback';
1922

2023
private _view?: vscode.WebviewView;
2124
private _disposables: vscode.Disposable[] = [];
2225
private _selectedExperiment: Experiment | undefined;
2326

24-
constructor(
27+
constructor(
2528
private readonly _extensionUri: vscode.Uri,
2629
private readonly _statusService: TraceServerConnectionStatusService,
2730
) {}
@@ -52,6 +55,16 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
5255
}
5356
}
5457

58+
protected registerCallbackForSignal(data: SignalCallbackInterface): void {
59+
this._view?.webview.onDidReceiveMessage(message => {
60+
switch (message.command) {
61+
case data._signal: {
62+
data._callback(message);
63+
}
64+
}
65+
});
66+
}
67+
5568
protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void {
5669
if (this._view) {
5770
this._selectedExperiment = experiment;
@@ -65,6 +78,11 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
6578
): void {
6679
this._view = webviewView;
6780

81+
// Registers an event with the traceExtensionEventManager to register callbacks for signals received in this webview
82+
const registerCallbackEvent = traceExtensionEventManager.registerEventEmitter(webviewView.viewType + '.register-callback',
83+
(data: unknown) => {this.registerCallbackForSignal(data as SignalCallbackInterface);},
84+
DataType.SignalCallback);
85+
6886
webviewView.webview.options = {
6987
// Allow scripts in the webview
7088
enableScripts: true,
@@ -132,6 +150,7 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
132150
signalManager().off(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated);
133151
signalManager().off(Signals.OPENED_TRACES_UPDATED, this._onOpenedTracesChanged);
134152
signalManager().off(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected);
153+
traceExtensionEventManager.disposeEvent(registerCallbackEvent);
135154
}, undefined, this._disposables);
136155
}
137156

vscode-trace-extension/src/trace-viewer-panel/trace-viewer-webview-panel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ export class TraceViewerPanel {
8686
TraceViewerPanel.currentPanel?.updateZoom(hasZoomedIn);
8787
}
8888

89+
public static getCurrentExperiment(): Experiment | undefined {
90+
return TraceViewerPanel.currentPanel?._experiment;
91+
}
92+
8993
private static async saveTraceCsv(csvData: string, defaultFileName: string) {
9094
const saveDialogOptions = {
9195
defaultUri: vscode.workspace.workspaceFolders

0 commit comments

Comments
 (0)