Skip to content

Commit

Permalink
Trace explorer prototype
Browse files Browse the repository at this point in the history
- Show all opened traces
- Show all available outputs for a trace

Signed-off-by: Simon Delisle <simon.delisle@ericsson.com>
  • Loading branch information
delislesim committed Oct 8, 2019
1 parent f6a7b76 commit 8861810
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 23 deletions.
2 changes: 2 additions & 0 deletions viewer-prototype/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
"ag-grid-community": "^19.0.0",
"ag-grid-react": "^19.0.0",
"react-grid-layout": "^0.16.6",
"react-virtualized": "^9.21.0",
"tsp-typescript-client": "https://github.com/theia-ide/tsp-typescript-client"
},
"devDependencies": {
"@types/chart.js": "^2.7.40",
"@types/react-grid-layout": "^0.16.5",
"@types/react-virtualized": "^9.18.12",
"rimraf": "latest",
"typescript": "latest"
},
Expand Down
63 changes: 63 additions & 0 deletions viewer-prototype/src/browser/style/trace-explorer.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.trace-explorer-container {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 1fr 1fr 1fr;
grid-row-gap: 10px;
}

.trace-explorer-opened {
grid-row-start: 1;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 25px 1fr;
}

.trace-explorer-files {
grid-row-start: 2;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 25px 1fr;
}

.trace-explorer-analysis {
grid-row-start: 3;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 25px 1fr;
}

.trace-explorer-panel-title {
grid-row-start: 1;
background-color: rgba(97, 97, 97, 0.5);
color: white;
text-align: center;
line-height: 25px;
}

.trace-explorer-panel-content {
grid-row-start: 2;
border: 1px solid rgba(97, 97, 97, 0.5);
color: white;
padding-inline-start: 5px;
overflow-x: scroll;
white-space: nowrap;
}

.trace-explorer-panel-content>ul {
list-style-type: none;
padding-inline-start: 0px;
}

.trace-list-container, .outputs-list-container {
color: white;
overflow-x: scroll;
white-space: nowrap;
}

.trace-list-name, .outputs-list-name {
font-weight: bold;
}

