Skip to content

Commit

Permalink
Render new trace server connection status bar item
Browse files Browse the repository at this point in the history
Add a contextual (secondary, right-side) VS Code status bar item, that
renders the current trace server status. Have the extension context pass
this status service to each active web-view provider, for them to update
the rendered status upon every related message from the view.

Have TraceExplorerOpenedTracesViewProvider pass its status service to
the TraceViewerPanel upon using it, in turn. Without this, there are
instances where the latest server status isn't shown as expected. Keep
this undefined in trace-tree.ts where that is not required.

This makes the server status listener-based. Add this connection
listener through the TspClientProvider constructor, thus consistently
use the latter across. This means also using it in the
TraceViewerContainer constructor, instead of TspClient's directly prior.

Properly 'stringify' (thus parse) the boolean status value, as passing
it straight would break the JSON message otherwise.

Explicitly defer showing the item to only once Trace Viewer gets
selected for the first time. Like open trace-explorer views which remain
shown if unselecting Trace Viewer, keep showing the status item from
there on. The VS Code API, anyway, doesn't hold an event that could
selectively hide anything upon solely exiting Trace Viewer.

Prefer a standard VS Code status bar item to the side widget approach of
theia-trace-extension. This should be more standard for such a
connection status display, from a VS Code UI (and UX) perspective. This
also allows not having to implement a custom widget that differs from
the Trace Viewer panels. -The latter being meant to render real views.

Base this simple design of the status bar item on [1] below. Now, use
the warning background for a found server, for such a positive status to
be highlighted, thus noticeable. Use the usual red background for a
trace server that is not found, highlighting that as an error. No other
color can or should be used anyway for status bar items, per [1]. The
latter reference also includes [2]'s, used to come up with this change.

[1]https://code.visualstudio.com/api/ux-guidelines/status-bar
[2]https://github.com/microsoft/vscode-extension-samples/blob/main/statusbar-sample/src/extension.ts

Use a short (thus usable) enough statusBarItem.text, along with a
tooltip to complement the trace server status narrative.

Overall, with this approach, defer the introduction of the inversify
module, used in theia-trace-extension. This would be to inject instances
such as objects involved in this change and likely other ones; some
being potential singletons.

Slightly refactor a few surrounding blank lines while editing some of
these files.

Fixes #33.

Signed-off-by: Marco Miller <marco.miller@ericsson.com>
  • Loading branch information
marco-miller committed Apr 21, 2023
1 parent e398d33 commit 8a689e4
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 20 deletions.
10 changes: 8 additions & 2 deletions vscode-trace-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ import { TraceExplorerItemPropertiesProvider } from './trace-explorer/properties
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 } from './trace-explorer/trace-tree';
import { TraceServerConnectionStatusService } from './utils/trace-server-status';
import { updateTspClient } from './utils/tspClient';

export function activate(context: vscode.ExtensionContext): void {

const tracesProvider = new TraceExplorerOpenedTracesViewProvider(context.extensionUri);
const serverStatusBarItemPriority = 1;
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));

const myAnalysisProvider = new TraceExplorerAvailableViewsProvider(context.extensionUri);
const myAnalysisProvider = new TraceExplorerAvailableViewsProvider(context.extensionUri, serverStatusService);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(TraceExplorerAvailableViewsProvider.viewType, myAnalysisProvider));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import * as vscode from 'vscode';
import { TraceViewerPanel } from '../../trace-viewer-panel/trace-viewer-webview-panel';
import { TraceServerConnectionStatusService } from '../../utils/trace-server-status';
import { getTraceServerUrl, getTspClientUrl } from '../../utils/tspClient';
import { convertSignalExperiment } from 'vscode-trace-extension/src/common/signal-converter';

Expand All @@ -24,6 +25,7 @@ export class TraceExplorerAvailableViewsProvider implements vscode.WebviewViewPr

constructor(
private readonly _extensionUri: vscode.Uri,
private readonly _statusService: TraceServerConnectionStatusService,
) { }

