Skip to content

Commit b702150

Browse files
committed
refactor: clean up code lens generics and use components instead of hooks
1 parent 10e98fb commit b702150

File tree

9 files changed

+220
-308
lines changed

9 files changed

+220
-308
lines changed

packages/vscode-js-profile-core/src/cpu/cpuProfileAnnotations.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export interface IProfileInformation {
1919
* up-front for very large profiles turned out to be costly (mainly in path)
2020
* manipulation.
2121
*/
22-
export class CpuProfileAnnotations extends ProfileAnnotations<IProfileInformation, ILocation> {
22+
export class CpuProfileAnnotations extends ProfileAnnotations<ILocation> {
23+
private readonly data = new Map<string, { position: Position; data: IProfileInformation }[]>();
24+
2325
/**
2426
* Adds a new code lens at the given location in the file.
2527
*/

packages/vscode-js-profile-core/src/heap/heapProfileAnnotations.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export interface IProfileInformation {
1818
* up-front for very large profiles turned out to be costly (mainly in path)
1919
* manipulation.
2020
*/
21-
export class HeapProfileAnnotations extends ProfileAnnotations<IProfileInformation, ITreeNode> {
21+
export class HeapProfileAnnotations extends ProfileAnnotations<ITreeNode> {
22+
private readonly data = new Map<string, { position: Position; data: IProfileInformation }[]>();
23+
2224
/**
2325
* Adds a new code lens at the given treeNode in the file.
2426
*/

packages/vscode-js-profile-core/src/profileAnnotations.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@ export const getBasename = (pathOrUrl: string) => basenameRe.exec(pathOrUrl)?.[0
1515
* up-front for very large profiles turned out to be costly (mainly in path)
1616
* manipulation.
1717
*/
18-
export abstract class ProfileAnnotations<DataType, NodeType extends INode> {
18+
export abstract class ProfileAnnotations<TNode extends INode> {
1919
protected readonly basenamesToExpand = new Map<string, (() => void)[]>();
20-
protected readonly data = new Map<string, { position: Position; data: DataType }[]>();
2120

22-
public add(rootPath: string | undefined, node: NodeType) {
21+
public add(rootPath: string | undefined, node: TNode) {
2322
const expand = once(() => {
2423
this.set(
2524
node.callFrame.url,
@@ -70,7 +69,7 @@ export abstract class ProfileAnnotations<DataType, NodeType extends INode> {
7069
/**
7170
* Adds a new code lens at the given location in the file.
7271
*/
73-
protected abstract set(file: string, position: Position, data: NodeType): void;
72+
protected abstract set(file: string, position: Position, data: TNode): void;
7473

7574
/**
7675
* Get all lenses for a file. Ordered by line number.

packages/vscode-js-profile-core/src/profileCodeLensProvider.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { ProfileAnnotations } from './profileAnnotations';
2121
*/
2222
export class ProfileCodeLensProvider implements CodeLensProvider {
2323
private readonly changeEmitter = new EventEmitter<void>();
24-
private lenses?: ProfileAnnotations<{}, INode>;
24+
private lenses?: ProfileAnnotations<INode>;
2525

2626
/**
2727
* @inheritdoc
@@ -31,8 +31,7 @@ export class ProfileCodeLensProvider implements CodeLensProvider {
3131
/**
3232
* Updates the set of lenses currently being displayed.
3333
*/
34-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35-
public registerLenses(lenses: ProfileAnnotations<{}, INode>): Disposable {
34+
public registerLenses(lenses: ProfileAnnotations<INode>): Disposable {
3635
commands.executeCommand('setContext', 'jsProfileVisualizer.hasCodeLenses', true);
3736
this.lenses = lenses;
3837

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import * as ChevronDown from '@vscode/codicons/src/icons/chevron-down.svg';
6+
import * as ChevronRight from '@vscode/codicons/src/icons/chevron-right.svg';
7+
import { FunctionComponent, h } from 'preact';
8+
import { useCallback, useContext } from 'preact/hooks';
9+
import { Icon } from 'vscode-js-profile-core/out/esm/client/icons';
10+
import { classes } from 'vscode-js-profile-core/out/esm/client/util';
11+
import { VsCodeApi } from 'vscode-js-profile-core/out/esm/client/vscodeApi';
12+
import { getNodeText } from 'vscode-js-profile-core/out/esm/common/display';
13+
import { IOpenDocumentMessage } from 'vscode-js-profile-core/out/esm/common/types';
14+
import { IGraphNode } from 'vscode-js-profile-core/out/esm/cpu/model';
15+
import { ITreeNode } from 'vscode-js-profile-core/out/esm/heap/model';
16+
import { IRowProps } from './base-time-view';
17+
import getGlobalUniqueId from './get-global-unique-id';
18+
import styles from './time-view.css';
19+
20+
export const makeBaseTimeViewRow = <T extends IGraphNode | ITreeNode>(): FunctionComponent<
21+
IRowProps<T>
22+
> => ({
23+
node,
24+
depth,
25+
expanded,
26+
position,
27+
onKeyDown: onKeyDownRaw,
28+
onFocus: onFocusRaw,
29+
onExpanded,
30+
children,
31+
}) => {
32+
const vscode = useContext(VsCodeApi);
33+
const onClick = useCallback(
34+
(evt: MouseEvent) =>
35+
vscode.postMessage<IOpenDocumentMessage>({
36+
type: 'openDocument',
37+
callFrame: node.callFrame,
38+
location: (node as IGraphNode).src,
39+
toSide: evt.altKey,
40+
}),
41+
[vscode, node],
42+
);
43+
44+
const onToggleExpand = useCallback(() => onExpanded(!expanded, node), [expanded, node]);
45+
46+
const onKeyDown = useCallback(
47+
(evt: KeyboardEvent) => {
48+
onKeyDownRaw?.(evt, node);
49+
},
50+
[onKeyDownRaw, node],
51+
);
52+
53+
const onFocus = useCallback(() => {
54+
onFocusRaw?.(node);
55+
}, [onFocusRaw, node]);
56+
57+
let root = node;
58+
while (root.parent) {
59+
root = root.parent as T;
60+
}
61+
62+
const expand = (
63+
<span className={styles.expander}>
64+
{node.childrenSize > 0 ? <Icon i={expanded ? ChevronDown : ChevronRight} /> : null}
65+
</span>
66+
);
67+
68+
const location = getNodeText(node)
69+
70+
return (
71+
<div
72+
className={styles.row}
73+
data-row-id={getGlobalUniqueId(node)}
74+
onKeyDown={onKeyDown}
75+
onFocus={onFocus}
76+
onClick={onToggleExpand}
77+
tabIndex={0}
78+
role="treeitem"
79+
aria-posinset={position}
80+
aria-setsize={node.parent?.childrenSize ?? 1}
81+
aria-level={depth + 1}
82+
aria-expanded={expanded}
83+
>
84+
{children}
85+
{!location ? (
86+
<div
87+
className={classes(styles.location, styles.virtual)}
88+
style={{ marginLeft: depth * 15 }}
89+
>
90+
{expand} <span className={styles.fn}>{node.callFrame.functionName}</span>
91+
</div>
92+
) : (
93+
<div className={styles.location} style={{ marginLeft: depth * 15 }}>
94+
{expand} <span className={styles.fn}>{node.callFrame.functionName}</span>
95+
<span className={styles.file}>
96+
<a href="#" onClick={onClick}>
97+
{location}
98+
</a>
99+
</span>
100+
</div>
101+
)}
102+
</div>
103+
);
104+
};

packages/vscode-js-profile-table/src/common/use-time-view.ts renamed to packages/vscode-js-profile-table/src/common/base-time-view.tsx

+60-19
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
* Copyright (C) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------*/
44

5+
import { ComponentChild, ComponentType, Fragment, FunctionComponent, h } from 'preact';
6+
import VirtualList from 'preact-virtual-list';
57
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks';
68
import { addToSet, removeFromSet, toggleInSet } from 'vscode-js-profile-core/out/esm/array';
79
import { ICommonNode } from 'vscode-js-profile-core/out/esm/common/model';
810
import { IGraphNode } from 'vscode-js-profile-core/out/esm/cpu/model';
911
import { ITreeNode } from 'vscode-js-profile-core/out/esm/heap/model';
1012
import { IQueryResults } from 'vscode-js-profile-core/out/esm/ql';
13+
import styles from './time-view.css';
1114
import { SortFn } from './types';
1215

1316
const getGlobalUniqueId = (node: ICommonNode) => {
@@ -19,19 +22,28 @@ const getGlobalUniqueId = (node: ICommonNode) => {
1922
return parts.join('-');
2023
};
2124

22-
const useTimeView = <T extends IGraphNode | ITreeNode>({
23-
data,
24-
query,
25-
initSortFn,
26-
}: {
25+
type NodeAtDepth<T> = { node: T; depth: number; position: number };
26+
27+
export interface IRowProps<T> {
28+
node: T;
29+
depth: number;
30+
position: number;
31+
expanded: boolean;
32+
onExpanded: (isExpanded: boolean, target: T) => void;
33+
onKeyDown: (evt: KeyboardEvent, target: T) => void;
34+
onFocus: (target: T) => void;
35+
}
36+
37+
export const makeBaseTimeView = <T extends IGraphNode | ITreeNode>(): FunctionComponent<{
2738
query: IQueryResults<T>;
2839
data: T[];
29-
initSortFn: SortFn;
30-
}) => {
40+
sortFn: SortFn | undefined;
41+
header: ComponentChild;
42+
row: ComponentType<IRowProps<T>>;
43+
}> => ({ data, header, query, sortFn, row: Row }) => {
3144
type NodeAtDepth = { node: T; depth: number; position: number };
3245

3346
const listRef = useRef<{ base: HTMLElement }>();
34-
const [sortFn, setSortFn] = useState<SortFn | undefined>(() => initSortFn);
3547
const [focused, setFocused] = useState<T | undefined>(undefined);
3648
const [expanded, setExpanded] = useState<ReadonlySet<T>>(new Set());
3749

@@ -148,16 +160,45 @@ const useTimeView = <T extends IGraphNode | ITreeNode>({
148160
});
149161
}, [focused]);
150162

151-
return {
152-
listRef,
153-
rendered,
154-
onKeyDown,
155-
expanded,
156-
setExpanded,
157-
setFocused,
158-
sortFn,
159-
setSortFn,
163+
const onExpanded = (isExpanded: boolean, node: T) => {
164+
setExpanded(prev => {
165+
const next = new Set(prev);
166+
if (isExpanded) {
167+
next.add(node);
168+
} else {
169+
next.delete(node);
170+
}
171+
172+
return next.size !== prev.size ? next : prev;
173+
});
160174
};
161-
};
162175

163-
export default useTimeView;
176+
const renderRow = useCallback(
177+
(row: NodeAtDepth) => (
178+
<Row
179+
onKeyDown={onKeyDown}
180+
node={row.node}
181+
depth={row.depth}
182+
position={row.position}
183+
expanded={expanded.has(row.node)}
184+
onExpanded={onExpanded}
185+
onFocus={setFocused}
186+
/>
187+
),
188+
[expanded, setExpanded, onKeyDown],
189+
);
190+
191+
return (
192+
<Fragment>
193+
{header}
194+
<VirtualList
195+
ref={listRef}
196+
className={styles.rows}
197+
data={rendered}
198+
renderRow={renderRow}
199+
rowHeight={25}
200+
overscanCount={100}
201+
/>
202+
</Fragment>
203+
);
204+
};

packages/vscode-js-profile-table/src/common/use-time-view-row.tsx

-71
This file was deleted.

0 commit comments

Comments
 (0)