Skip to content

Commit 6ce5d66

Browse files
committed
* Updates visualization library.
* Adds support for more js debug adapters. * Minor improvements.
1 parent 6da44bd commit 6ce5d66

File tree

16 files changed

+340
-38
lines changed

16 files changed

+340
-38
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
"typescript.updateImportsOnFileMove.enabled": "prompt",
99
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
1010
"mochaExplorer.require": ["ts-node/register", "source-map-support/register"],
11-
"tasksStatusbar.taskLabelFilter": "dev"
11+
"tasksStatusbar.taskLabelFilter": "dev",
12+
"editor.formatOnSave": true
1213
}

data-extraction/src/CommonDataTypes.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export type GraphEdge = {
107107
label?: string;
108108
id?: string;
109109
color?: string;
110-
dashes?: boolean;
110+
style?: "solid" | "dashed" | "dotted";
111111
};
112112

113113
export type GridVisualizationData = {
@@ -157,12 +157,38 @@ export type MonacoTextVisualizationData = {
157157
* The text to show
158158
*/
159159
text: string;
160+
decorations?: {
161+
range: LineColumnRange;
162+
label?: string;
163+
}[];
160164
/**
161165
* An optional filename that might be used for chosing a syntax highlighter
162166
*/
163167
fileName?: string;
164168
};
165169

170+
export type LineColumnRange = {
171+
/**
172+
* The start position
173+
*/
174+
start: LineColumnPosition;
175+
/**
176+
* The end position
177+
*/
178+
end: LineColumnPosition;
179+
};
180+
181+
export type LineColumnPosition = {
182+
/**
183+
* The 0-based line number
184+
*/
185+
line: number;
186+
/**
187+
* The 0-based column number
188+
*/
189+
column: number;
190+
};
191+
166192
export type MonacoTextDiffVisualizationData = {
167193
kind: {
168194
text: true;

data-extraction/src/js/api/DataExtractorApi.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export interface DataExtractorApi {
2222
getData(
2323
valueFn: () => unknown,
2424
evalFn: <T>(expression: string) => T,
25-
preferredDataExtractorId: string | undefined
25+
preferredDataExtractorId: string | undefined,
26+
variablesInScope: Record<string, unknown>
2627
): JSONString<DataResult>;
2728

2829
/**
@@ -68,6 +69,8 @@ export interface DataExtractorContext {
6869
* Evaluates an expression in the context of the active stack frame.
6970
*/
7071
evalFn: <TEval>(expression: string) => TEval;
72+
73+
variablesInScope: Record<string, unknown>;
7174
}
7275

7376
export interface DataExtraction {

data-extraction/src/js/api/DataExtractorApiImpl.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { registerDefaultExtractors } from "./default-extractors";
1414
*/
1515
export class DataExtractorApiImpl implements DataExtractorApi {
1616
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
17+
public static lastVariablesInScope:
18+
| Record<string, unknown>
19+
| undefined = undefined;
1720

1821
private readonly extractors = new Map<string, DataExtractor>();
1922

@@ -34,7 +37,8 @@ export class DataExtractorApiImpl implements DataExtractorApi {
3437
public getData(
3538
valueFn: () => unknown,
3639
evalFn: <T>(expression: string) => T,
37-
preferredDataExtractorId: string | undefined
40+
preferredDataExtractorId: string | undefined,
41+
variablesInScope: Record<string, unknown>
3842
): JSONString<DataResult> {
3943
const extractions = new Array<DataExtraction>();
4044
const extractionCollector: ExtractionCollector = {
@@ -43,13 +47,20 @@ export class DataExtractorApiImpl implements DataExtractorApi {
4347
},
4448
};
4549

50+
DataExtractorApiImpl.lastVariablesInScope = variablesInScope;
4651
DataExtractorApiImpl.lastEvalFn = evalFn;
4752
const value = valueFn();
48-
DataExtractorApiImpl.lastEvalFn = undefined;
4953

5054
for (const e of this.extractors.values()) {
51-
e.getExtractions(value, extractionCollector, { evalFn });
55+
e.getExtractions(value, extractionCollector, {
56+
evalFn,
57+
variablesInScope,
58+
});
5259
}
60+
61+
DataExtractorApiImpl.lastVariablesInScope = undefined;
62+
DataExtractorApiImpl.lastEvalFn = undefined;
63+
5364
extractions.sort((a, b) => b.priority - a.priority);
5465
let usedExtraction = extractions[0];
5566
if (!usedExtraction) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { DataExtractor, DataExtractorContext, ExtractionCollector } from "../..";
2+
3+
export class StringDiffExtractor implements DataExtractor {
4+
public readonly id = "string-diff";
5+
6+
public getExtractions(data: unknown,
7+
collector: ExtractionCollector,
8+
context: DataExtractorContext) {
9+
if (Array.isArray(data) && data.length === 2 && typeof data[0] === "string" && typeof data[1] === "string") {
10+
collector.addExtraction({
11+
id: "string-diff",
12+
name: "String Diff",
13+
priority: 1000,
14+
extractData: () => ({
15+
kind: { text: true },
16+
text: data[0],
17+
otherText: data[1]
18+
})
19+
})
20+
}
21+
}
22+
}

data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { ObjectGraphExtractor } from "./ObjectGraphExtractor";
88
import { getDataExtractorApi } from "../injection";
99
import { GridExtractor } from "./GridExtractor";
1010
import { TableDataExtractor } from "./TableExtractor";
11+
import { StringDiffExtractor } from "./StringDiffExtractor";
12+
import { find } from "../../helpers";
1113

1214
/**
1315
* The default data extractors should be registered by VS Code automatically.
@@ -25,7 +27,102 @@ export function registerDefaultExtractors(
2527
new ObjectGraphExtractor(),
2628
new GridExtractor(),
2729
new TableDataExtractor(),
30+
new StringDiffExtractor(),
2831
]) {
2932
api.registerExtractor(item);
3033
}
34+
35+
function isRange(
36+
value: unknown
37+
): value is {
38+
startColumn: number;
39+
startLineNumber: number;
40+
endColumn: number;
41+
endLineNumber: number;
42+
} {
43+
return (
44+
typeof value === "object" &&
45+
!!value &&
46+
"startColumn" in value &&
47+
"startLineNumber" in value
48+
);
49+
}
50+
51+
function isPosition(
52+
value: unknown
53+
): value is { column: number; lineNumber: number } {
54+
return (
55+
typeof value === "object" &&
56+
!!value &&
57+
"column" in value &&
58+
"lineNumber" in value
59+
);
60+
}
61+
62+
// hedietDbgVis.find(x => x.constructor.name === 'TextModel').getValue()
63+
api.registerExtractor({
64+
id: "positionInTextModel",
65+
getExtractions(data, collector, context) {
66+
let range: {
67+
start: {
68+
line: number;
69+
column: number;
70+
};
71+
end: {
72+
line: number;
73+
column: number;
74+
};
75+
};
76+
if (isRange(data)) {
77+
range = {
78+
start: {
79+
line: data.startLineNumber - 1,
80+
column: data.startColumn - 1,
81+
},
82+
end: {
83+
line: data.endLineNumber - 1,
84+
column: data.endColumn - 1,
85+
},
86+
};
87+
} else if (isPosition(data)) {
88+
range = {
89+
start: {
90+
line: data.lineNumber - 1,
91+
column: data.column - 1,
92+
},
93+
end: {
94+
line: data.lineNumber - 1,
95+
column: data.column - 1,
96+
},
97+
};
98+
} else {
99+
return;
100+
}
101+
102+
if (!isRange(data) && !isPosition(data)) {
103+
return;
104+
}
105+
106+
const textModel: any = find(
107+
x =>
108+
typeof x === "object" &&
109+
!!x &&
110+
"constructor" in x &&
111+
(x as any).constructor.name === "TextModel"
112+
);
113+
114+
collector.addExtraction({
115+
id: "positionInTextModel",
116+
name: "Position In TextModel",
117+
extractData() {
118+
return {
119+
kind: { text: true },
120+
text: textModel.getValue(),
121+
decorations: [{ range }],
122+
};
123+
},
124+
priority: 1000,
125+
});
126+
},
127+
});
31128
}

data-extraction/src/js/helpers/createGraph.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
GraphVisualizationData,
55
} from "../../CommonDataTypes";
66

7-
export type CreateGraphEdge<T> = { to: T } & Omit<GraphEdge, "from" | "to">;
7+
export type CreateGraphEdge<T> = ({ to: T } | { from: T } | { include: T }) &
8+
Omit<GraphEdge, "from" | "to">;
89

910
/**
1011
* Given a list of roots, it creates a graph by following their edges recursively.
@@ -58,21 +59,39 @@ export function createGraph<T>(
5859
const fromId = getId(item);
5960
r.nodes.push({ ...nodeInfo, id: fromId, ["edges" as any]: undefined });
6061
for (const e of nodeInfo.edges) {
61-
const toId = getId(e.to);
62-
r.edges.push({
63-
...e,
64-
from: fromId,
65-
to: toId,
66-
});
67-
let shouldPush = !processed.has(e.to);
62+
let other: T;
63+
let toId: string | undefined;
64+
let fromId_: string | undefined;
65+
if ("to" in e) {
66+
other = e.to;
67+
toId = getId(e.to);
68+
fromId_ = fromId;
69+
} else if ("from" in e) {
70+
other = e.from;
71+
toId = getId(e.from);
72+
fromId_ = fromId;
73+
} else if ("include" in e) {
74+
other = e.include;
75+
} else {
76+
throw new Error("Every edge must either have 'to' or 'from'");
77+
}
78+
79+
if (fromId_ !== undefined && toId !== undefined) {
80+
r.edges.push({
81+
...e,
82+
from: fromId_,
83+
to: toId,
84+
});
85+
}
86+
let shouldPush = !processed.has(other);
6887
if (
6988
options.maxSize &&
7089
processed.size + queue.length > options.maxSize
7190
) {
7291
shouldPush = false;
7392
}
7493
if (shouldPush) {
75-
queue.push({ item: e.to, dist: dist + 1 });
94+
queue.push({ item: other, dist: dist + 1 });
7695
}
7796
}
7897
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { DataExtractorApiImpl } from "../api";
2+
3+
export function find(predicate: (obj: unknown) => boolean): unknown {
4+
const processed = new Set();
5+
if (!DataExtractorApiImpl.lastVariablesInScope) {
6+
throw new Error("No variables in scope!");
7+
}
8+
9+
const values = Object.values(DataExtractorApiImpl.lastVariablesInScope);
10+
const queue = [...values];
11+
12+
let i = 10000;
13+
while (i > 0) {
14+
i--;
15+
16+
const top = queue.shift();
17+
processed.add(top);
18+
19+
if (predicate(top)) {
20+
return top;
21+
}
22+
23+
if (typeof top === "object" && top) {
24+
for (const val of Object.values(top)) {
25+
if (!processed.has(val)) {
26+
processed.add(val);
27+
queue.push(val);
28+
}
29+
}
30+
}
31+
}
32+
33+
return undefined;
34+
}

data-extraction/src/js/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./tryEval";
44
export * from "./markedGrid";
55
export * from "./cache";
66
export * from "./asData";
7+
export * from "./find";

extension/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change Log
22

3+
## 2.1.0
4+
5+
- Adds text diff and text decoration viewer.
6+
- Supports more js debug adapters
7+
38
## 2.0.6
49

510
- Fixes grid extractor bug

0 commit comments

Comments
 (0)