Skip to content

Commit 313300a

Browse files
PoC - add libraries from theia-trace-extension
This is a quick proof-of-concept, of bringing the following libraries, from repo theia-trace-extension, to vscode-trace-extension: - traceviewer-base - traceviewer-react-components Utility script typescript-references.js was also brought along. Note: the commit history for the components were not preserved in this PoC. Signed-off-by: Marc Dumais <[email protected]>
1 parent 3b8a612 commit 313300a

File tree

112 files changed

+20797
-88
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+20797
-88
lines changed

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
{
22
"private": true,
33
"scripts": {
4-
"prepare": "lerna run prepare",
4+
"prepare": "yarn build",
5+
"build": "yarn -s tsref && yarn -s tsbuild",
6+
"tsref": "node scripts/typescript-references.js",
7+
"tsbuild": "tsc -b",
58
"watch": "lerna exec --stream --parallel -- \"yarn run watch\"",
69
"clean": "lerna run clean",
710
"vsce:package": "lerna run vsce:package",
@@ -35,11 +38,16 @@
3538
"webpack-cli": "^4.5.0"
3639
},
3740
"workspaces": [
41+
"packages/base",
42+
"packages/react-components",
3843
"vscode-trace-common",
3944
"vscode-trace-webviews",
4045
"vscode-trace-extension"
46+
4147
],
4248
"resolutions": {
43-
"@vscode/vsce": "2.25.0"
49+
"@vscode/vsce": "2.25.0",
50+
"@types/react": "18.3.8",
51+
"@types/lodash": "4.17.14"
4452
}
4553
}

packages/base/.eslintrc.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/** @type {import('eslint').Linter.Config} */
2+
module.exports = {
3+
root: true,
4+
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
5+
parserOptions: {
6+
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
7+
sourceType: "module", // Allows for the use of imports
8+
tsconfigRootDir: __dirname,
9+
project: 'tsconfig.json',
10+
projectFolderIgnoreList: [
11+
'/lib/'
12+
]
13+
},
14+
extends: [
15+
'plugin:@typescript-eslint/recommended',
16+
'../../configs/base.eslintrc.json',
17+
'../../configs/warnings.eslintrc.json',
18+
'../../configs/errors.eslintrc.json'
19+
],
20+
ignorePatterns: [
21+
'node_modules',
22+
'lib',
23+
'.eslintrc.js',
24+
'plugins'
25+
]
26+
};

packages/base/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2019, 2021 Ericsson and others
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19+
OR OTHER DEALINGS IN THE SOFTWARE.

