@memlab/cli 1.0.7 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/{memlab → memlab.js} +5 -3
- package/dist/BaseCommand.d.ts +1 -1
- package/dist/BaseCommand.js +1 -1
- package/dist/CommandLoader.d.ts +8 -7
- package/dist/CommandLoader.js +8 -1
- package/dist/Dispatcher.d.ts +8 -3
- package/dist/Dispatcher.js +13 -6
- package/dist/TypesThirdParty.d.ts +1 -1
- package/dist/TypesThirdParty.js +1 -1
- package/dist/commands/CleanLoggerDataCommand.d.ts +1 -1
- package/dist/commands/CleanLoggerDataCommand.js +1 -1
- package/dist/commands/CleanRunDataCommand.d.ts +1 -1
- package/dist/commands/CleanRunDataCommand.js +1 -1
- package/dist/commands/GetVersionCommand.d.ts +1 -1
- package/dist/commands/GetVersionCommand.js +2 -2
- package/dist/commands/InitDirectoryCommand.d.ts +1 -1
- package/dist/commands/InitDirectoryCommand.js +1 -1
- package/dist/commands/ListScenariosCommand.d.ts +1 -1
- package/dist/commands/ListScenariosCommand.js +1 -1
- package/dist/commands/MemLabRunCommand.d.ts +1 -1
- package/dist/commands/MemLabRunCommand.js +1 -1
- package/dist/commands/PrintSummaryCommand.d.ts +1 -1
- package/dist/commands/PrintSummaryCommand.js +1 -1
- package/dist/commands/ResetDirectoryCommand.d.ts +1 -1
- package/dist/commands/ResetDirectoryCommand.js +1 -1
- package/dist/commands/RunMeasureCommand.d.ts +1 -1
- package/dist/commands/RunMeasureCommand.js +1 -1
- package/dist/commands/WarmupAppCommand.d.ts +1 -1
- package/dist/commands/WarmupAppCommand.js +1 -1
- package/dist/commands/heap/CheckLeakCommand.d.ts +1 -1
- package/dist/commands/heap/CheckLeakCommand.js +3 -1
- package/dist/commands/heap/GetRetainerTraceCommand.d.ts +1 -1
- package/dist/commands/heap/GetRetainerTraceCommand.js +1 -1
- package/dist/commands/heap/HeapAnalysisCommand.d.ts +1 -1
- package/dist/commands/heap/HeapAnalysisCommand.js +1 -1
- package/dist/commands/heap/HeapAnalysisSubCommandWrapper.d.ts +1 -1
- package/dist/commands/heap/HeapAnalysisSubCommandWrapper.js +1 -1
- package/dist/commands/heap/interactive/InteractiveCommandLoader.d.ts +16 -0
- package/dist/commands/heap/interactive/InteractiveCommandLoader.js +33 -0
- package/dist/commands/heap/interactive/InteractiveHeapCommand.d.ts +28 -0
- package/dist/commands/heap/interactive/InteractiveHeapCommand.js +166 -0
- package/dist/commands/heap/interactive/InteractiveHeapExploreCommand.d.ts +25 -0
- package/dist/commands/heap/interactive/InteractiveHeapExploreCommand.js +169 -0
- package/dist/commands/heap/interactive/ui-components/CliScreen.d.ts +38 -0
- package/dist/commands/heap/interactive/ui-components/CliScreen.js +253 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewController.d.ts +73 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewController.js +358 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewUtils.d.ts +32 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewUtils.js +205 -0
- package/dist/commands/heap/interactive/ui-components/ListComponent.d.ts +65 -0
- package/dist/commands/heap/interactive/ui-components/ListComponent.js +237 -0
- package/dist/commands/helper/GenerateCLIDocCommand.d.ts +1 -1
- package/dist/commands/helper/GenerateCLIDocCommand.js +1 -1
- package/dist/commands/helper/HelperCommand.d.ts +1 -1
- package/dist/commands/helper/HelperCommand.js +17 -5
- package/dist/commands/helper/lib/CommandOrder.d.ts +1 -1
- package/dist/commands/helper/lib/CommandOrder.js +1 -1
- package/dist/commands/helper/lib/Types.d.ts +1 -1
- package/dist/commands/helper/lib/Types.js +1 -1
- package/dist/commands/query/QueryDefaultWorkDirCommand.d.ts +1 -1
- package/dist/commands/query/QueryDefaultWorkDirCommand.js +1 -1
- package/dist/commands/snapshot/CheckXvfbSupportCommand.d.ts +1 -1
- package/dist/commands/snapshot/CheckXvfbSupportCommand.js +1 -1
- package/dist/commands/snapshot/Snapshot.d.ts +1 -1
- package/dist/commands/snapshot/Snapshot.js +1 -1
- package/dist/commands/snapshot/TakeSnapshotCommand.d.ts +1 -1
- package/dist/commands/snapshot/TakeSnapshotCommand.js +1 -1
- package/dist/commands/snapshot/WarmupAndSnapshotCommand.d.ts +1 -1
- package/dist/commands/snapshot/WarmupAndSnapshotCommand.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/options/AppOption.d.ts +1 -1
- package/dist/options/AppOption.js +1 -1
- package/dist/options/DebugOption.d.ts +1 -1
- package/dist/options/DebugOption.js +1 -1
- package/dist/options/DisableXvfbOption.d.ts +1 -1
- package/dist/options/DisableXvfbOption.js +1 -1
- package/dist/options/FullExecutionOption.d.ts +1 -1
- package/dist/options/FullExecutionOption.js +1 -1
- package/dist/options/HeadfulBrowserOption.d.ts +1 -1
- package/dist/options/HeadfulBrowserOption.js +1 -1
- package/dist/options/HeapNodeIdOption.d.ts +1 -1
- package/dist/options/HeapNodeIdOption.js +1 -1
- package/dist/options/HelperOption.d.ts +1 -1
- package/dist/options/HelperOption.js +1 -1
- package/dist/options/InteractionOption.d.ts +1 -1
- package/dist/options/InteractionOption.js +1 -1
- package/dist/options/MLClusteringLinkageMaxDistanceOption.d.ts +1 -1
- package/dist/options/MLClusteringLinkageMaxDistanceOption.js +1 -1
- package/dist/options/MLClusteringMaxDFOption.d.ts +18 -0
- package/dist/options/MLClusteringMaxDFOption.js +47 -0
- package/dist/options/MLClusteringOption.d.ts +1 -1
- package/dist/options/MLClusteringOption.js +1 -1
- package/dist/options/NumberOfRunsOption.d.ts +1 -1
- package/dist/options/NumberOfRunsOption.js +1 -1
- package/dist/options/RemoteBrowserDebugOption.d.ts +1 -1
- package/dist/options/RemoteBrowserDebugOption.js +1 -1
- package/dist/options/RunningModeOption.d.ts +1 -1
- package/dist/options/RunningModeOption.js +1 -1
- package/dist/options/ScenarioFileOption.d.ts +1 -1
- package/dist/options/ScenarioFileOption.js +1 -1
- package/dist/options/SetContinuousTestOption.d.ts +1 -1
- package/dist/options/SetContinuousTestOption.js +1 -1
- package/dist/options/SetDeviceOption.d.ts +1 -1
- package/dist/options/SetDeviceOption.js +1 -1
- package/dist/options/SetWorkingDirectoryOption.d.ts +1 -1
- package/dist/options/SetWorkingDirectoryOption.js +1 -1
- package/dist/options/SilentOption.d.ts +1 -1
- package/dist/options/SilentOption.js +1 -1
- package/dist/options/SkipExtraOperationOption.d.ts +1 -1
- package/dist/options/SkipExtraOperationOption.js +1 -1
- package/dist/options/SkipGCOption.d.ts +1 -1
- package/dist/options/SkipGCOption.js +1 -1
- package/dist/options/SkipScreenshotOption.d.ts +1 -1
- package/dist/options/SkipScreenshotOption.js +1 -1
- package/dist/options/SkipScrollOption.d.ts +1 -1
- package/dist/options/SkipScrollOption.js +1 -1
- package/dist/options/SkipSnapshotOption.d.ts +1 -1
- package/dist/options/SkipSnapshotOption.js +1 -1
- package/dist/options/SkipWarmupOption.d.ts +1 -1
- package/dist/options/SkipWarmupOption.js +1 -1
- package/dist/options/VerboseOption.d.ts +1 -1
- package/dist/options/VerboseOption.js +1 -1
- package/dist/options/heap/BaselineFileOption.d.ts +1 -1
- package/dist/options/heap/BaselineFileOption.js +1 -1
- package/dist/options/heap/FinalFileOption.d.ts +1 -1
- package/dist/options/heap/FinalFileOption.js +1 -1
- package/dist/options/heap/JSEngineOption.d.ts +1 -1
- package/dist/options/heap/JSEngineOption.js +1 -1
- package/dist/options/heap/LeakClusterSizeThresholdOption.d.ts +1 -1
- package/dist/options/heap/LeakClusterSizeThresholdOption.js +1 -1
- package/dist/options/heap/LogTraceAsClusterOption.d.ts +1 -1
- package/dist/options/heap/LogTraceAsClusterOption.js +1 -1
- package/dist/options/heap/OversizeThresholdOption.d.ts +1 -1
- package/dist/options/heap/OversizeThresholdOption.js +1 -1
- package/dist/options/heap/SnapshotDirectoryOption.d.ts +1 -1
- package/dist/options/heap/SnapshotDirectoryOption.js +1 -1
- package/dist/options/heap/SnapshotFileOption.d.ts +1 -1
- package/dist/options/heap/SnapshotFileOption.js +1 -1
- package/dist/options/heap/TargetFileOption.d.ts +1 -1
- package/dist/options/heap/TargetFileOption.js +1 -1
- package/dist/options/heap/TraceAllObjectsOption.d.ts +1 -1
- package/dist/options/heap/TraceAllObjectsOption.js +1 -1
- package/dist/options/heap/leak-filter/LeakFilterFileOption.d.ts +1 -1
- package/dist/options/heap/leak-filter/LeakFilterFileOption.js +1 -1
- package/dist/options/heap/leak-filter/examples/FilterLib.d.ts +1 -1
- package/dist/options/heap/leak-filter/examples/FilterLib.js +1 -1
- package/dist/options/heap/leak-filter/examples/dup-string-as-leak.example-1.d.ts +1 -1
- package/dist/options/heap/leak-filter/examples/dup-string-as-leak.example-1.js +1 -1
- package/dist/options/heap/leak-filter/examples/dup-string-as-leak.example-2.d.ts +1 -1
- package/dist/options/heap/leak-filter/examples/dup-string-as-leak.example-2.js +1 -1
- package/dist/options/lib/UniversalOptions.d.ts +1 -1
- package/dist/options/lib/UniversalOptions.js +1 -1
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall ws_labs
|
|
9
|
+
*/
|
|
10
|
+
import type { IHeapSnapshot, IHeapNode } from '@memlab/core';
|
|
11
|
+
import type ListComponent from './ListComponent';
|
|
12
|
+
import { ComponentDataItem, ComponentData } from './HeapViewUtils';
|
|
13
|
+
declare type SelectHeapObjectOption = {
|
|
14
|
+
noChangeInReferenceBox?: boolean;
|
|
15
|
+
noChangeInReferrerBox?: boolean;
|
|
16
|
+
noChangeInRetainerTraceBox?: boolean;
|
|
17
|
+
noChangeInObjectPropertyBox?: boolean;
|
|
18
|
+
componentDataItem?: ComponentDataItem;
|
|
19
|
+
};
|
|
20
|
+
export declare type ObjectCategory = Map<string, ComponentDataItem[]>;
|
|
21
|
+
/**
|
|
22
|
+
* HeapViewController managers all the data associated with each
|
|
23
|
+
* UI components in CLI and coordinates the events/interaction
|
|
24
|
+
* among all UI components.
|
|
25
|
+
*/
|
|
26
|
+
export default class HeapViewController {
|
|
27
|
+
private currentHeapObject;
|
|
28
|
+
private selectedHeapObject;
|
|
29
|
+
private currentHeapObjectsInfo;
|
|
30
|
+
private currentClusteredObjectsInfo;
|
|
31
|
+
private componentIdToDataMap;
|
|
32
|
+
private componentIdToComponentMap;
|
|
33
|
+
private heap;
|
|
34
|
+
private clusteredBox;
|
|
35
|
+
private referrerBox;
|
|
36
|
+
private objectBox;
|
|
37
|
+
private referenceBox;
|
|
38
|
+
private objectPropertyBox;
|
|
39
|
+
private retainerTracePropertyBox;
|
|
40
|
+
constructor(heap: IHeapSnapshot, objectCategory: ObjectCategory);
|
|
41
|
+
private getFlattenHeapObjectsInfo;
|
|
42
|
+
private getFlattenClusteredObjectsInfo;
|
|
43
|
+
private shouldClusterCategory;
|
|
44
|
+
private clusterComponentDataItems;
|
|
45
|
+
getContent(componentId: number): string[];
|
|
46
|
+
setClusteredBox(component: ListComponent): void;
|
|
47
|
+
getClusteredBoxData(): ComponentData;
|
|
48
|
+
setReferrerBox(component: ListComponent): void;
|
|
49
|
+
getReferrerBoxData(node?: IHeapNode): ComponentData;
|
|
50
|
+
setObjectBox(component: ListComponent): void;
|
|
51
|
+
getObjectBoxData(): ComponentData;
|
|
52
|
+
setReferenceBox(component: ListComponent): void;
|
|
53
|
+
getReferenceBoxData(): ComponentData;
|
|
54
|
+
setObjectPropertyBox(component: ListComponent): void;
|
|
55
|
+
getObjectPropertyData(options?: {
|
|
56
|
+
details?: Map<string, string>;
|
|
57
|
+
}): ComponentData;
|
|
58
|
+
private getReadableString;
|
|
59
|
+
private getKeyValuePairString;
|
|
60
|
+
setRetainerTraceBox(component: ListComponent): void;
|
|
61
|
+
getRetainerTraceData(): ComponentData;
|
|
62
|
+
setCurrentHeapObjectFromComponent(componentId: number, itemIndex: number, options?: {
|
|
63
|
+
skipFocus?: boolean;
|
|
64
|
+
}): void;
|
|
65
|
+
setCurrentHeapObject(node: IHeapNode, options?: {
|
|
66
|
+
skipFocus?: boolean;
|
|
67
|
+
}): void;
|
|
68
|
+
focusOnComponent(componentId: number): void;
|
|
69
|
+
setSelectedHeapObjectFromComponent(componentId: number, itemIndex: number): void;
|
|
70
|
+
setSelectedHeapObject(node: IHeapNode, options?: SelectHeapObjectOption): void;
|
|
71
|
+
}
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=HeapViewController.d.ts.map
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const core_1 = require("@memlab/core");
|
|
8
|
+
const HeapViewUtils_1 = require("./HeapViewUtils");
|
|
9
|
+
/**
|
|
10
|
+
* HeapViewController managers all the data associated with each
|
|
11
|
+
* UI components in CLI and coordinates the events/interaction
|
|
12
|
+
* among all UI components.
|
|
13
|
+
*/
|
|
14
|
+
class HeapViewController {
|
|
15
|
+
constructor(heap, objectCategory) {
|
|
16
|
+
this.heap = heap;
|
|
17
|
+
this.currentHeapObjectsInfo =
|
|
18
|
+
this.getFlattenHeapObjectsInfo(objectCategory);
|
|
19
|
+
this.currentClusteredObjectsInfo =
|
|
20
|
+
this.getFlattenClusteredObjectsInfo(objectCategory);
|
|
21
|
+
this.currentHeapObject = (0, HeapViewUtils_1.getHeapObjectAt)(this.currentHeapObjectsInfo, 0);
|
|
22
|
+
this.componentIdToDataMap = new Map();
|
|
23
|
+
this.componentIdToComponentMap = new Map();
|
|
24
|
+
}
|
|
25
|
+
getFlattenHeapObjectsInfo(objectCategory) {
|
|
26
|
+
let ret = [];
|
|
27
|
+
for (const category of objectCategory.keys()) {
|
|
28
|
+
const nodes = objectCategory.get(category);
|
|
29
|
+
ret = [...ret, ...nodes];
|
|
30
|
+
}
|
|
31
|
+
return ret;
|
|
32
|
+
}
|
|
33
|
+
getFlattenClusteredObjectsInfo(objectCategory) {
|
|
34
|
+
let ret = [];
|
|
35
|
+
for (const category of objectCategory.keys()) {
|
|
36
|
+
let nodes = objectCategory.get(category);
|
|
37
|
+
if (this.shouldClusterCategory(category)) {
|
|
38
|
+
nodes = this.clusterComponentDataItems(nodes);
|
|
39
|
+
}
|
|
40
|
+
ret = [...ret, ...nodes];
|
|
41
|
+
}
|
|
42
|
+
return ret;
|
|
43
|
+
}
|
|
44
|
+
shouldClusterCategory(category) {
|
|
45
|
+
return category === 'detached';
|
|
46
|
+
}
|
|
47
|
+
clusterComponentDataItems(nodes) {
|
|
48
|
+
const ret = [];
|
|
49
|
+
const nodeIds = new Set(nodes
|
|
50
|
+
.filter(node => node.heapObject)
|
|
51
|
+
.map(node => { var _a, _b; return (_b = (_a = node.heapObject) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : -1; }));
|
|
52
|
+
const clusters = core_1.analysis.clusterHeapObjects(nodeIds, this.heap);
|
|
53
|
+
clusters.forEach(cluster => {
|
|
54
|
+
var _a;
|
|
55
|
+
const id = (_a = cluster.id) !== null && _a !== void 0 ? _a : -1;
|
|
56
|
+
let node = null;
|
|
57
|
+
node = this.heap.getNodeById(id);
|
|
58
|
+
const details = new Map();
|
|
59
|
+
if (cluster.count) {
|
|
60
|
+
details.set('# of clusters', `${cluster.count}`);
|
|
61
|
+
}
|
|
62
|
+
if (cluster.retainedSize) {
|
|
63
|
+
details.set('aggregated retained size', `${core_1.utils.getReadableBytes(cluster.retainedSize)}`);
|
|
64
|
+
}
|
|
65
|
+
ret.push({ tag: 'Cluster', heapObject: node, details });
|
|
66
|
+
});
|
|
67
|
+
return ret;
|
|
68
|
+
}
|
|
69
|
+
getContent(componentId) {
|
|
70
|
+
const ret = [];
|
|
71
|
+
const data = this.componentIdToDataMap.get(componentId);
|
|
72
|
+
if (data) {
|
|
73
|
+
for (const item of data.items) {
|
|
74
|
+
ret.push(HeapViewUtils_1.ComponentDataItem.getTextForDisplay(item));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return ret;
|
|
78
|
+
}
|
|
79
|
+
setClusteredBox(component) {
|
|
80
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
81
|
+
this.clusteredBox = component;
|
|
82
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
83
|
+
}
|
|
84
|
+
getClusteredBoxData() {
|
|
85
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
86
|
+
data.selectedIdx = 0;
|
|
87
|
+
data.items = this.currentClusteredObjectsInfo;
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
setReferrerBox(component) {
|
|
91
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
92
|
+
this.referrerBox = component;
|
|
93
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
94
|
+
}
|
|
95
|
+
getReferrerBoxData(node = this.selectedHeapObject) {
|
|
96
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
97
|
+
node.forEachReferrer(ref => {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
const tag = ref.fromNode.id === ((_b = (_a = node.pathEdge) === null || _a === void 0 ? void 0 : _a.fromNode) === null || _b === void 0 ? void 0 : _b.id) ? { tag: '<-' } : {};
|
|
100
|
+
data.items.push(Object.assign({ heapObject: ref.fromNode, referenceEdge: ref }, tag));
|
|
101
|
+
return { stop: false };
|
|
102
|
+
});
|
|
103
|
+
data.selectedIdx = data.items.length > 0 ? 0 : -1;
|
|
104
|
+
return data;
|
|
105
|
+
}
|
|
106
|
+
setObjectBox(component) {
|
|
107
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
108
|
+
this.objectBox = component;
|
|
109
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
110
|
+
}
|
|
111
|
+
getObjectBoxData() {
|
|
112
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
113
|
+
const index = this.currentHeapObjectsInfo.findIndex(item => { var _a; return ((_a = item.heapObject) === null || _a === void 0 ? void 0 : _a.id) === this.currentHeapObject.id; });
|
|
114
|
+
if (index >= 0) {
|
|
115
|
+
data.selectedIdx = index;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
data.selectedIdx = 0;
|
|
119
|
+
this.currentHeapObjectsInfo.unshift({
|
|
120
|
+
tag: 'Chosen',
|
|
121
|
+
heapObject: this.currentHeapObject,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
data.items = this.currentHeapObjectsInfo;
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
setReferenceBox(component) {
|
|
128
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
129
|
+
this.referenceBox = component;
|
|
130
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
131
|
+
}
|
|
132
|
+
getReferenceBoxData() {
|
|
133
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
134
|
+
this.selectedHeapObject.forEachReference(ref => {
|
|
135
|
+
data.items.push({ referrerEdge: ref, heapObject: ref.toNode });
|
|
136
|
+
return { stop: false };
|
|
137
|
+
});
|
|
138
|
+
data.items.sort((i1, i2) => { var _a, _b, _c, _d; return ((_b = (_a = i2.heapObject) === null || _a === void 0 ? void 0 : _a.retainedSize) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = i1.heapObject) === null || _c === void 0 ? void 0 : _c.retainedSize) !== null && _d !== void 0 ? _d : 0); });
|
|
139
|
+
data.selectedIdx = data.items.length > 0 ? 0 : -1;
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
setObjectPropertyBox(component) {
|
|
143
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
144
|
+
this.objectPropertyBox = component;
|
|
145
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
146
|
+
}
|
|
147
|
+
getObjectPropertyData(options = {}) {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
150
|
+
const node = this.selectedHeapObject;
|
|
151
|
+
data.items.push({
|
|
152
|
+
stringContent: this.getKeyValuePairString('id', `@${node.id}`),
|
|
153
|
+
});
|
|
154
|
+
data.items.push({
|
|
155
|
+
stringContent: this.getKeyValuePairString('name', node.name),
|
|
156
|
+
});
|
|
157
|
+
data.items.push({
|
|
158
|
+
stringContent: this.getKeyValuePairString('type', node.type),
|
|
159
|
+
});
|
|
160
|
+
data.items.push({
|
|
161
|
+
stringContent: this.getKeyValuePairString('self size', core_1.utils.getReadableBytes(node.self_size)),
|
|
162
|
+
});
|
|
163
|
+
data.items.push({
|
|
164
|
+
stringContent: this.getKeyValuePairString('retained size', core_1.utils.getReadableBytes(node.retainedSize)),
|
|
165
|
+
});
|
|
166
|
+
data.items.push({
|
|
167
|
+
stringContent: this.getKeyValuePairString('# of references', node.edge_count),
|
|
168
|
+
});
|
|
169
|
+
data.items.push({
|
|
170
|
+
stringContent: this.getKeyValuePairString('# of referrers', node.referrers.length),
|
|
171
|
+
});
|
|
172
|
+
if (node.dominatorNode) {
|
|
173
|
+
data.items.push({
|
|
174
|
+
stringContent: 'dominator node' + chalk_1.default.grey(': '),
|
|
175
|
+
heapObject: node.dominatorNode,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
179
|
+
const self = this;
|
|
180
|
+
(_a = options.details) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => data.items.push({
|
|
181
|
+
stringContent: self.getKeyValuePairString(key, value),
|
|
182
|
+
}));
|
|
183
|
+
// inject additional node information
|
|
184
|
+
if (node.isString) {
|
|
185
|
+
const stringNode = node.toStringNode();
|
|
186
|
+
if (stringNode) {
|
|
187
|
+
const value = this.getReadableString(stringNode.stringValue);
|
|
188
|
+
data.items.push({
|
|
189
|
+
stringContent: this.getKeyValuePairString('string value', value),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (node.type === 'number') {
|
|
194
|
+
data.items.push({
|
|
195
|
+
stringContent: this.getKeyValuePairString('numeric value', (_b = core_1.utils.getNumberNodeValue(node)) !== null && _b !== void 0 ? _b : '<error>'),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (node.type === 'closure') {
|
|
199
|
+
const contextNode = node.getReferenceNode('context', 'internal');
|
|
200
|
+
if (contextNode) {
|
|
201
|
+
contextNode.forEachReference(edge => {
|
|
202
|
+
data.items.push({
|
|
203
|
+
tag: chalk_1.default.grey('Scope Variable'),
|
|
204
|
+
referrerEdge: edge,
|
|
205
|
+
heapObject: edge.toNode,
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
data.selectedIdx = data.items.length > 0 ? 0 : -1;
|
|
211
|
+
return data;
|
|
212
|
+
}
|
|
213
|
+
getReadableString(value) {
|
|
214
|
+
return value.length > 300
|
|
215
|
+
? value.substring(0, 300) + chalk_1.default.grey('...')
|
|
216
|
+
: value;
|
|
217
|
+
}
|
|
218
|
+
getKeyValuePairString(key, value) {
|
|
219
|
+
return key + chalk_1.default.grey(': ') + chalk_1.default.green(value);
|
|
220
|
+
}
|
|
221
|
+
setRetainerTraceBox(component) {
|
|
222
|
+
this.componentIdToComponentMap.set(component.id, component);
|
|
223
|
+
this.retainerTracePropertyBox = component;
|
|
224
|
+
this.componentIdToDataMap.set(component.id, new HeapViewUtils_1.ComponentData());
|
|
225
|
+
}
|
|
226
|
+
getRetainerTraceData() {
|
|
227
|
+
var _a;
|
|
228
|
+
const data = new HeapViewUtils_1.ComponentData();
|
|
229
|
+
const node = this.selectedHeapObject;
|
|
230
|
+
let curNode = node;
|
|
231
|
+
while (curNode && !core_1.utils.isRootNode(curNode)) {
|
|
232
|
+
if (!curNode.pathEdge) {
|
|
233
|
+
curNode = null;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
data.items.unshift({ referrerEdge: curNode.pathEdge, heapObject: curNode });
|
|
237
|
+
curNode = (_a = curNode.pathEdge) === null || _a === void 0 ? void 0 : _a.fromNode;
|
|
238
|
+
}
|
|
239
|
+
if (curNode) {
|
|
240
|
+
data.items.unshift({ heapObject: curNode });
|
|
241
|
+
}
|
|
242
|
+
data.selectedIdx = data.items.length > 0 ? 0 : -1;
|
|
243
|
+
return data;
|
|
244
|
+
}
|
|
245
|
+
setCurrentHeapObjectFromComponent(componentId, itemIndex, options = {}) {
|
|
246
|
+
const data = this.componentIdToDataMap.get(componentId);
|
|
247
|
+
if (!data) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const item = data.items[itemIndex];
|
|
251
|
+
if (!item) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const heapObject = item.heapObject;
|
|
255
|
+
if (!heapObject) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.setCurrentHeapObject(heapObject, options);
|
|
259
|
+
}
|
|
260
|
+
setCurrentHeapObject(node, options = {}) {
|
|
261
|
+
this.currentHeapObject = node;
|
|
262
|
+
// set parent box's data and content
|
|
263
|
+
const clusteredBoxData = this.getClusteredBoxData();
|
|
264
|
+
this.componentIdToDataMap.set(this.clusteredBox.id, clusteredBoxData);
|
|
265
|
+
this.clusteredBox.setContent(this.getContent(this.clusteredBox.id));
|
|
266
|
+
this.clusteredBox.selectIndex(clusteredBoxData.selectedIdx);
|
|
267
|
+
// set object box's data and content
|
|
268
|
+
const objectBoxData = this.getObjectBoxData();
|
|
269
|
+
this.componentIdToDataMap.set(this.objectBox.id, objectBoxData);
|
|
270
|
+
this.objectBox.setContent(this.getContent(this.objectBox.id));
|
|
271
|
+
this.objectBox.selectIndex(objectBoxData.selectedIdx);
|
|
272
|
+
this.setSelectedHeapObject(node);
|
|
273
|
+
if (!options.skipFocus) {
|
|
274
|
+
this.focusOnComponent(this.objectBox.id);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
focusOnComponent(componentId) {
|
|
278
|
+
var _a;
|
|
279
|
+
for (const component of this.componentIdToComponentMap.values()) {
|
|
280
|
+
if (component.id === componentId) {
|
|
281
|
+
component.focus();
|
|
282
|
+
const data = this.componentIdToDataMap.get(componentId);
|
|
283
|
+
const selectIndex = (_a = (data && data.selectedIdx)) !== null && _a !== void 0 ? _a : -1;
|
|
284
|
+
this.setSelectedHeapObjectFromComponent(componentId, selectIndex);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
component.loseFocus();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
setSelectedHeapObjectFromComponent(componentId, itemIndex) {
|
|
292
|
+
const data = this.componentIdToDataMap.get(componentId);
|
|
293
|
+
if (!data) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
data.selectedIdx = itemIndex;
|
|
297
|
+
const item = data.items[itemIndex];
|
|
298
|
+
if (!item) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const heapObject = item.heapObject;
|
|
302
|
+
if (!heapObject) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// if selecting in a specific box, do not update content in that box
|
|
306
|
+
const noChangeInReferenceBox = componentId === this.referenceBox.id;
|
|
307
|
+
const noChangeInReferrerBox = componentId === this.referrerBox.id;
|
|
308
|
+
const noChangeInRetainerTraceBox = componentId === this.retainerTracePropertyBox.id;
|
|
309
|
+
const noChangeInObjectPropertyBox = componentId === this.objectPropertyBox.id;
|
|
310
|
+
this.setSelectedHeapObject(heapObject, {
|
|
311
|
+
noChangeInReferenceBox,
|
|
312
|
+
noChangeInReferrerBox,
|
|
313
|
+
noChangeInRetainerTraceBox,
|
|
314
|
+
noChangeInObjectPropertyBox,
|
|
315
|
+
componentDataItem: item,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
setSelectedHeapObject(node, options = {}) {
|
|
319
|
+
var _a;
|
|
320
|
+
this.selectedHeapObject = node;
|
|
321
|
+
// set referrer box's data and content
|
|
322
|
+
if (!options.noChangeInReferrerBox) {
|
|
323
|
+
const data = this.getReferrerBoxData();
|
|
324
|
+
this.componentIdToDataMap.set(this.referrerBox.id, data);
|
|
325
|
+
this.referrerBox.setContent(this.getContent(this.referrerBox.id));
|
|
326
|
+
this.referrerBox.selectIndex(data.selectedIdx);
|
|
327
|
+
this.referrerBox.setLabel(`Referrers of @${node.id}`);
|
|
328
|
+
}
|
|
329
|
+
// set reference box's data and content
|
|
330
|
+
if (!options.noChangeInReferenceBox) {
|
|
331
|
+
const data = this.getReferenceBoxData();
|
|
332
|
+
this.componentIdToDataMap.set(this.referenceBox.id, data);
|
|
333
|
+
this.referenceBox.setContent(this.getContent(this.referenceBox.id));
|
|
334
|
+
this.referenceBox.selectIndex(data.selectedIdx);
|
|
335
|
+
this.referenceBox.setLabel(`References of @${node.id}`);
|
|
336
|
+
}
|
|
337
|
+
// set object property box's data and content
|
|
338
|
+
if (!options.noChangeInObjectPropertyBox) {
|
|
339
|
+
const propertyOption = ((_a = options === null || options === void 0 ? void 0 : options.componentDataItem) === null || _a === void 0 ? void 0 : _a.details)
|
|
340
|
+
? { details: options.componentDataItem.details }
|
|
341
|
+
: {};
|
|
342
|
+
const data = this.getObjectPropertyData(propertyOption);
|
|
343
|
+
this.componentIdToDataMap.set(this.objectPropertyBox.id, data);
|
|
344
|
+
this.objectPropertyBox.setContent(this.getContent(this.objectPropertyBox.id));
|
|
345
|
+
this.objectPropertyBox.selectIndex(data.selectedIdx);
|
|
346
|
+
this.objectPropertyBox.setLabel(`Object: @${node.id}`);
|
|
347
|
+
}
|
|
348
|
+
// set retainer trace box's data and content
|
|
349
|
+
if (!options.noChangeInRetainerTraceBox) {
|
|
350
|
+
const data = this.getRetainerTraceData();
|
|
351
|
+
this.componentIdToDataMap.set(this.retainerTracePropertyBox.id, data);
|
|
352
|
+
this.retainerTracePropertyBox.setContent(this.getContent(this.retainerTracePropertyBox.id));
|
|
353
|
+
this.retainerTracePropertyBox.selectIndex(data.selectedIdx);
|
|
354
|
+
this.retainerTracePropertyBox.setLabel(`Retainer Trace of @${node.id}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
exports.default = HeapViewController;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall ws_labs
|
|
9
|
+
*/
|
|
10
|
+
import type { IHeapEdge, IHeapNode } from '@memlab/core';
|
|
11
|
+
export declare class ComponentDataItem {
|
|
12
|
+
stringContent?: string;
|
|
13
|
+
tag?: string;
|
|
14
|
+
referrerEdge?: IHeapEdge;
|
|
15
|
+
heapObject?: IHeapNode;
|
|
16
|
+
referenceEdge?: IHeapEdge;
|
|
17
|
+
type?: string;
|
|
18
|
+
details?: Map<string, string>;
|
|
19
|
+
static getTextForDisplay(data: ComponentDataItem): string;
|
|
20
|
+
private static getTextContent;
|
|
21
|
+
}
|
|
22
|
+
export declare class ComponentData {
|
|
23
|
+
selectedIdx: number;
|
|
24
|
+
items: ComponentDataItem[];
|
|
25
|
+
}
|
|
26
|
+
export declare function throwIfNodesEmpty(nodes: ComponentDataItem[]): boolean;
|
|
27
|
+
export declare function getHeapObjectAt(nodes: ComponentDataItem[], index: number): IHeapNode;
|
|
28
|
+
export declare function substringWithColor(input: string, begin: number): string;
|
|
29
|
+
export declare type DebounceCallback = () => void;
|
|
30
|
+
export declare type DebounceFunction = (callback: DebounceCallback) => void;
|
|
31
|
+
export declare function debounce(timeInMs: number): DebounceFunction;
|
|
32
|
+
//# sourceMappingURL=HeapViewUtils.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.debounce = exports.substringWithColor = exports.getHeapObjectAt = exports.throwIfNodesEmpty = exports.ComponentData = exports.ComponentDataItem = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const core_1 = require("@memlab/core");
|
|
9
|
+
const lessUsefulEdgeTypeForDebugging = new Set([
|
|
10
|
+
'internal',
|
|
11
|
+
'hidden',
|
|
12
|
+
'shortcut',
|
|
13
|
+
'weak',
|
|
14
|
+
]);
|
|
15
|
+
const reactEdgeNames = new Set([
|
|
16
|
+
'alternate',
|
|
17
|
+
'firstEffect',
|
|
18
|
+
'lastEffect',
|
|
19
|
+
'concurrentQueues',
|
|
20
|
+
'child',
|
|
21
|
+
'return',
|
|
22
|
+
'sibling',
|
|
23
|
+
]);
|
|
24
|
+
function isUsefulEdgeForDebugging(edge) {
|
|
25
|
+
if (lessUsefulEdgeTypeForDebugging.has(edge.type)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const edgeStr = `${edge.name_or_index}`;
|
|
29
|
+
if (reactEdgeNames.has(edgeStr)) {
|
|
30
|
+
if (core_1.utils.isFiberNode(edge.fromNode) || core_1.utils.isFiberNode(edge.toNode)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (edgeStr.startsWith('__reactProps$')) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const lessUsefulObjectTypeForDebugging = new Set([
|
|
40
|
+
'native',
|
|
41
|
+
'hidden',
|
|
42
|
+
'array',
|
|
43
|
+
'code',
|
|
44
|
+
'synthetic',
|
|
45
|
+
]);
|
|
46
|
+
function isUsefulObjectForDebugging(object) {
|
|
47
|
+
if (lessUsefulObjectTypeForDebugging.has(object.type)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return !core_1.utils.isFiberNode(object);
|
|
51
|
+
}
|
|
52
|
+
class ComponentDataItem {
|
|
53
|
+
static getTextForDisplay(data) {
|
|
54
|
+
const content = ComponentDataItem.getTextContent(data);
|
|
55
|
+
if (data.referrerEdge && isUsefulEdgeForDebugging(data.referrerEdge)) {
|
|
56
|
+
return content;
|
|
57
|
+
}
|
|
58
|
+
if (data.referenceEdge && isUsefulEdgeForDebugging(data.referenceEdge)) {
|
|
59
|
+
return content;
|
|
60
|
+
}
|
|
61
|
+
if (data.heapObject && isUsefulObjectForDebugging(data.heapObject)) {
|
|
62
|
+
return content;
|
|
63
|
+
}
|
|
64
|
+
if (!data.referenceEdge && !data.heapObject && !data.referrerEdge) {
|
|
65
|
+
return content;
|
|
66
|
+
}
|
|
67
|
+
return chalk_1.default.grey(content);
|
|
68
|
+
}
|
|
69
|
+
static getTextContent(data) {
|
|
70
|
+
let ret = '';
|
|
71
|
+
if (data.tag) {
|
|
72
|
+
ret += `[${data.tag}] `;
|
|
73
|
+
}
|
|
74
|
+
if (data.stringContent) {
|
|
75
|
+
ret += data.stringContent;
|
|
76
|
+
}
|
|
77
|
+
const arrowPrefix = chalk_1.default.grey('--');
|
|
78
|
+
const arrowSuffix = chalk_1.default.grey('---') + '>';
|
|
79
|
+
if (data.referrerEdge) {
|
|
80
|
+
const edgeType = chalk_1.default.grey(`(${data.referrerEdge.type})`);
|
|
81
|
+
const edgeName = data.referrerEdge.name_or_index;
|
|
82
|
+
ret += `${arrowPrefix}${edgeName}${edgeType}${arrowSuffix} `;
|
|
83
|
+
}
|
|
84
|
+
if (data.heapObject) {
|
|
85
|
+
const objectType = chalk_1.default.grey(`(${data.heapObject.type})`);
|
|
86
|
+
const objectId = chalk_1.default.grey(` @${data.heapObject.id}`);
|
|
87
|
+
const size = core_1.utils.getReadableBytes(data.heapObject.retainedSize);
|
|
88
|
+
const sizeInfo = chalk_1.default.grey(' [') + chalk_1.default.bold(chalk_1.default.blue(size)) + chalk_1.default.grey(']');
|
|
89
|
+
ret +=
|
|
90
|
+
chalk_1.default.green('[') +
|
|
91
|
+
(isUsefulObjectForDebugging(data.heapObject)
|
|
92
|
+
? chalk_1.default.green(data.heapObject.name)
|
|
93
|
+
: chalk_1.default.bold(chalk_1.default.grey(data.heapObject.name))) +
|
|
94
|
+
chalk_1.default.green(']') +
|
|
95
|
+
objectType +
|
|
96
|
+
objectId +
|
|
97
|
+
sizeInfo;
|
|
98
|
+
}
|
|
99
|
+
if (data.referenceEdge) {
|
|
100
|
+
const edgeType = chalk_1.default.grey(`(${data.referenceEdge.type})`);
|
|
101
|
+
const edgeName = data.referenceEdge.name_or_index;
|
|
102
|
+
ret += ` ${arrowPrefix}${edgeName}${edgeType}${arrowSuffix} `;
|
|
103
|
+
}
|
|
104
|
+
return ret === '' ? chalk_1.default.grey('<undefinied>') : ret;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.ComponentDataItem = ComponentDataItem;
|
|
108
|
+
class ComponentData {
|
|
109
|
+
constructor() {
|
|
110
|
+
this.selectedIdx = -1;
|
|
111
|
+
this.items = [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.ComponentData = ComponentData;
|
|
115
|
+
function throwIfNodesEmpty(nodes) {
|
|
116
|
+
if (nodes.length === 0) {
|
|
117
|
+
throw core_1.utils.haltOrThrow('no heap node specified');
|
|
118
|
+
}
|
|
119
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
120
|
+
if (!nodes[i].heapObject) {
|
|
121
|
+
throw core_1.utils.haltOrThrow('heap node missing in ComponentDataItem[]');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
exports.throwIfNodesEmpty = throwIfNodesEmpty;
|
|
127
|
+
function getHeapObjectAt(nodes, index) {
|
|
128
|
+
throwIfNodesEmpty(nodes);
|
|
129
|
+
if (index < 0 || index >= nodes.length) {
|
|
130
|
+
throw core_1.utils.haltOrThrow('index is outside of nodes range');
|
|
131
|
+
}
|
|
132
|
+
return nodes[index].heapObject;
|
|
133
|
+
}
|
|
134
|
+
exports.getHeapObjectAt = getHeapObjectAt;
|
|
135
|
+
// eslint-disable-next-line no-control-regex
|
|
136
|
+
const colorBegin = /^\u001b\[(\d+)m/;
|
|
137
|
+
// eslint-disable-next-line no-control-regex
|
|
138
|
+
const colorEnd = /^\u001b\](\d+)m/;
|
|
139
|
+
function stripColorCodeIfAny(input) {
|
|
140
|
+
const matchBegin = input.match(colorBegin);
|
|
141
|
+
const matchEnd = input.match(colorEnd);
|
|
142
|
+
const match = matchBegin || matchEnd;
|
|
143
|
+
if (!match) {
|
|
144
|
+
return { str: input, code: -1, isBegin: false };
|
|
145
|
+
}
|
|
146
|
+
const isBegin = !!matchBegin;
|
|
147
|
+
const code = parseInt(match[1], 10);
|
|
148
|
+
const str = input.substring(match[0].length);
|
|
149
|
+
return { str, code, isBegin };
|
|
150
|
+
}
|
|
151
|
+
function toColorControlChar(code, isBegin) {
|
|
152
|
+
const colorSpecialChar = '\u001b';
|
|
153
|
+
return colorSpecialChar + (isBegin ? '[' : ']') + code + 'm';
|
|
154
|
+
}
|
|
155
|
+
function substringWithColor(input, begin) {
|
|
156
|
+
const codeQueue = [];
|
|
157
|
+
let curIndex = 0;
|
|
158
|
+
let curStr = input;
|
|
159
|
+
while (curIndex < begin) {
|
|
160
|
+
// enqueue all control characters
|
|
161
|
+
let strip;
|
|
162
|
+
do {
|
|
163
|
+
strip = stripColorCodeIfAny(curStr);
|
|
164
|
+
curStr = strip.str;
|
|
165
|
+
if (strip.code >= 0) {
|
|
166
|
+
// pop if control begin meets control ends
|
|
167
|
+
const last = codeQueue[codeQueue.length - 1];
|
|
168
|
+
if (!last ||
|
|
169
|
+
last.code !== strip.code ||
|
|
170
|
+
strip.isBegin === true ||
|
|
171
|
+
last.isBegin === false) {
|
|
172
|
+
codeQueue.push({ code: strip.code, isBegin: strip.isBegin });
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
codeQueue.pop();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} while (strip.code >= 0);
|
|
179
|
+
// strip one actual content character
|
|
180
|
+
curStr = curStr.substring(1);
|
|
181
|
+
++curIndex;
|
|
182
|
+
}
|
|
183
|
+
// prepend control characters
|
|
184
|
+
while (codeQueue.length > 0) {
|
|
185
|
+
const last = codeQueue.pop();
|
|
186
|
+
if (last) {
|
|
187
|
+
curStr = toColorControlChar(last === null || last === void 0 ? void 0 : last.code, last === null || last === void 0 ? void 0 : last.isBegin) + curStr;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return curStr;
|
|
191
|
+
}
|
|
192
|
+
exports.substringWithColor = substringWithColor;
|
|
193
|
+
function debounce(timeInMs) {
|
|
194
|
+
let id = null;
|
|
195
|
+
return (callback) => {
|
|
196
|
+
if (id) {
|
|
197
|
+
clearTimeout(id);
|
|
198
|
+
}
|
|
199
|
+
id = setTimeout(() => {
|
|
200
|
+
callback();
|
|
201
|
+
id = null;
|
|
202
|
+
}, timeInMs);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
exports.debounce = debounce;
|