Skip to content

Commit 9476781

Browse files
committed
feat: add split view for realtime graph
1 parent b2d7424 commit 9476781

File tree

8 files changed

+103
-32
lines changed

8 files changed

+103
-32
lines changed

packages/vscode-js-profile-flame/package.json

+11
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@
5252
"command": "vscode-js-profile-flame.setRealtimeCharts",
5353
"when": "view == vscode-js-profile-flame.realtime",
5454
"group": "navigation@1"
55+
},
56+
{
57+
"command": "vscode-js-profile-flame.toggleSplitCharts",
58+
"when": "view == vscode-js-profile-flame.realtime",
59+
"group": "navigation@1"
5560
}
5661
]
5762
},
@@ -61,6 +66,12 @@
6166
"category": "Debug",
6267
"title": "Toggle Realtime Performance Charts",
6368
"icon": "$(gear)"
69+
},
70+
{
71+
"command": "vscode-js-profile-flame.toggleSplitCharts",
72+
"category": "Debug",
73+
"title": "Toggle realtime split charts",
74+
"icon": "$(split-vertical)"
6475
}
6576
],
6677
"configuration": [

packages/vscode-js-profile-flame/src/extension.ts

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function activate(context: vscode.ExtensionContext) {
5050
const settings = readRealtimeSettings(context);
5151
const quickpick = vscode.window.createQuickPick<{ label: string; index: number }>();
5252

53+
quickpick.title = 'Toggle visible performance charts';
5354
quickpick.canSelectMany = true;
5455
quickpick.items = metrics.map((metric, i) => ({
5556
label: metric.name(),
@@ -74,6 +75,11 @@ export function activate(context: vscode.ExtensionContext) {
7475

7576
realtimeTracker.setEnabledMetrics(chosen);
7677
}),
78+
79+
vscode.commands.registerCommand('vscode-js-profile-flame.toggleSplitCharts', () => {
80+
const settings = readRealtimeSettings(context);
81+
realtimeTracker.setSplitCharts(!settings.splitCharts);
82+
}),
7783
);
7884
}
7985

packages/vscode-js-profile-flame/src/realtime/chart.css

+12
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@
7979
font-size: 0.8em;
8080
}
8181

82+
.maxContainer.split {
83+
position: static;
84+
}
85+
86+
.maxContainer.split .max {
87+
position: absolute;
88+
}
89+
90+
.maxContainer.split .max::before {
91+
content: none !important;
92+
}
93+
8294
.max {
8395
display: inline;
8496
}

packages/vscode-js-profile-flame/src/realtime/chart.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,12 @@ export class Chart {
182182
val.parentElement?.removeChild(val);
183183
}
184184

185+
maxContainer.classList[this.settings.value.splitCharts ? 'add' : 'remove'](styles.split);
185186
labelList.innerHTML = '';
186187
this.valElements = [];
187188

188-
for (const metric of this.settings.enabledMetrics) {
189+
for (let i = 0; i < this.settings.enabledMetrics.length; i++) {
190+
const metric = this.settings.enabledMetrics[i];
189191
const label = document.createElement('span');
190192
label.style.setProperty('--metric-color', this.settings.metricColor(metric));
191193
label.classList.add(styles.primary);
@@ -199,6 +201,7 @@ export class Chart {
199201
const max = document.createElement('div');
200202
max.classList.add(styles.max, styles.primary);
201203
max.innerText = metric.format(metric.maxY);
204+
max.style.top = `${(i / this.settings.enabledMetrics.length) * 100}%`;
202205
maxContainer.appendChild(max);
203206

204207
this.valElements.push([metric, { max, val }]);

packages/vscode-js-profile-flame/src/realtime/frameCanvas.ts

+49-23
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { Canvas } from './canvas';
88
export const enum Sizing {
99
LabelHeight = 18,
1010
RulerWidth = 1,
11+
LineWidth = 1,
1112
Easing = 200,
13+
SplitSpacing = 3,
1214
}
1315

1416
const rulers = 4;
@@ -18,6 +20,7 @@ export class FrameCanvas extends Canvas {
1820
private paths: [Path2D, string][] = [];
1921
private ease?: { raf: number; dx: number };
2022
private rmSettingListener = this.settings.onChange(() => this.redraw());
23+
private metricRanges = this.getMetricYRanges();
2124

2225
/**
2326
* Redraws the max values
@@ -29,10 +32,7 @@ export class FrameCanvas extends Canvas {
2932
easeLength += this.ease.dx;
3033
}
3134

32-
this.paths = this.settings.enabledMetrics.map(metric => [
33-
this.createMetricPath(metric),
34-
this.settings.metricColor(metric),
35-
]);
35+
this.paths = this.createMetricPaths();
3636

3737
if (!shouldEase) {
3838
this.drawGraph();
@@ -74,6 +74,7 @@ export class FrameCanvas extends Canvas {
7474

7575
protected redraw() {
7676
this.rulers = this.createRulers();
77+
this.metricRanges = this.getMetricYRanges();
7778
this.updateMetrics(false);
7879
}
7980

@@ -107,43 +108,68 @@ export class FrameCanvas extends Canvas {
107108

108109
private createRulers() {
109110
const path = new Path2D();
110-
const { width, height } = this;
111-
const step = (height - Sizing.RulerWidth) / rulers;
112-
113-
let y = step;
114-
for (let i = 0; i < rulers; i++) {
115-
const targetY = Math.floor(y) + Sizing.RulerWidth / 2;
116-
path.moveTo(0, targetY);
117-
path.lineTo(width, targetY);
118-
y += step;
111+
112+
const ranges = this.settings.value.splitCharts ? this.getMetricYRanges() : [[0, this.height]];
113+
for (const [y1, y2] of ranges) {
114+
const step = (y2 - y1) / rulers;
115+
let y = y1 + step;
116+
for (let i = 0; i < rulers; i++) {
117+
const targetY = Math.floor(y) - Sizing.RulerWidth / 2;
118+
path.moveTo(0, targetY);
119+
path.lineTo(this.width, targetY);
120+
y += step;
121+
}
119122
}
120123

121124
return path;
122125
}
123126

124-
private createMetricPath({ maxY, metrics }: Metric) {
125-
const { width, height } = this;
127+
private getMetricYRanges(): [number, number][] {
128+
const metrics = this.settings.enabledMetrics;
129+
if (!this.settings.value.splitCharts) {
130+
return metrics.map(() => [0, this.height]);
131+
}
132+
133+
const yStep = this.height / metrics.length;
134+
return metrics.map((_, i) => [
135+
Math.ceil(yStep * i + (i > 0 ? Sizing.SplitSpacing / 2 : 0)),
136+
Math.floor(yStep * (i + 1) - (i < metrics.length - 1 ? Sizing.SplitSpacing / 2 : 0)),
137+
]);
138+
}
139+
140+
private createMetricPaths(): [Path2D, string][] {
141+
return this.settings.enabledMetrics.map((metric, i) => [
142+
this.createMetricPath(metric, this.metricRanges[i][0], this.metricRanges[i][1]),
143+
this.settings.metricColor(metric),
144+
]);
145+
}
146+
147+
private createMetricPath({ maxY, metrics }: Metric, y1: number, y2: number) {
148+
const width = this.width;
149+
const lineBaseY = y2 - Sizing.LineWidth / 2;
126150
const stepSize = width / this.settings.steps;
127151
const path = new Path2D();
128152

129153
if (metrics.length === 0) {
130-
path.moveTo(0, height - 1);
131-
path.lineTo(width, height - 1);
154+
path.moveTo(0, lineBaseY);
155+
path.lineTo(width, lineBaseY);
132156
return path;
133157
}
134158

135159
let x = width;
136-
path.moveTo(x, height * (1 - metrics[metrics.length - 1] / maxY));
160+
path.moveTo(x, getY(y1, lineBaseY, 1 - metrics[metrics.length - 1] / maxY));
137161

138162
for (let i = metrics.length - 2; i >= 0; i--) {
139163
x -= stepSize;
140-
path.lineTo(x, height * (1 - metrics[i] / maxY));
164+
path.lineTo(x, getY(y1, lineBaseY, 1 - metrics[i] / maxY));
141165
}
142166

143-
path.lineTo(x - stepSize, height - 1);
144-
path.lineTo(-stepSize, height - 1);
145-
path.lineTo(-stepSize, height + 2);
146-
path.lineTo(width, height + 2);
167+
path.lineTo(x - stepSize, lineBaseY);
168+
path.lineTo(-stepSize, lineBaseY);
169+
path.lineTo(width, lineBaseY);
147170
return path;
148171
}
149172
}
173+
174+
const getY = (y1: number, y2: number, value: number) =>
175+
y1 + (y2 - y1) * Math.max(0, Math.min(1, value));

packages/vscode-js-profile-flame/src/realtime/protocol.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
export interface ISettings {
66
enabledMetrics: number[];
7+
splitCharts: boolean;
78
viewDuration: number;
89
pollInterval: number;
910
zoomLevel: number;

packages/vscode-js-profile-flame/src/realtime/settings.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export class Settings {
1818
viewDuration: 30_000,
1919
zoomLevel: 0,
2020
enabledMetrics: [],
21+
splitCharts: false,
2122
easing: true,
2223
};
2324

packages/vscode-js-profile-flame/src/realtimeSessionTracker.ts

+19-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const readRealtimeSettings = (context: vscode.ExtensionContext): ISetting
2121
config.get(Config.Easing) ??
2222
vscode.window.activeColorTheme.kind !== vscode.ColorThemeKind.HighContrast,
2323
enabledMetrics: context.workspaceState.get(enabledMetricsKey) ?? [0, 1],
24+
splitCharts: context.workspaceState.get(splitChartsKey) ?? false,
2425
pollInterval: config.get(Config.PollInterval, 1000),
2526
viewDuration: config.get(Config.ViewDuration, 30_000),
2627
zoomLevel: config.get('window.zoomLevel', 0),
@@ -34,6 +35,7 @@ interface ISessionData {
3435
}
3536

3637
const enabledMetricsKey = 'enabledMetrics';
38+
const splitChartsKey = 'splitCharts';
3739

3840
/**
3941
* Tracks ongoing debug sessions and webviews. While there's any visible
@@ -63,6 +65,15 @@ export class RealtimeSessionTracker {
6365
this.updateSettings();
6466
}
6567

68+
/**
69+
* Configures whether multiple metrics are shown on the same chart, or
70+
* different charts.
71+
*/
72+
public setSplitCharts(split: boolean) {
73+
this.context.workspaceState.update(splitChartsKey, split);
74+
this.updateSettings();
75+
}
76+
6677
/**
6778
* Adds a webview to the session tracking.
6879
*/
@@ -193,15 +204,15 @@ export class RealtimeSessionTracker {
193204
webview.postMessage(this.getSettingsUpdate());
194205

195206
const data = this.displayedSession && this.sessionData.get(this.displayedSession);
196-
if (!data) {
197-
return;
207+
if (data) {
208+
const metrics: ToWebViewMessage = {
209+
type: MessageType.ApplyData,
210+
data: data.metrics.map(m => m.metrics),
211+
};
212+
webview.postMessage(metrics);
213+
} else {
214+
this.onDidChangeActiveSession(vscode.debug.activeDebugSession);
198215
}
199-
200-
const metrics: ToWebViewMessage = {
201-
type: MessageType.ApplyData,
202-
data: data.metrics.map(m => m.metrics),
203-
};
204-
webview.postMessage(metrics);
205216
}
206217

207218
private getSettingsUpdate() {

0 commit comments

Comments
 (0)