public resolveWebviewView(
Expand All @@ -47,6 +49,12 @@ export class TraceExplorerAvailableViewsProvider implements vscode.WebviewViewPr
// Handle messages from the webview
webviewView.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'connectionStatus':
if (message.data && message.data.status) {
const status: boolean = JSON.parse(message.data.status);
this._statusService.render(status);
}
return;
case 'webviewReady':
// Post the tspTypescriptClient
webviewView.webview.postMessage({command: 'set-tspClient', data: getTspClientUrl()});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import * as vscode from 'vscode';
import { convertSignalExperiment } from 'vscode-trace-extension/src/common/signal-converter';
import { TraceViewerPanel } from '../../trace-viewer-panel/trace-viewer-webview-panel';
import { TraceServerConnectionStatusService } from '../../utils/trace-server-status';
import { getTraceServerUrl, getTspClientUrl } from '../../utils/tspClient';

const JSONBig = JSONBigConfig({
Expand All @@ -21,6 +22,7 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView

constructor(
private readonly _extensionUri: vscode.Uri,
private readonly _statusService: TraceServerConnectionStatusService,
) {}

private _onOpenedTracesWidgetActivated = (experiment: Experiment): void => this.doHandleTracesWidgetActivatedSignal(experiment);
Expand Down Expand Up @@ -76,6 +78,12 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
// Handle messages from the webview
webviewView.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'connectionStatus':
if (message.data && message.data.status) {
const status: boolean = JSON.parse(message.data.status);
this._statusService.render(status);
}
return;
case 'webviewReady':
// Post the tspTypescriptClient
webviewView.webview.postMessage({command: 'set-tspClient', data: getTspClientUrl()});
Expand All @@ -89,7 +97,7 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
case 'reopenTrace':
if (message.data && message.data.wrapper) {
const experiment = convertSignalExperiment(JSONBig.parse(message.data.wrapper));
const panel = TraceViewerPanel.createOrShow(this._extensionUri, experiment.name);
const panel = TraceViewerPanel.createOrShow(this._extensionUri, experiment.name, this._statusService);
panel.setExperiment(experiment);
signalManager().fireExperimentSelectedSignal(experiment);
}
Expand All @@ -113,6 +121,7 @@ export class TraceExplorerOpenedTracesViewProvider implements vscode.WebviewView
}
}
}, undefined, this._disposables);

signalManager().on(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated);
signalManager().on(Signals.OPENED_TRACES_UPDATED, this._onOpenedTracesChanged);
signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected);
Expand Down
4 changes: 2 additions & 2 deletions vscode-trace-extension/src/trace-explorer/trace-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class Trace extends vscode.TreeItem {
}