packages/base/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Description
2+
3+
The Trace Viewer base package contains trace management utilities for managing traces using Trace Server applications that implement the Trace Server Protocol (TSP). While being initially used within the Theia Trace Viewer extension, its code base is independent to any Theia APIs and hence can be integrated in other web applications.
4+
5+
## Additional Information
6+
7+
- [Theia Trace Viewer Extension git repository](https://github.com/eclipse-cdt-cloud/theia-trace-extension)
8+
- [Trace Server Protocol git repository](https://github.com/eclipse-cdt-cloud/trace-server-protocol)
9+
- [Reference Trace Server - Download (Eclipse Trace Compass)](https://download.eclipse.org/tracecompass.incubator/trace-server/rcp/)

packages/base/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "traceviewer-base",
3+
"version": "0.7.2",
4+
"description": "Trace Viewer base package, contains trace management utilities",
5+
"license": "MIT",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/eclipse-cdt-cloud/theia-trace-extension"
9+
},
10+
"bugs": {
11+
"url": "https://github.com/eclipse-cdt-cloud/theia-trace-extension/issues"
12+
},
13+
"homepage": "https://github.com/eclipse-cdt-cloud/theia-trace-extension",
14+
"files": [
15+
"lib",
16+
"src"
17+
],
18+
"dependencies": {
19+
"tsp-typescript-client": "^0.6.0"
20+
},
21+
"devDependencies": {
22+
"@typescript-eslint/eslint-plugin": "^3.4.0",
23+
"@typescript-eslint/parser": "^3.4.0",
24+
"eslint": "^7.3.0",
25+
"eslint-plugin-import": "^2.21.2",
26+
"eslint-plugin-no-null": "^1.0.2",
27+
"eslint-plugin-react": "^7.20.0",
28+
"rimraf": "^5.0.0",
29+
"typescript": "4.9.5"
30+
},
31+
"scripts": {
32+
"build": "tsc -b",
33+
"clean": "rimraf lib *.tsbuildinfo",
34+
"lint": "eslint .",
35+
"lint:fix": "eslint . --fix",
36+
"test": "echo 'test'",
37+
"watch": "tsc -w",
38+
"format:write": "prettier --write ./src",
39+
"format:check": "prettier --check ./src"
40+
}
41+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { Trace } from 'tsp-typescript-client/lib/models/trace';
2+
import { ITspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
3+
import { Query } from 'tsp-typescript-client/lib/models/query/query';
4+
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
5+
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
6+
import { TraceManager } from './trace-manager';
7+
import { TspClientResponse } from 'tsp-typescript-client/lib/protocol/tsp-client-response';
8+
import { signalManager } from './signals/signal-manager';
9+
10+
export class ExperimentManager {
11+
private fOpenExperiments: Map<string, Experiment> = new Map();
12+
private fTspClient: ITspClient;
13+
private fTraceManager: TraceManager;
14+
15+
constructor(tspClient: ITspClient, traceManager: TraceManager) {
16+
this.fTspClient = tspClient;
17+
this.fTraceManager = traceManager;
18+
signalManager().on('EXPERIMENT_DELETED', (experiment: Experiment) => this.onExperimentDeleted(experiment));
19+
}
20+
21+
/**
22+
* Get an array of opened experiments
23+
* @returns Array of experiment
24+
*/
25+
async getOpenedExperiments(): Promise<Experiment[]> {
26+
const openedExperiments: Array<Experiment> = [];
27+
// Look on the server for opened experiments
28+
const experimentsResponse = await this.fTspClient.fetchExperiments();
29+
const experiments = experimentsResponse.getModel();
30+
if (experimentsResponse.isOk() && experiments) {
31+
openedExperiments.push(...experiments);
32+
}
33+
return openedExperiments;
34+
}
35+
36+
/**
37+
* Get a specific experiment information
38+
* @param experimentUUID experiment UUID
39+
*/
40+
async getExperiment(experimentUUID: string): Promise<Experiment | undefined> {
41+
// Check if the experiment is in "cache"
42+
let experiment = this.fOpenExperiments.get(experimentUUID);
43+
44+
// If the experiment is undefined, check on the server
45+
if (!experiment) {
46+
const experimentResponse = await this.fTspClient.fetchExperiment(experimentUUID);
47+
if (experimentResponse.isOk()) {
48+
experiment = experimentResponse.getModel();
49+
}
50+
}
51+
return experiment;
52+
}
53+
54+
/**
55+
* Get an array of OutputDescriptor for a given experiment
56+
* @param experimentUUID experiment UUID
57+
*/
58+
async getAvailableOutputs(experimentUUID: string): Promise<OutputDescriptor[] | undefined> {
59+
const outputsResponse = await this.fTspClient.experimentOutputs(experimentUUID);
60+
if (outputsResponse && outputsResponse.isOk()) {
61+
return outputsResponse.getModel();
62+
}
63+
return undefined;
64+
}
65+
66+
/**
67+
* Open a given experiment on the server
68+
* @param experimentURI experiment URI to open
69+
* @param experimentName Optional name for the experiment. If not specified the URI name is used
70+
* @returns The opened experiment
71+
*/
72+
async openExperiment(experimentName: string, traces: Array<Trace>): Promise<Experiment | undefined> {
73+
const name = experimentName;
74+
75+
const traceURIs = new Array<string>();
76+
for (let i = 0; i < traces.length; i++) {
77+
traceURIs.push(traces[i].UUID);
78+
}
79+
80+
const tryCreate = async function (
81+
tspClient: ITspClient,
82+
retry: number
83+
): Promise<TspClientResponse<Experiment>> {
84+
return tspClient.createExperiment(
85+
new Query({
86+
name: retry === 0 ? name : name + '(' + retry + ')',
87+
traces: traceURIs
88+
})
89+
);
90+
};
91+
let tryNb = 0;
92+
let experimentResponse: TspClientResponse<Experiment> | undefined;
93+
while (experimentResponse === undefined || experimentResponse.getStatusCode() === 409) {
94+
experimentResponse = await tryCreate(this.fTspClient, tryNb);
95+
tryNb++;
96+
}
97+
const experiment = experimentResponse.getModel();
98+
if (experimentResponse.isOk() && experiment) {
99+
this.addExperiment(experiment);
100+
signalManager().emit('EXPERIMENT_OPENED', experiment);
101+
return experiment;
102+
}
103+
// TODO Handle any other experiment open errors
104+
return undefined;
105+
}
106+
107+
/**
108+
* Update the experiment with the latest info from the server.
109+
* @param experimentUUID experiment UUID
110+
* @returns The updated experiment or undefined if the experiment failed to update
111+
*/
112+
async updateExperiment(experimentUUID: string): Promise<Experiment | undefined> {
113+
const experimentResponse = await this.fTspClient.fetchExperiment(experimentUUID);
114+
const experiment = experimentResponse.getModel();
115+
if (experiment && experimentResponse.isOk()) {
116+
this.fOpenExperiments.set(experimentUUID, experiment);
117+
return experiment;
118+
}
119+
return undefined;
120+
}
121+
122+
/**
123+
* Delete the given experiment from the server
124+
* @param experimentUUID experiment UUID
125+
*/
126+
async deleteExperiment(experimentUUID: string): Promise<void> {
127+
const experimentToDelete = this.fOpenExperiments.get(experimentUUID);
128+
if (experimentToDelete) {
129+
await this.fTspClient.deleteExperiment(experimentUUID);
130+
const deletedExperiment = this.removeExperiment(experimentUUID);
131+
if (deletedExperiment) {
132+
signalManager().emit('EXPERIMENT_DELETED', deletedExperiment);
133+
}
134+
}
135+
}
136+
137+
private onExperimentDeleted(experiment: Experiment) {
138+
/*
139+
* TODO: Do not close traces used by another experiment
140+
*/
141+
// Close each trace
142+
const traces = experiment.traces;
143+
for (let i = 0; i < traces.length; i++) {
144+
this.fTraceManager.deleteTrace(traces[i].UUID);
145+
}
146+
}
147+
148+
public addExperiment(experiment: Experiment): void {
149+
this.fOpenExperiments.set(experiment.UUID, experiment);
150+
experiment.traces.forEach(trace => {
151+
this.fTraceManager.addTrace(trace);
152+
});
153+
}
154+
155+
private removeExperiment(experimentUUID: string): Experiment | undefined {
156+
const deletedExperiment = this.fOpenExperiments.get(experimentUUID);
157+
this.fOpenExperiments.delete(experimentUUID);
158+
return deletedExperiment;
159+
}
160+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
import { ITspClient } from 'tsp-typescript-client';
4+
import { HttpTspClient } from 'tsp-typescript-client/lib/protocol/http-tsp-client';
5+
6+
/**
7+
* Hack!
8+
* The `LazyTspClient` replaces _every_ method with an asynchronous one.
9+
* Only keep methods, discard properties.
10+
*/
11+
export type LazyTspClient = {
12+
[K in keyof ITspClient]: ITspClient[K] extends (...args: infer A) => infer R | Promise<infer R>
13+
? (...args: A) => Promise<R>
14+
: never; // Discard property.
15+
};
16+
17+
export type LazyTspClientFactory = typeof LazyTspClientFactory;
18+
export function LazyTspClientFactory(provider: () => Promise<string>): ITspClient {
19+
// All methods from the `HttpTspClient` are asynchronous. The `LazyTspClient`
20+
// will just delay each call to its methods by first awaiting for the
21+
// asynchronous `baseUrl` resolution to then get a valid `HttpTspClient`.
22+
23+
// Save the current HttpTspClient and the URL used for it.
24+
let tspClient: HttpTspClient;
25+
let lastUrl: string;
26+
// eslint-disable-next-line no-null/no-null
27+
return new Proxy(Object.create(null), {
28+
get(target, property, _receiver) {
29+
let method = target[property];
30+
if (!method) {
31+
target[property] = method = async (...args: any[]) => {
32+
tspClient = await provider().then(baseUrl => {
33+
// If the url has not been updated keep the same client.
34+
if (lastUrl === baseUrl) {
35+
return tspClient;
36+
}
37+
// If the url has changed save it and create a new client.
38+
lastUrl = baseUrl;
39+
return new HttpTspClient(baseUrl);
40+
});
41+
return (tspClient as any)[property](...args);
42+
};
43+
}
44+
return method;
45+
}
46+
}) as LazyTspClient as ITspClient;
47+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export enum MessageCategory {
2+
TRACE_CONTEXT,
3+
SERVER_MESSAGE,
4+
SERVER_STATUS
5+
}
6+
7+
export enum MessageSeverity {
8+
ERROR,
9+
WARNING,
10+
INFO,
11+
DEBUG
12+
}
13+
14+
export interface StatusMessage {
15+
text: string;
16+
category?: MessageCategory;
17+
severity?: MessageSeverity;
18+
}
19+
20+
export declare interface MessageManager {
21+
addStatusMessage(messageKey: string, message: StatusMessage): void;
22+
removeStatusMessage(messageKey: string): void;
23+
}
24+
25+
export class MessageManager implements MessageManager {
26+
addStatusMessage(
27+
messageKey: string,
28+
{ text, category = MessageCategory.SERVER_MESSAGE, severity = MessageSeverity.INFO }: StatusMessage
29+
): void {
30+
console.log('New status message', messageKey, text, category, severity);
31+
}
32+
33+
removeStatusMessage(messageKey: string): void {
34+
console.log('Removing status message status message', messageKey);
35+
}
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
2+
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
3+
4+
export class AvailableViewsChangedSignalPayload {
5+
private _availableOutputDescriptors: OutputDescriptor[];
6+
private _experiment: Experiment;
7+
8+
constructor(availableOutputDescriptors: OutputDescriptor[], experiment: Experiment) {
9+
this._availableOutputDescriptors = availableOutputDescriptors;
10+
this._experiment = experiment;
11+
}
12+
13+
public getAvailableOutputDescriptors(): OutputDescriptor[] {
14+
return this._availableOutputDescriptors;
15+
}
16+
17+
public getExperiment(): Experiment {
18+
return this._experiment;
19+
}
20+
}

0 commit comments

Comments
 (0)