.trace-list-path, .outputs-list-description {
color: rgb(160, 160, 160);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { AbstractViewContribution } from "@theia/core/lib/browser/shell/view-contribution";
import { TraceExplorerWidget, TRACE_EXPLORER_ID, TRACE_EXPLORER_LABEL } from "./trace-explorer-widget";

export class TraceExplorerContribution extends AbstractViewContribution<TraceExplorerWidget> {
constructor() {
super({
widgetId: TRACE_EXPLORER_ID,
widgetName: TRACE_EXPLORER_LABEL,
defaultWidgetOptions: {
area: 'left'
},
toggleCommandId: 'trace-explorer:toggle'
});
}
}
150 changes: 150 additions & 0 deletions viewer-prototype/src/browser/trace-explorer/trace-explorer-widget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { injectable, inject } from 'inversify';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import * as React from 'react';
import { TraceManager } from '../../common/trace-manager';
import { Trace } from 'tsp-typescript-client/lib/models/trace';
import { List, ListRowProps } from 'react-virtualized';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';

export const TRACE_EXPLORER_ID = 'trace-explorer';
export const TRACE_EXPLORER_LABEL = 'Trace Explorer';

@injectable()
export class TraceExplorerWidget extends ReactWidget {
private OPENED_TRACE_TITLE: string = 'Opened traces';
private FILE_NAVIGATOR_TITLE: string = 'File navigator';
private ANALYSIS_TITLE: string = 'Available analysis';

private openedTraces: Array<Trace> = new Array();
private availableOutputDescriptors: Array<OutputDescriptor> = new Array();

constructor(
@inject(TraceManager) private traceManager: TraceManager,
) {
super();
this.id = TRACE_EXPLORER_ID;
this.title.label = TRACE_EXPLORER_LABEL;
this.toDispose.push(traceManager.traceOpenedSignal(trace => this.onTraceOpened(trace)));
this.toDispose.push(traceManager.traceClosedSignal(trace => this.onTraceClosed(trace)));
this.initialize();
}

private onTraceOpened(openedTrace: Trace) {
this.updateOpenedTraces();
this.updateAvailableAnalysis(openedTrace);
}

private onTraceClosed(closedTrace: Trace) {
this.updateOpenedTraces();
this.updateAvailableAnalysis(undefined);
}

async initialize(): Promise<void> {
this.updateOpenedTraces();
this.updateAvailableAnalysis(undefined);
}

protected render(): React.ReactNode {
this.updateOpenedTraces = this.updateOpenedTraces.bind(this);
this.updateAvailableAnalysis = this.updateAvailableAnalysis.bind(this);
this.traceRowRenderer = this.traceRowRenderer.bind(this);
this.outputsRowRenderer = this.outputsRowRenderer.bind(this);
return <div className='trace-explorer-container'>
<div className='trace-explorer-opened'>
<div className='trace-explorer-panel-title' onClick={this.updateOpenedTraces}>
{this.OPENED_TRACE_TITLE}
</div>
<div className='trace-explorer-panel-content'>
<List
height={300}
width={300}
rowCount={this.openedTraces.length}
rowHeight={50}
rowRenderer={this.traceRowRenderer}/>
</div>
</div>
<div className='trace-explorer-files'>
<div className='trace-explorer-panel-title'>
{this.FILE_NAVIGATOR_TITLE}
</div>
<div className='trace-explorer-panel-content'>
{'List of files'}
</div>
</div>
<div className='trace-explorer-analysis'>
<div className='trace-explorer-panel-title'>
{this.ANALYSIS_TITLE}
</div>
<div className='trace-explorer-panel-content'>
<List
height={300}
width={300}
rowCount={this.availableOutputDescriptors.length}
rowHeight={50}
rowRenderer={this.outputsRowRenderer} />
</div>
</div>
</div>;
}

private traceRowRenderer(props: ListRowProps): React.ReactNode {
let traceName = '';
let tracePath = '';
if (this.openedTraces && this.openedTraces.length && props.index < this.openedTraces.length) {
traceName = this.openedTraces[props.index].name;
tracePath = this.openedTraces[props.index].path;
}
return <div className='trace-list-container' key={props.key} style={props.style}>
<div className='trace-list-name'>
{traceName}
</div>
<div className='trace-list-path'>
{tracePath}
</div>
</div>;
}

private outputsRowRenderer(props: ListRowProps): React.ReactNode {
let outputName = '';
let outputDescription = '';
if (this.availableOutputDescriptors && this.availableOutputDescriptors.length && props.index < this.availableOutputDescriptors.length) {
outputName = this.availableOutputDescriptors[props.index].name;
outputDescription = this.availableOutputDescriptors[props.index].description;
}
return <div className='outputs-list-container' key={props.key} style={props.style}>
<div className='outputs-list-name'>
{outputName}
</div>
<div className='outputs-list-description'>
{outputDescription}
</div>
</div>;
}

private async updateOpenedTraces() {
this.openedTraces = this.traceManager.getOpenTraces();
this.update();
}

private async updateAvailableAnalysis(trace: Trace | undefined) {
this.availableOutputDescriptors = new Array();
if (trace) {
this.availableOutputDescriptors = await this.getOutputDescriptors(trace);
} else {
if (this.openedTraces.length) {
this.availableOutputDescriptors = await this.getOutputDescriptors(this.openedTraces[0]);
}
}

this.update();
}

private async getOutputDescriptors(trace: Trace): Promise<OutputDescriptor[]> {
const outputDescriptors: OutputDescriptor[] = new Array();
const descriptors = await this.traceManager.getAvailableOutputs(trace.name);
if (descriptors && descriptors.length) {
outputDescriptors.push(...descriptors);
}
return outputDescriptors;
}
}
36 changes: 35 additions & 1 deletion viewer-prototype/src/browser/trace-viewer-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import { ContainerModule, Container } from 'inversify';
import { WidgetFactory, OpenHandler, FrontendApplicationContribution } from '@theia/core/lib/browser';
import { WidgetFactory, OpenHandler, FrontendApplicationContribution, bindViewContribution } from '@theia/core/lib/browser';
import { TraceViewerWidget, TraceViewerWidgetOptions } from './trace-viewer-widget';
import { TraceViewerContribution } from './trace-viewer-contribution';
import { CommandContribution } from '@theia/core/lib/common';
Expand All @@ -25,8 +25,16 @@ import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import '../../src/browser/style/trace-viewer.css';
import '../../src/browser/style/trace-explorer.css';
import { TraceExplorerContribution } from './trace-explorer/trace-explorer-contribution';
import { TRACE_EXPLORER_ID, TraceExplorerWidget } from './trace-explorer/trace-explorer-widget';
import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
import { TraceManager } from '../common/trace-manager';

export default new ContainerModule(bind => {
bind(TspClient).toDynamicValue(() => new TspClient('http://localhost:8080/tsp/api')).inSingletonScope();
bind(TraceManager).toSelf().inSingletonScope();

bind(TraceViewerWidget).toSelf();
bind<WidgetFactory>(WidgetFactory).toDynamicValue(context => ({
id: TraceViewerWidget.ID,
Expand All @@ -43,4 +51,30 @@ export default new ContainerModule(bind => {
[CommandContribution, OpenHandler, FrontendApplicationContribution].forEach(serviceIdentifier =>
bind(serviceIdentifier).toService(TraceViewerContribution)
);

bindViewContribution(bind, TraceExplorerContribution);
bind(TraceExplorerWidget).toSelf();
// bind(FrontendApplicationContribution).toService(TraceExplorerContribution);
bind(WidgetFactory).toDynamicValue(context => ({
id: TRACE_EXPLORER_ID,
createWidget: () => context.container.get<TraceExplorerWidget>(TraceExplorerWidget)
}));

// bindFileNavigatorPreferences(bind);
// bind(FileNavigatorFilter).toSelf().inSingletonScope();

// bind(NavigatorContextKeyService).toSelf().inSingletonScope();

// bindViewContribution(bind, FileNavigatorContribution);
// bind(FrontendApplicationContribution).toService(FileNavigatorContribution);

// bind(KeybindingContext).to(NavigatorActiveContext).inSingletonScope();

// bind(FileNavigatorWidget).toDynamicValue(ctx =>
// createFileNavigatorWidget(ctx.container)
// );
// bind(WidgetFactory).toDynamicValue(context => ({
// id: FILE_NAVIGATOR_ID,
// createWidget: () => context.container.get<FileNavigatorWidget>(FileNavigatorWidget)
// }));
});
9 changes: 3 additions & 6 deletions viewer-prototype/src/browser/trace-viewer-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ export class TraceViewerWidget extends ReactWidget {
static LABEL = 'Trace Viewer';

protected readonly uri: Path;
// protected readonly resource: Resource;
private traceManager: TraceManager;
private tspClient: TspClient;
private openedTrace: Trace | undefined;
private traceInfo: Array<any> = new Array();
private tableColumns: Array<any> = new Array();
Expand All @@ -62,11 +59,11 @@ export class TraceViewerWidget extends ReactWidget {
private XYTitle: string = '';

constructor(
@inject(TraceViewerWidgetOptions) protected readonly options: TraceViewerWidgetOptions
@inject(TraceViewerWidgetOptions) protected readonly options: TraceViewerWidgetOptions,
@inject(TraceManager) private traceManager: TraceManager,
@inject(TspClient) private tspClient: TspClient
) {
super();
this.traceManager = TraceManager.getInstance();
this.tspClient = new TspClient('http://localhost:8080/tsp/api');
this.uri = new Path(this.options.traceURI);
this.id = 'theia-traceOpen';
this.title.label = 'Trace: ' + this.uri.base;
Expand Down
Loading

0 comments on commit 8861810

Please sign in to comment.