export const traceHandler = (analysisTree: AnalysisProvider) => (context: vscode.ExtensionContext, trace: Trace): void => {
const panel = TraceViewerPanel.createOrShow(context.extensionUri, trace.name);
const panel = TraceViewerPanel.createOrShow(context.extensionUri, trace.name, undefined);
(async () => {
const traces = new Array<TspTrace>();
const t = await traceManager.openTrace(trace.uri, trace.name);
Expand Down Expand Up @@ -127,7 +127,7 @@ export const fileHandler = (analysisTree: AnalysisProvider) => async (context: v
return;
}
const name = uri.substring(uri.lastIndexOf('/') + 1);
const panel = TraceViewerPanel.createOrShow(context.extensionUri, name);
const panel = TraceViewerPanel.createOrShow(context.extensionUri, name, undefined);
(async () => {

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { getTspClientUrl, getTraceServerUrl } from '../utils/tspClient';
import { TraceServerConnectionStatusService } from '../utils/trace-server-status';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { handleStatusMessage, handleRemoveMessage, setStatusFromPanel } from '../common/trace-message';
import { signalManager, Signals } from 'traceviewer-base/lib/signals/signal-manager';
Expand Down Expand Up @@ -29,11 +30,13 @@ export class TraceViewerPanel {

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);

public static createOrShow(extensionUri: vscode.Uri, name: string): TraceViewerPanel {
public static createOrShow(extensionUri: vscode.Uri, name: string, statusService: TraceServerConnectionStatusService | undefined): TraceViewerPanel {

const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;

Expand All @@ -43,7 +46,7 @@ export class TraceViewerPanel {
if (openedPanel) {
openedPanel._panel.reveal(column);
} else {
openedPanel = new TraceViewerPanel(extensionUri, column || vscode.ViewColumn.One, name);
openedPanel = new TraceViewerPanel(extensionUri, column || vscode.ViewColumn.One, name, statusService);
TraceViewerPanel.activePanels[name] = openedPanel;
setStatusFromPanel(name);
}
Expand Down Expand Up @@ -96,8 +99,10 @@ export class TraceViewerPanel {
}
}

private constructor(extensionUri: vscode.Uri, column: vscode.ViewColumn, name: string) {
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
Expand Down Expand Up @@ -171,6 +176,12 @@ export class TraceViewerPanel {
TraceViewerPanel.saveTraceCsv(message.payload.data, ((this._experiment !== undefined) ? this._experiment.name : 'trace')+'.csv');
}
return;
case 'connectionStatus':
if (message.data && message.data.status && this._statusService) {
const status: boolean = JSON.parse(message.data.status);
this._statusService.render(status);
}
return;
}
}, undefined, this._disposables);
signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected);
Expand Down
24 changes: 24 additions & 0 deletions vscode-trace-extension/src/utils/trace-server-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { StatusBarItem, ThemeColor } from 'vscode';

export class TraceServerConnectionStatusService {

private statusBarItem: StatusBarItem;

public constructor(statusBarItem: StatusBarItem) {
this.statusBarItem = statusBarItem;
this.statusBarItem.hide();
}

public async render(status: boolean): Promise<void> {
if (status) {
this.statusBarItem.backgroundColor = new ThemeColor('statusBarItem.warningBackground');
this.statusBarItem.text = '$(check) Trace Server';
this.statusBarItem.tooltip = 'Trace Viewer: server found';
} else {
this.statusBarItem.backgroundColor = new ThemeColor('statusBarItem.errorBackground');
this.statusBarItem.text = '$(error) Trace Server';
this.statusBarItem.tooltip = 'Trace Viewer: server not found';
}
this.statusBarItem.show();
}
}
12 changes: 11 additions & 1 deletion vscode-trace-webviews/src/common/tsp-client-provider-impl.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
import { RestClient, ConnectionStatusListener } from 'tsp-typescript-client/lib/protocol/rest-client';
import { ExperimentManager } from 'traceviewer-base/lib/experiment-manager';
import { TraceManager } from 'traceviewer-base/lib/trace-manager';
import { ITspClientProvider } from 'traceviewer-base/lib/tsp-client-provider';
import { VsCodeMessageManager } from './vscode-message-manager';

export class TspClientProvider implements ITspClientProvider {

private _tspClient: TspClient;
private _traceManager: TraceManager;
private _experimentManager: ExperimentManager;
private _signalHandler: VsCodeMessageManager;
private _statusListener: ConnectionStatusListener;
// private _listeners: ((tspClient: TspClient) => void)[];

constructor(traceServerUrl: string
constructor(traceServerUrl: string, signalHandler: VsCodeMessageManager
) {
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._signalHandler.notifyConnection(status);
});
RestClient.addConnectionStatusListener(this._statusListener);
// this._listeners = [];
// tspUrlProvider.addTraceServerUrlChangedListener(url => {
// this._tspClient = new TspClient(url);
Expand Down
7 changes: 6 additions & 1 deletion vscode-trace-webviews/src/common/vscode-message-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export interface VsCodeTraceAction {
}

export class VsCodeMessageManager extends Messages.MessageManager {

constructor() {
super();
}
Expand All @@ -40,9 +39,15 @@ export class VsCodeMessageManager extends Messages.MessageManager {
vscode.postMessage({command: 'webviewReady'});
}

notifyConnection(serverStatus: boolean): void {
const status: string = JSON.stringify(serverStatus);
vscode.postMessage({command: 'connectionStatus', data: { status }});
}

/**************************************************************************
* Trace Explorer React APP
*************************************************************************/

reOpenTrace(experiment: Experiment): void {
const wrapper: string = JSONBig.stringify(experiment);
vscode.postMessage({command: 'reopenTrace', data: {wrapper}});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class TraceExplorerViewsWidget extends React.Component<{}, AvailableViewsAppStat
const message = event.data; // The JSON data our extension sent
switch (message.command) {
case 'set-tspClient':
this.setState({ tspClientProvider: new TspClientProvider(message.data) });
this.setState({ tspClientProvider: new TspClientProvider(message.data, this._signalHandler) });
break;
case 'experimentSelected':
let experiment: Experiment | undefined = undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class TraceExplorerOpenedTraces extends React.Component<{}, OpenedTracesAppState
const message = event.data; // The JSON data our extension sent
switch (message.command) {
case 'set-tspClient':
const tspClientProvider: ITspClientProvider = new TspClientProvider(message.data);
const tspClientProvider: ITspClientProvider = new TspClientProvider(message.data, this._signalHandler);
this._experimentManager = tspClientProvider.getExperimentManager();
this.setState({ tspClientProvider: tspClientProvider });
if (this.state.tspClientProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { TraceContextComponent } from 'traceviewer-react-components/lib/componen
import 'traceviewer-react-components/style/trace-context-style.css';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
import { TspClientProvider } from '../common/tsp-client-provider-impl';
import { VsCodeMessageManager } from '../common/vscode-message-manager';
import { convertSignalExperiment } from '../common/vscode-signal-converter';
import '../style/trace-viewer.css';
Expand All @@ -23,7 +23,7 @@ const JSONBig = JSONBigConfig({

interface VscodeAppState {
experiment: Experiment | undefined;
tspClient: TspClient | undefined;
tspClientProvider: TspClientProvider | undefined;
outputs: OutputDescriptor[];
overviewOutputDescriptor: OutputDescriptor| undefined;
theme: string;
Expand Down Expand Up @@ -66,7 +66,7 @@ class TraceViewerContainer extends React.Component<{}, VscodeAppState> {
super(props);
this.state = {
experiment: undefined,
tspClient: undefined,
tspClientProvider: undefined,
outputs: [],
overviewOutputDescriptor: undefined,
theme: 'light'
Expand All @@ -81,7 +81,7 @@ class TraceViewerContainer extends React.Component<{}, VscodeAppState> {
this.doHandleExperimentSetSignal(convertSignalExperiment(JSONBig.parse(message.data)));
break;
case 'set-tspClient':
this.setState({tspClient: new TspClient(message.data)}, () => {
this.setState({tspClientProvider: new TspClientProvider(message.data, this._signalHandler)}, () => {
if (message.experiment) {
this.doHandleExperimentSetSignal(convertSignalExperiment(JSONBig.parse(message.experiment)));
}
Expand Down Expand Up @@ -168,8 +168,8 @@ class TraceViewerContainer extends React.Component<{}, VscodeAppState> {

protected async getAvailableTraceOverviewOutputDescriptor(experiment: Experiment| undefined): Promise<OutputDescriptor[] | undefined> {
let descriptors: OutputDescriptor[] | undefined;
if (experiment && this.state.tspClient) {
const outputsResponse = await this.state.tspClient.experimentOutputs(experiment.UUID);
if (experiment && this.state.tspClientProvider) {
const outputsResponse = await this.state.tspClientProvider.getTspClient().experimentOutputs(experiment.UUID);
if (outputsResponse && outputsResponse.isOk()) {
descriptors = outputsResponse.getModel();
}
Expand All @@ -181,9 +181,9 @@ class TraceViewerContainer extends React.Component<{}, VscodeAppState> {
public render(): React.ReactNode {
return (
<div className="trace-viewer-container">
{ this.state.experiment && this.state.tspClient && <TraceContextComponent
{ this.state.experiment && this.state.tspClientProvider && <TraceContextComponent
experiment={this.state.experiment}
tspClient={this.state.tspClient}
tspClient={this.state.tspClientProvider.getTspClient()}
outputs={this.state.outputs}
overviewDescriptor={this.state.overviewOutputDescriptor}
markerCategoriesMap={this.selectedMarkerCategoriesMap}
Expand Down

0 comments on commit 8a689e4

Please sign in to comment.