Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fb1e8127a | ||
|
|
a2a5d1b873 | ||
|
|
e7e76ccfb9 | ||
|
|
e01c09eac5 | ||
|
|
86e71ae534 | ||
|
|
e46cdef402 | ||
|
|
2af8ef9a53 | ||
|
|
aa3412e6ac | ||
|
|
6543180591 | ||
|
|
cd88168211 | ||
|
|
683f1e1f76 | ||
|
|
f2b4f620fe | ||
|
|
d22e5d4111 | ||
|
|
6ce5d666a3 | ||
|
|
6da44bd722 | ||
|
|
db1e5a8086 | ||
|
|
28d02ca4ab | ||
|
|
1e65dd1990 | ||
|
|
76fa30f71a | ||
|
|
3c8ccd9a53 | ||
|
|
9c70e06ee7 | ||
|
|
8f83a1d96e | ||
|
|
9cd290ab57 | ||
|
|
061ccdca33 | ||
|
|
12099d6152 | ||
|
|
b47efb7176 | ||
|
|
ee9f405033 | ||
|
|
cc272acb06 | ||
|
|
4a81e9910b | ||
|
|
d8840bf33b | ||
|
|
e882593cea | ||
|
|
7ab4e0c4b5 | ||
|
|
4aca50f67a | ||
|
|
6ef0ecb60f | ||
|
|
0ce1b02b2c | ||
|
|
abfa44b4f9 | ||
|
|
fa1432ffc8 | ||
|
|
fc142926b8 | ||
|
|
d68d3b4076 | ||
|
|
f8c3b73d4f | ||
|
|
a8e1a5a36c | ||
|
|
d421fa1497 | ||
|
|
118d02e797 | ||
|
|
c420ff7f6f | ||
|
|
7aaebf0332 | ||
|
|
2f44c2d6f8 | ||
|
|
f5f6f5d56d | ||
|
|
3e64d3b05c | ||
|
|
0429eac23e | ||
|
|
dc9b07a4b6 | ||
|
|
2b6dbf52e9 | ||
|
|
e4606a4ae3 | ||
|
|
508062768c | ||
|
|
d7dc1f27e0 | ||
|
|
4525c226c8 | ||
|
|
c09dadb199 | ||
|
|
c88e98cc24 | ||
|
|
17be602906 |
7
.gitignore
vendored
@@ -80,4 +80,9 @@ typings/
|
||||
.dynamodb/
|
||||
|
||||
dist/
|
||||
out/
|
||||
out/
|
||||
|
||||
# Python byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
10
.vscode/launch.json
vendored
@@ -47,7 +47,7 @@
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
||||
"${workspaceFolder}/demos/python"
|
||||
"${workspaceFolder}/demos/js"
|
||||
],
|
||||
"env": {
|
||||
"HOT_RELOAD": "",
|
||||
@@ -70,6 +70,12 @@
|
||||
},
|
||||
"outFiles": ["${workspaceFolder}/extension/dist/**/*.js"],
|
||||
"preLaunchTask": "npm: dev - extension"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "dart",
|
||||
"request": "launch",
|
||||
"name": "Run Dart samples",
|
||||
"program": "demos/dart/demo.dart"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
9
.vscode/settings.json
vendored
@@ -7,6 +7,11 @@
|
||||
"plantuml.exportOutDir": "docs/exported",
|
||||
"typescript.updateImportsOnFileMove.enabled": "prompt",
|
||||
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
|
||||
"mochaExplorer.require": ["ts-node/register", "source-map-support/register"],
|
||||
"tasksStatusbar.taskLabelFilter": "dev"
|
||||
"mochaExplorer.require": [
|
||||
"ts-node/register",
|
||||
"source-map-support/register"
|
||||
],
|
||||
"tasksStatusbar.taskLabelFilter": "dev",
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
}
|
||||
|
||||
@@ -42,16 +42,10 @@ Implements the UI and is hosted inside a webview in VS Code.
|
||||
Can be opened in a browser window.
|
||||
Uses websockets and JSON RPC to communicate with the extension.
|
||||
|
||||
### visualization
|
||||
### hediet/visualization
|
||||
|
||||
Contains React components that visualize various JSON documents.
|
||||
Also provides infrastructure for selecting the best visualization for a given JSON document.
|
||||
|
||||
### visualization-playground
|
||||
|
||||
A playground to test and play with the visualizations for the web.
|
||||
Is hosted at [https://hediet.github.io/vscode-debug-visualizer/](https://hediet.github.io/vscode-debug-visualizer/).
|
||||
Aims to have fast build times.
|
||||
Contains all the visualizers and visualization infrastructure.
|
||||
This external project can be found [here](https://github.com/hediet/visualization).
|
||||
|
||||
### extension
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hediet/debug-visualizer-data-extraction",
|
||||
"description": "A library that helps implementing data extractors for the Debug Visualizer VS Code extension.",
|
||||
"version": "0.13.0",
|
||||
"version": "0.14.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"author": {
|
||||
@@ -29,17 +29,17 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/plotly.js": "1.44.28",
|
||||
"coveralls": "^3.0.11",
|
||||
"mocha": "^7.1.1",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^15.0.0",
|
||||
"source-map-support": "^0.5.16",
|
||||
"typescript": "^3.8.2",
|
||||
"@types/plotly.js": "1.44.28",
|
||||
"@types/node": "^13.7.4",
|
||||
"ts-loader": "^6.2.1",
|
||||
"tslint": "^6.0.0",
|
||||
"typescript": "^3.8.2",
|
||||
"webpack": "^4.41.6",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"ts-loader": "^6.2.1"
|
||||
"webpack-cli": "^3.3.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export type KnownVisualizationData =
|
||||
| GridVisualizationData
|
||||
| ImageVisualizationData
|
||||
| MonacoTextVisualizationData
|
||||
| MonacoTextDiffVisualizationData
|
||||
| TableVisualizationData
|
||||
| PlotlyVisualizationData
|
||||
| SimpleTextVisualizationData
|
||||
@@ -106,7 +107,7 @@ export type GraphEdge = {
|
||||
label?: string;
|
||||
id?: string;
|
||||
color?: string;
|
||||
dashes?: boolean;
|
||||
style?: "solid" | "dashed" | "dotted";
|
||||
};
|
||||
|
||||
export type GridVisualizationData = {
|
||||
@@ -156,6 +157,50 @@ export type MonacoTextVisualizationData = {
|
||||
* The text to show
|
||||
*/
|
||||
text: string;
|
||||
decorations?: {
|
||||
range: LineColumnRange;
|
||||
label?: string;
|
||||
}[];
|
||||
/**
|
||||
* An optional filename that might be used for chosing a syntax highlighter
|
||||
*/
|
||||
fileName?: string;
|
||||
};
|
||||
|
||||
export type LineColumnRange = {
|
||||
/**
|
||||
* The start position
|
||||
*/
|
||||
start: LineColumnPosition;
|
||||
/**
|
||||
* The end position
|
||||
*/
|
||||
end: LineColumnPosition;
|
||||
};
|
||||
|
||||
export type LineColumnPosition = {
|
||||
/**
|
||||
* The 0-based line number
|
||||
*/
|
||||
line: number;
|
||||
/**
|
||||
* The 0-based column number
|
||||
*/
|
||||
column: number;
|
||||
};
|
||||
|
||||
export type MonacoTextDiffVisualizationData = {
|
||||
kind: {
|
||||
text: true;
|
||||
};
|
||||
/**
|
||||
* The text to show
|
||||
*/
|
||||
text: string;
|
||||
/**
|
||||
* The text to compare against
|
||||
*/
|
||||
otherText: string;
|
||||
/**
|
||||
* An optional filename that might be used for chosing a syntax highlighter
|
||||
*/
|
||||
@@ -183,6 +228,16 @@ export type PlotlyVisualizationData = {
|
||||
text?: string | string[];
|
||||
xaxis?: string;
|
||||
yaxis?: string;
|
||||
cells?: {
|
||||
values?: string[][];
|
||||
};
|
||||
header?: {
|
||||
values?: string[];
|
||||
};
|
||||
domain?: {
|
||||
x?: number[],
|
||||
y?: number[],
|
||||
};
|
||||
x?: (string | number | null)[] | (string | number | null)[][];
|
||||
y?: (string | number | null)[] | (string | number | null)[][];
|
||||
z?: (string | number | null)[] | (string | number | null)[][];
|
||||
@@ -212,7 +267,8 @@ export type PlotlyVisualizationData = {
|
||||
| "waterfall"
|
||||
| "funnel"
|
||||
| "funnelarea"
|
||||
| "scattermapbox";
|
||||
| "scattermapbox"
|
||||
| "table";
|
||||
mode?:
|
||||
| "lines"
|
||||
| "markers"
|
||||
|
||||
11
data-extraction/src/getGlobal.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export function getGlobal(): any {
|
||||
if (typeof globalThis === "object") {
|
||||
return globalThis;
|
||||
} else if (typeof global === "object") {
|
||||
return global;
|
||||
} else if (typeof window === "object") {
|
||||
return window;
|
||||
}
|
||||
|
||||
throw new Error("No global available");
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
VisualizationData,
|
||||
DataExtractionResult,
|
||||
} from "../../DataExtractionResult";
|
||||
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||
|
||||
export interface DataExtractorApi {
|
||||
/**
|
||||
@@ -22,7 +23,8 @@ export interface DataExtractorApi {
|
||||
getData(
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, unknown>
|
||||
): JSONString<DataResult>;
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,9 @@ export interface DataExtractorApi {
|
||||
* @preferExisting if `true`, existing extractors with the same id are not overwritten.
|
||||
*/
|
||||
registerDefaultExtractors(preferExisting?: boolean): void;
|
||||
|
||||
registerDataExtractorsSource(id: string, fn: LoadDataExtractorsFn): void;
|
||||
unregisterDataExtractorsSource(id: string): void;
|
||||
}
|
||||
|
||||
export type DataResult =
|
||||
@@ -68,6 +73,8 @@ export interface DataExtractorContext {
|
||||
* Evaluates an expression in the context of the active stack frame.
|
||||
*/
|
||||
evalFn: <TEval>(expression: string) => TEval;
|
||||
|
||||
variablesInScope: Record<string, () => unknown>;
|
||||
}
|
||||
|
||||
export interface DataExtraction {
|
||||
|
||||
@@ -5,17 +5,21 @@ import {
|
||||
DataExtractor,
|
||||
DataExtraction,
|
||||
ExtractionCollector,
|
||||
DataExtractorContext,
|
||||
} from "./DataExtractorApi";
|
||||
import { DataExtractorInfo } from "../../DataExtractionResult";
|
||||
import { registerDefaultExtractors } from "./default-extractors";
|
||||
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||
import * as helpers from "../helpers";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
|
||||
public static lastContext: DataExtractorContext | undefined = undefined;
|
||||
|
||||
private readonly extractors = new Map<string, DataExtractor>();
|
||||
private readonly extractorSources = new Map<string, LoadDataExtractorsFn>();
|
||||
|
||||
private toJson<TData>(data: TData): JSONString<TData> {
|
||||
return JSON.stringify(data) as any;
|
||||
@@ -34,7 +38,8 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
public getData(
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, () => unknown>
|
||||
): JSONString<DataResult> {
|
||||
const extractions = new Array<DataExtraction>();
|
||||
const extractionCollector: ExtractionCollector = {
|
||||
@@ -43,13 +48,28 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
},
|
||||
};
|
||||
|
||||
DataExtractorApiImpl.lastEvalFn = evalFn;
|
||||
const value = valueFn();
|
||||
DataExtractorApiImpl.lastEvalFn = undefined;
|
||||
const context: DataExtractorContext = {
|
||||
evalFn,
|
||||
variablesInScope,
|
||||
};
|
||||
|
||||
for (const e of this.extractors.values()) {
|
||||
e.getExtractions(value, extractionCollector, { evalFn });
|
||||
DataExtractorApiImpl.lastContext = context;
|
||||
const value = valueFn();
|
||||
|
||||
const extractors = new Array<DataExtractor>();
|
||||
|
||||
for (const fn of this.extractorSources.values()) {
|
||||
fn((extractor) => {
|
||||
extractors.push(extractor);
|
||||
}, helpers);
|
||||
}
|
||||
|
||||
for (const e of [...this.extractors.values(), ...extractors]) {
|
||||
e.getExtractions(value, extractionCollector, context);
|
||||
}
|
||||
|
||||
DataExtractorApiImpl.lastContext = undefined;
|
||||
|
||||
extractions.sort((a, b) => b.priority - a.priority);
|
||||
let usedExtraction = extractions[0];
|
||||
if (!usedExtraction) {
|
||||
@@ -58,7 +78,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
|
||||
if (preferredDataExtractorId) {
|
||||
const preferred = extractions.find(
|
||||
e => e.id === preferredDataExtractorId
|
||||
(e) => e.id === preferredDataExtractorId
|
||||
);
|
||||
if (preferred) {
|
||||
usedExtraction = preferred;
|
||||
@@ -88,4 +108,15 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
// TODO consider preferExisting
|
||||
registerDefaultExtractors(this);
|
||||
}
|
||||
|
||||
public registerDataExtractorsSource(
|
||||
id: string,
|
||||
fn: LoadDataExtractorsFn
|
||||
): void {
|
||||
this.extractorSources.set(id, fn);
|
||||
}
|
||||
|
||||
public unregisterDataExtractorsSource(id: string): void {
|
||||
this.extractorSources.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
7
data-extraction/src/js/api/LoadDataExtractorsFn.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { DataExtractor } from "./DataExtractorApi";
|
||||
import type * as helpersTypes from "../helpers"
|
||||
|
||||
export type LoadDataExtractorsFn = (
|
||||
register: (extractor: DataExtractor) => void,
|
||||
helpers: typeof helpersTypes
|
||||
) => void;
|
||||
@@ -24,7 +24,7 @@ export class GridExtractor implements DataExtractor {
|
||||
extractData: () =>
|
||||
expect<GridVisualizationData>({
|
||||
kind: { grid: true },
|
||||
rows: [{ columns: data.map(d => ({ tag: d })) }],
|
||||
rows: [{ columns: data.map(d => ({ tag: "" + d })) }],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DataExtractor, DataExtractorContext, ExtractionCollector } from "../..";
|
||||
|
||||
export class StringDiffExtractor implements DataExtractor {
|
||||
public readonly id = "string-diff";
|
||||
|
||||
public getExtractions(data: unknown,
|
||||
collector: ExtractionCollector,
|
||||
context: DataExtractorContext) {
|
||||
if (Array.isArray(data) && data.length === 2 && typeof data[0] === "string" && typeof data[1] === "string") {
|
||||
collector.addExtraction({
|
||||
id: "string-diff",
|
||||
name: "String Diff",
|
||||
priority: 1000,
|
||||
extractData: () => ({
|
||||
kind: { text: true },
|
||||
text: data[0],
|
||||
otherText: data[1]
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { isAssertionExpression } from "typescript";
|
||||
import { DataExtractor, ExtractionCollector, DataExtractorContext } from "..";
|
||||
import { TableVisualizationData } from "../../../CommonDataTypes";
|
||||
import { expect } from "../../../util";
|
||||
|
||||
function assert<T>(value: unknown): asserts value {}
|
||||
|
||||
export class TableDataExtractor implements DataExtractor {
|
||||
readonly id = "table";
|
||||
|
||||
getExtractions(
|
||||
data: unknown,
|
||||
collector: ExtractionCollector,
|
||||
context: DataExtractorContext
|
||||
): void {
|
||||
if (!Array.isArray(data)) {
|
||||
return;
|
||||
}
|
||||
if (!data.every(d => typeof d === "object" && d)) {
|
||||
return;
|
||||
}
|
||||
assert<object[]>(data);
|
||||
|
||||
collector.addExtraction({
|
||||
id: "table",
|
||||
name: "Table",
|
||||
priority: 1000,
|
||||
extractData() {
|
||||
return expect<TableVisualizationData>({
|
||||
kind: {
|
||||
table: true,
|
||||
},
|
||||
rows: data,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
collector.addExtraction({
|
||||
id: "table-with-type-name",
|
||||
name: "Table (With Type Name)",
|
||||
priority: 950,
|
||||
extractData() {
|
||||
return expect<TableVisualizationData>({
|
||||
kind: {
|
||||
table: true,
|
||||
},
|
||||
rows: data.map(d => ({ type: d.constructor.name, ...d })),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ export class TypeScriptAstDataExtractor implements DataExtractor {
|
||||
root: helper.toTreeNode(
|
||||
rootNode || finalRootSourceFile,
|
||||
"root",
|
||||
"",
|
||||
marked,
|
||||
fn
|
||||
),
|
||||
@@ -121,19 +122,24 @@ export class TypeScriptAstDataExtractor implements DataExtractor {
|
||||
class Helper {
|
||||
constructor(private readonly tsApi: typeof ts) {}
|
||||
|
||||
findKey(value: any, object: any): string | null {
|
||||
for (var key in object) {
|
||||
if (key.startsWith("_")) continue;
|
||||
getPropertyNameInParent(value: any, parent: any): string | undefined {
|
||||
for (const propertyName in parent) {
|
||||
if (propertyName.startsWith("_")) continue;
|
||||
|
||||
var member = object[key];
|
||||
if (member === value) return key;
|
||||
const member = parent[propertyName];
|
||||
if (member === value) {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
if (Array.isArray(member) && member.indexOf(value) !== -1) {
|
||||
return key;
|
||||
if (Array.isArray(member)) {
|
||||
const index = member.indexOf(value);
|
||||
if (index !== -1) {
|
||||
return `${propertyName}[${index}]`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getChildren(node: ts.Node): ts.Node[] {
|
||||
@@ -147,30 +153,38 @@ class Helper {
|
||||
toTreeNode(
|
||||
node: ts.Node,
|
||||
memberName: string,
|
||||
segmentName: string,
|
||||
marked: Set<ts.Node>,
|
||||
emphasizedValueFn: (node: ts.Node) => string | undefined
|
||||
): AstTreeNode {
|
||||
const name = this.tsApi.SyntaxKind[node.kind];
|
||||
const children = this.getChildren(node)
|
||||
.map((childNode, idx) => {
|
||||
let parentPropertyName = this.findKey(childNode, node) || "";
|
||||
let parentPropertyName =
|
||||
this.getPropertyNameInParent(childNode, node) || "";
|
||||
|
||||
if (childNode.kind == this.tsApi.SyntaxKind.SyntaxList) {
|
||||
const children = this.getChildren(childNode);
|
||||
children.some(c => {
|
||||
parentPropertyName = this.findKey(c, node) || "";
|
||||
return !!parentPropertyName;
|
||||
});
|
||||
|
||||
if (children.length === 0) return null!;
|
||||
for (const c of children) {
|
||||
const name =
|
||||
this.getPropertyNameInParent(c, node) || "";
|
||||
if (name) {
|
||||
parentPropertyName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let segmentName = "." + parentPropertyName;
|
||||
if (node.kind == this.tsApi.SyntaxKind.SyntaxList) {
|
||||
parentPropertyName = "" + idx;
|
||||
segmentName = `[${idx}]`;
|
||||
}
|
||||
|
||||
return this.toTreeNode(
|
||||
childNode,
|
||||
parentPropertyName,
|
||||
segmentName,
|
||||
marked,
|
||||
emphasizedValueFn
|
||||
);
|
||||
@@ -201,6 +215,7 @@ class Helper {
|
||||
return {
|
||||
items,
|
||||
children: children,
|
||||
segment: segmentName,
|
||||
span: {
|
||||
length: node.end - node.pos,
|
||||
start: node.pos,
|
||||
|
||||
@@ -7,6 +7,8 @@ import { PlotlyDataExtractor } from "./PlotlyDataExtractor";
|
||||
import { ObjectGraphExtractor } from "./ObjectGraphExtractor";
|
||||
import { getDataExtractorApi } from "../injection";
|
||||
import { GridExtractor } from "./GridExtractor";
|
||||
import { TableDataExtractor } from "./TableExtractor";
|
||||
import { StringDiffExtractor } from "./StringDiffExtractor";
|
||||
|
||||
/**
|
||||
* The default data extractors should be registered by VS Code automatically.
|
||||
@@ -23,6 +25,8 @@ export function registerDefaultExtractors(
|
||||
new PlotlyDataExtractor(),
|
||||
new ObjectGraphExtractor(),
|
||||
new GridExtractor(),
|
||||
new TableDataExtractor(),
|
||||
new StringDiffExtractor(),
|
||||
]) {
|
||||
api.registerExtractor(item);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./DataExtractorApi";
|
||||
export * from "./injection";
|
||||
export { DataExtractorApiImpl } from "./DataExtractorApiImpl";
|
||||
export * from "./LoadDataExtractorsFn";
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DataExtractorApi } from "./DataExtractorApi";
|
||||
import { DataExtractorApiImpl } from "./DataExtractorApiImpl";
|
||||
import * as helpers from "../helpers";
|
||||
import * as globalHelpers from "../global-helpers";
|
||||
import { getGlobal } from "../../getGlobal";
|
||||
|
||||
/**
|
||||
* Returns standalone JS code representing an expression that initializes the data extraction API.
|
||||
@@ -20,22 +21,19 @@ export function getExpressionToInitializeDataExtractorApi(): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns standalone JS code representing an expression returns the data extraction API.
|
||||
* Returns standalone JS code representing an expression that returns the data extraction API.
|
||||
* This expression returns an object of type `DataExtractorApi`.
|
||||
* If the API must be initialized with `getExpressionToInitializeDataExtractorApi()`,
|
||||
* it throws an exception containing the text of `ApiHasNotBeenInitializedCode`.
|
||||
* This function is called in the VS Code extension, the expression is evaluated in the debugee.
|
||||
*/
|
||||
export function getExpressionForDataExtractorApi(): string {
|
||||
return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`;
|
||||
}
|
||||
|
||||
const apiKey = "@hediet/data-extractor-api/v2";
|
||||
const apiKey = "@hediet/data-extractor-api/v3";
|
||||
|
||||
export function getDataExtractorApi(): DataExtractorApi {
|
||||
installHelpers();
|
||||
const globalObj =
|
||||
typeof window === "object" ? (window as any) : (global as any);
|
||||
const globalObj = getGlobal();
|
||||
if (!globalObj[apiKey]) {
|
||||
globalObj[apiKey] = new DataExtractorApiImpl();
|
||||
}
|
||||
@@ -46,29 +44,28 @@ export function getDataExtractorApi(): DataExtractorApi {
|
||||
* This code is used to detect if the API has not been initialized yet.
|
||||
* @internal
|
||||
*/
|
||||
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
|
||||
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
||||
const globalObj =
|
||||
typeof window === "object" ? (window as any) : (global as any);
|
||||
const key: typeof apiKey = "@hediet/data-extractor-api/v2";
|
||||
let api: DataExtractorApi | undefined = globalObj[key];
|
||||
if (!api) {
|
||||
const code: typeof ApiHasNotBeenInitializedCode =
|
||||
"EgH0cybXij1jYUozyakO";
|
||||
throw new Error(
|
||||
`Data Extractor API has not been initialized. Code: ${code}`
|
||||
);
|
||||
}
|
||||
return api;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
||||
const globalObj =
|
||||
typeof window === "object" ? (window as any) : (global as any);
|
||||
const key: typeof apiKey = "@hediet/data-extractor-api/v3";
|
||||
let api: DataExtractorApi | undefined = globalObj[key];
|
||||
if (!api) {
|
||||
const code: typeof ApiHasNotBeenInitializedCode =
|
||||
"EgH0cybXij1jYUozyakO";
|
||||
throw new Error(
|
||||
`Data Extractor API has not been initialized. Code: ${code}`
|
||||
);
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
export function installHelpers(): void {
|
||||
const globalObj =
|
||||
typeof window === "object" ? (window as any) : (global as any);
|
||||
const globalObj = getGlobal();
|
||||
// `hediet` as prefix to avoid name collision (I own `hediet.de`).
|
||||
globalObj["hedietDbgVis"] = { ...helpers, ...globalHelpers };
|
||||
}
|
||||
|
||||
6
data-extraction/src/js/helpers/asData.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { KnownVisualizationData } from "../../CommonDataTypes";
|
||||
import { VisualizationData } from "../../DataExtractionResult";
|
||||
|
||||
export function asData(data: KnownVisualizationData): VisualizationData {
|
||||
return data;
|
||||
}
|
||||
@@ -12,8 +12,8 @@ export function cache<T>(
|
||||
let resultFn: () => any;
|
||||
let key: string;
|
||||
if (typeof expression === "string") {
|
||||
const evalFn = DataExtractorApiImpl.lastEvalFn!;
|
||||
resultFn = () => evalFn(expression);
|
||||
const context = DataExtractorApiImpl.lastContext!;
|
||||
resultFn = () => context.evalFn(expression);
|
||||
key = JSON.stringify({ expr: expression, id });
|
||||
} else {
|
||||
resultFn = () => expression();
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
GraphVisualizationData,
|
||||
} from "../../CommonDataTypes";
|
||||
|
||||
export type CreateGraphEdge<T> = { to: T } & Omit<GraphEdge, "from" | "to">;
|
||||
export type CreateGraphEdge<T> = ({ to: T } | { from: T } | { include: T }) &
|
||||
Omit<GraphEdge, "from" | "to">;
|
||||
|
||||
/**
|
||||
* Given a list of roots, it creates a graph by following their edges recursively.
|
||||
@@ -58,13 +59,31 @@ export function createGraph<T>(
|
||||
const fromId = getId(item);
|
||||
r.nodes.push({ ...nodeInfo, id: fromId, ["edges" as any]: undefined });
|
||||
for (const e of nodeInfo.edges) {
|
||||
const toId = getId(e.to);
|
||||
r.edges.push({
|
||||
...e,
|
||||
from: fromId,
|
||||
to: toId,
|
||||
});
|
||||
let shouldPush = !processed.has(e.to);
|
||||
let other: T;
|
||||
let toId: string | undefined;
|
||||
let fromId_: string | undefined;
|
||||
if ("to" in e) {
|
||||
other = e.to;
|
||||
toId = getId(e.to);
|
||||
fromId_ = fromId;
|
||||
} else if ("from" in e) {
|
||||
other = e.from;
|
||||
toId = getId(e.from);
|
||||
fromId_ = fromId;
|
||||
} else if ("include" in e) {
|
||||
other = e.include;
|
||||
} else {
|
||||
throw new Error("Every edge must either have 'to' or 'from'");
|
||||
}
|
||||
|
||||
if (fromId_ !== undefined && toId !== undefined) {
|
||||
r.edges.push({
|
||||
...e,
|
||||
from: fromId_,
|
||||
to: toId,
|
||||
});
|
||||
}
|
||||
let shouldPush = !processed.has(other);
|
||||
if (
|
||||
options.maxSize &&
|
||||
processed.size + queue.length > options.maxSize
|
||||
@@ -72,7 +91,7 @@ export function createGraph<T>(
|
||||
shouldPush = false;
|
||||
}
|
||||
if (shouldPush) {
|
||||
queue.push({ item: e.to, dist: dist + 1 });
|
||||
queue.push({ item: other, dist: dist + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
data-extraction/src/js/helpers/find.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { DataExtractorApiImpl } from "../api";
|
||||
|
||||
export function find(predicate: (obj: unknown) => boolean): unknown {
|
||||
const processed = new Set();
|
||||
if (!DataExtractorApiImpl.lastContext) {
|
||||
throw new Error("No data extractor context!");
|
||||
}
|
||||
|
||||
const values = Object.values(
|
||||
DataExtractorApiImpl.lastContext.variablesInScope
|
||||
);
|
||||
const queue = [
|
||||
...values.map((v) => {
|
||||
try {
|
||||
return v();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
let i = 10000;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
|
||||
const top = queue.shift();
|
||||
processed.add(top);
|
||||
|
||||
if (predicate(top)) {
|
||||
return top;
|
||||
}
|
||||
|
||||
if (typeof top === "object" && top) {
|
||||
for (const val of Object.values(top)) {
|
||||
if (!processed.has(val)) {
|
||||
processed.add(val);
|
||||
queue.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -3,3 +3,5 @@ export * from "./createGraphFromPointers";
|
||||
export * from "./tryEval";
|
||||
export * from "./markedGrid";
|
||||
export * from "./cache";
|
||||
export * from "./asData";
|
||||
export * from "./find";
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { GridVisualizationData } from "../../CommonDataTypes";
|
||||
|
||||
export function markedGrid(
|
||||
arr: any[],
|
||||
marked: Record<string, number>
|
||||
arr: unknown[],
|
||||
marked: Record<string, number | undefined>
|
||||
): GridVisualizationData {
|
||||
return {
|
||||
kind: { grid: true },
|
||||
rows: [{ columns: arr.map(d => ({ tag: d })) }],
|
||||
markers: Object.entries(marked).map(([key, val]) => ({
|
||||
id: key,
|
||||
row: 0,
|
||||
column: val,
|
||||
})),
|
||||
rows: [{ columns: arr.map(d => ({ content: "" + d, tag: "" + d })) }],
|
||||
markers: Object.entries(marked)
|
||||
.map(([key, val]) => ({
|
||||
id: "" + key,
|
||||
row: 0,
|
||||
column: val!,
|
||||
}))
|
||||
.filter(c => c.column !== undefined),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,17 +30,17 @@ export function tryEval(
|
||||
obj: Record<string, string> | string[] | string
|
||||
): Record<string, unknown> | unknown {
|
||||
const result: Record<string, unknown> = {};
|
||||
const evalFn = DataExtractorApiImpl.lastEvalFn!;
|
||||
const context = DataExtractorApiImpl.lastContext!;
|
||||
if (Array.isArray(obj)) {
|
||||
for (const val of obj) {
|
||||
try {
|
||||
result[val] = evalFn(val);
|
||||
result[val] = context.evalFn(val);
|
||||
} catch (e) {}
|
||||
}
|
||||
} else {
|
||||
for (const [key, val] of Object.entries(obj)) {
|
||||
try {
|
||||
result[key] = evalFn(val);
|
||||
result[key] = context.evalFn(val);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
85
demos/dart/debug_visualizers.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'dart:convert';
|
||||
|
||||
String graph(List<GraphNode> nodes, List<GraphEdge> edges) {
|
||||
return jsonEncode({
|
||||
'kind': {'graph': true},
|
||||
'nodes': nodes,
|
||||
'edges': edges,
|
||||
});
|
||||
}
|
||||
|
||||
String plotly(List<num> values) {
|
||||
return jsonEncode({
|
||||
'kind': {'plotly': true},
|
||||
'data': [
|
||||
{'y': values}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
String tree(TreeNode root) {
|
||||
return jsonEncode({
|
||||
'kind': {'tree': true},
|
||||
'root': root,
|
||||
});
|
||||
}
|
||||
|
||||
class Graph {
|
||||
List<GraphNode> nodes;
|
||||
List<GraphEdge> edges;
|
||||
}
|
||||
|
||||
class GraphEdge {
|
||||
final String from;
|
||||
final String to;
|
||||
final String label;
|
||||
|
||||
GraphEdge(this.from, this.to, this.label);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'from': from,
|
||||
'to': to,
|
||||
'label': label,
|
||||
};
|
||||
}
|
||||
|
||||
class GraphNode {
|
||||
final String id;
|
||||
final String label;
|
||||
|
||||
GraphNode(
|
||||
this.id,
|
||||
this.label,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'label': label,
|
||||
};
|
||||
}
|
||||
|
||||
class TreeNode {
|
||||
final List<TreeNode> children;
|
||||
final List<TreeNodeItem> items;
|
||||
final String segment;
|
||||
final bool isMarked;
|
||||
|
||||
TreeNode(this.children, this.items, this.segment, [this.isMarked = false]);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'children': children ?? [],
|
||||
'items': items,
|
||||
'segment': segment,
|
||||
'isMarked': isMarked,
|
||||
};
|
||||
}
|
||||
|
||||
class TreeNodeItem {
|
||||
final String text;
|
||||
|
||||
TreeNodeItem(this.text);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'text': text,
|
||||
};
|
||||
}
|
||||
83
demos/dart/demo.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:developer';
|
||||
import 'dart:math';
|
||||
|
||||
// ignore: unused_import
|
||||
import 'debug_visualizers.dart';
|
||||
|
||||
final _rnd = Random();
|
||||
|
||||
String Function() visualize;
|
||||
|
||||
void main() {
|
||||
debugger();
|
||||
// Open a Debug Visualizer window and evalute the expression "visualize()"
|
||||
// and then press Continue to step through the samples.
|
||||
plotly_demo();
|
||||
graph_demo();
|
||||
tree_demo();
|
||||
}
|
||||
|
||||
void plotly_demo() {
|
||||
final values = <int>[];
|
||||
visualize = () => plotly(values);
|
||||
for (var i = 0; i < 20; i++) {
|
||||
debugger();
|
||||
values.add(i + _rnd.nextInt(5) - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void graph_demo() {
|
||||
graphFromDoubleLinked<T>(
|
||||
DoubleLinkedQueue<T> values, String Function(T) toString) {
|
||||
node(T s) => GraphNode(toString(s), toString(s));
|
||||
edge(DoubleLinkedQueueEntry<T> e) => [
|
||||
if (e.nextEntry() != null)
|
||||
GraphEdge(
|
||||
toString(e.element),
|
||||
toString(e.nextEntry().element),
|
||||
"next",
|
||||
),
|
||||
if (e.previousEntry() != null)
|
||||
GraphEdge(
|
||||
toString(e.element),
|
||||
toString(e.previousEntry().element),
|
||||
"prev",
|
||||
),
|
||||
];
|
||||
|
||||
final nodes = values.map(node).toList();
|
||||
final edges = <GraphEdge>[];
|
||||
values.forEachEntry((e) => edges.addAll(edge(e)));
|
||||
return graph(nodes, edges);
|
||||
}
|
||||
|
||||
final values = DoubleLinkedQueue<String>();
|
||||
visualize = () => graphFromDoubleLinked(values, (s) => s);
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
debugger();
|
||||
values.addLast('Node $i');
|
||||
}
|
||||
}
|
||||
|
||||
void tree_demo() {
|
||||
var nodeNum = 1;
|
||||
TreeNode createNode(int levels) {
|
||||
final name = 'Node ${nodeNum++}';
|
||||
final children =
|
||||
levels > 0 ? List.generate(2, (_) => createNode(levels - 1)) : null;
|
||||
return TreeNode(children, [TreeNodeItem(name)], name);
|
||||
}
|
||||
|
||||
var levels = 0;
|
||||
visualize = () {
|
||||
nodeNum = 1;
|
||||
return tree(createNode(levels));
|
||||
};
|
||||
|
||||
for (var i = 1; i < 5; i++) {
|
||||
debugger();
|
||||
levels = i;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>java</name>
|
||||
<comment/>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
<name>java</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1599063072264</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
||||
|
||||
8
demos/js/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.minimap.enabled": false,
|
||||
"git.enabled": false,
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"debugVisualizer.js.customScriptPaths": [
|
||||
"${workspaceFolder}/custom-visualizer.js",
|
||||
]
|
||||
}
|
||||
152
demos/js/custom-visualizer.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {import("@hediet/debug-visualizer-data-extraction").LoadDataExtractorsFn}
|
||||
*/
|
||||
module.exports = (register, helpers) => {
|
||||
register({
|
||||
id: "map",
|
||||
getExtractions(data, collector, context) {
|
||||
if (!(data instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collector.addExtraction({
|
||||
priority: 1000,
|
||||
id: "map",
|
||||
name: "Map",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { table: true },
|
||||
rows: [...data].map(([k, v]) => ({ key: k, value: v }))
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
register({
|
||||
id: "binaryViewer",
|
||||
getExtractions(data, collector, context) {
|
||||
if (typeof data !== "number") {
|
||||
return;
|
||||
}
|
||||
|
||||
collector.addExtraction({
|
||||
priority: 100,
|
||||
id: "binary",
|
||||
name: "Bits of Integer",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { text: true },
|
||||
text: data.toString(2),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
(register, helpers) => {
|
||||
register({
|
||||
id: "positionOrRangeInTextModel",
|
||||
getExtractions(data, collector, context) {
|
||||
collector.addExtraction({
|
||||
priority: 100,
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { text: true },
|
||||
text: Object.keys(context.variablesInScope).join(", "),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
register({
|
||||
id: "positionOrRangeInTextModel",
|
||||
getExtractions(data, collector, context) {
|
||||
/** @type {{ start: { line: number; column: number; }; end: { line: number; column: number; }; }} */
|
||||
let range;
|
||||
if (isRange(data)) {
|
||||
range = {
|
||||
start: {
|
||||
line: data.startLineNumber - 1,
|
||||
column: data.startColumn - 1,
|
||||
},
|
||||
end: {
|
||||
line: data.endLineNumber - 1,
|
||||
column: data.endColumn - 1,
|
||||
},
|
||||
};
|
||||
} else if (isPosition(data)) {
|
||||
range = {
|
||||
start: {
|
||||
line: data.lineNumber - 1,
|
||||
column: data.column - 1,
|
||||
},
|
||||
end: { line: data.lineNumber - 1, column: data.column - 1 },
|
||||
};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRange(data) && !isPosition(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
const textModel = helpers.find(
|
||||
x =>
|
||||
typeof x === "object" &&
|
||||
!!x &&
|
||||
"constructor" in x &&
|
||||
x.constructor.name === "TextModel"
|
||||
);
|
||||
|
||||
collector.addExtraction({
|
||||
id: "positionOrRangeInTextModel",
|
||||
name: "Position/Range In TextModel",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { text: true },
|
||||
text: textModel.getValue(),
|
||||
decorations: [{ range }],
|
||||
});
|
||||
},
|
||||
priority: 1000,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {value is { startColumn: number; startLineNumber: number; endColumn: number; endLineNumber: number; }}
|
||||
*/
|
||||
function isRange(value) {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"startColumn" in value &&
|
||||
"startLineNumber" in value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {value is { column: number; lineNumber: number }}
|
||||
*/
|
||||
function isPosition(value) {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"column" in value &&
|
||||
"lineNumber" in value
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@hediet/node-reload": "0.7.3",
|
||||
"@types/node": "^13.7.4",
|
||||
"node-fetch": "^2.6.1",
|
||||
"typescript": "^3.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
807
demos/js/src/demo_address_book.ts
Normal file
@@ -0,0 +1,807 @@
|
||||
|
||||
class Person {
|
||||
constructor(public id: number, public firstName: string, public lastName: string, public email: string, public gender: string, public company: string) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Company {
|
||||
constructor(public id: number, public name: string, public street: string, public email: string) { }
|
||||
}
|
||||
|
||||
class AddressBook {
|
||||
public static loadPersons(): Person[] {
|
||||
const contactsData = [{
|
||||
"id": 1,
|
||||
"first_name": "Burg",
|
||||
"last_name": "Legg",
|
||||
"email": "blegg0@elpais.com",
|
||||
"gender": "Male",
|
||||
"company": "Zoozzy"
|
||||
}, {
|
||||
"id": 2,
|
||||
"first_name": "Erminia",
|
||||
"last_name": "Heersema",
|
||||
"email": "eheersema1@jugem.jp",
|
||||
"gender": "Female",
|
||||
"company": "Skipfire"
|
||||
}, {
|
||||
"id": 3,
|
||||
"first_name": "Penrod",
|
||||
"last_name": "Claxton",
|
||||
"email": "pclaxton2@furl.net",
|
||||
"gender": "Male",
|
||||
"company": "Dabvine"
|
||||
}, {
|
||||
"id": 4,
|
||||
"first_name": "Peterus",
|
||||
"last_name": "Alabone",
|
||||
"email": "palabone3@icio.us",
|
||||
"gender": "Male",
|
||||
"company": "Gabtune"
|
||||
}, {
|
||||
"id": 5,
|
||||
"first_name": "Rickie",
|
||||
"last_name": "Belsher",
|
||||
"email": "rbelsher4@marketwatch.com",
|
||||
"gender": "Male",
|
||||
"company": "Digitube"
|
||||
}, {
|
||||
"id": 6,
|
||||
"first_name": "Griffie",
|
||||
"last_name": "Poulsum",
|
||||
"email": "gpoulsum5@printfriendly.com",
|
||||
"gender": "Male",
|
||||
"company": "Mymm"
|
||||
}, {
|
||||
"id": 7,
|
||||
"first_name": "Fidelio",
|
||||
"last_name": "Gateland",
|
||||
"email": "fgateland6@intel.com",
|
||||
"gender": "Male",
|
||||
"company": "Meeveo"
|
||||
}, {
|
||||
"id": 8,
|
||||
"first_name": "Fayth",
|
||||
"last_name": "D'Oyly",
|
||||
"email": "fdoyly7@twitter.com",
|
||||
"gender": "Female",
|
||||
"company": "Podcat"
|
||||
}, {
|
||||
"id": 9,
|
||||
"first_name": "Janela",
|
||||
"last_name": "Shelton",
|
||||
"email": "jshelton8@dion.ne.jp",
|
||||
"gender": "Female",
|
||||
"company": "Kayveo"
|
||||
}, {
|
||||
"id": 10,
|
||||
"first_name": "Yance",
|
||||
"last_name": "Vatcher",
|
||||
"email": "yvatcher9@ask.com",
|
||||
"gender": "Male",
|
||||
"company": "Photobug"
|
||||
}, {
|
||||
"id": 11,
|
||||
"first_name": "Filippo",
|
||||
"last_name": "Timoney",
|
||||
"email": "ftimoneya@spotify.com",
|
||||
"gender": "Male",
|
||||
"company": "Rhyzio"
|
||||
}, {
|
||||
"id": 12,
|
||||
"first_name": "Helli",
|
||||
"last_name": "Friskey",
|
||||
"email": "hfriskeyb@usa.gov",
|
||||
"gender": "Female",
|
||||
"company": "Eare"
|
||||
}, {
|
||||
"id": 13,
|
||||
"first_name": "Enos",
|
||||
"last_name": "Connick",
|
||||
"email": "econnickc@sitemeter.com",
|
||||
"gender": "Male",
|
||||
"company": "Meetz"
|
||||
}, {
|
||||
"id": 14,
|
||||
"first_name": "Lorri",
|
||||
"last_name": "Chessun",
|
||||
"email": "lchessund@wp.com",
|
||||
"gender": "Female",
|
||||
"company": "Jaxworks"
|
||||
}, {
|
||||
"id": 15,
|
||||
"first_name": "Justina",
|
||||
"last_name": "Spedroni",
|
||||
"email": "jspedronie@abc.net.au",
|
||||
"gender": "Female",
|
||||
"company": "Rhyzio"
|
||||
}, {
|
||||
"id": 16,
|
||||
"first_name": "Odilia",
|
||||
"last_name": "Ealden",
|
||||
"email": "oealdenf@fastcompany.com",
|
||||
"gender": "Female",
|
||||
"company": "Blognation"
|
||||
}, {
|
||||
"id": 17,
|
||||
"first_name": "Dale",
|
||||
"last_name": "Bretherick",
|
||||
"email": "dbretherickg@dmoz.org",
|
||||
"gender": "Male",
|
||||
"company": "Skipfire"
|
||||
}, {
|
||||
"id": 18,
|
||||
"first_name": "Syman",
|
||||
"last_name": "Lawdham",
|
||||
"email": "slawdhamh@so-net.ne.jp",
|
||||
"gender": "Male",
|
||||
"company": "Wordify"
|
||||
}, {
|
||||
"id": 19,
|
||||
"first_name": "Catarina",
|
||||
"last_name": "Stoile",
|
||||
"email": "cstoilei@hud.gov",
|
||||
"gender": "Female",
|
||||
"company": "Avaveo"
|
||||
}, {
|
||||
"id": 20,
|
||||
"first_name": "Simonette",
|
||||
"last_name": "Danett",
|
||||
"email": "sdanettj@eepurl.com",
|
||||
"gender": "Female",
|
||||
"company": "Realblab"
|
||||
}, {
|
||||
"id": 21,
|
||||
"first_name": "Fielding",
|
||||
"last_name": "MacHarg",
|
||||
"email": "fmachargk@blogtalkradio.com",
|
||||
"gender": "Male",
|
||||
"company": "Photolist"
|
||||
}, {
|
||||
"id": 22,
|
||||
"first_name": "Anni",
|
||||
"last_name": "Hounsom",
|
||||
"email": "ahounsoml@wikia.com",
|
||||
"gender": "Female",
|
||||
"company": "Devify"
|
||||
}, {
|
||||
"id": 23,
|
||||
"first_name": "Evelin",
|
||||
"last_name": "Gaytor",
|
||||
"email": "egaytorm@myspace.com",
|
||||
"gender": "Male",
|
||||
"company": "Aivee"
|
||||
}, {
|
||||
"id": 24,
|
||||
"first_name": "Lynde",
|
||||
"last_name": "Itscowicz",
|
||||
"email": "litscowiczn@fc2.com",
|
||||
"gender": "Female",
|
||||
"company": "Skipfire"
|
||||
}, {
|
||||
"id": 25,
|
||||
"first_name": "Reta",
|
||||
"last_name": "Felipe",
|
||||
"email": "rfelipeo@free.fr",
|
||||
"gender": "Female",
|
||||
"company": "Ainyx"
|
||||
}, {
|
||||
"id": 26,
|
||||
"first_name": "Bar",
|
||||
"last_name": "Shillingford",
|
||||
"email": "bshillingfordp@sun.com",
|
||||
"gender": "Male",
|
||||
"company": "Skilith"
|
||||
}, {
|
||||
"id": 27,
|
||||
"first_name": "Mel",
|
||||
"last_name": "Cooch",
|
||||
"email": "mcoochq@icq.com",
|
||||
"gender": "Female",
|
||||
"company": "Meevee"
|
||||
}, {
|
||||
"id": 28,
|
||||
"first_name": "Wheeler",
|
||||
"last_name": "Kettlesting",
|
||||
"email": "wkettlestingr@miitbeian.gov.cn",
|
||||
"gender": "Male",
|
||||
"company": "Mybuzz"
|
||||
}, {
|
||||
"id": 29,
|
||||
"first_name": "Corabel",
|
||||
"last_name": "Pordall",
|
||||
"email": "cpordalls@sourceforge.net",
|
||||
"gender": "Female",
|
||||
"company": "Blogtag"
|
||||
}, {
|
||||
"id": 30,
|
||||
"first_name": "Merrily",
|
||||
"last_name": "Weatherhill",
|
||||
"email": "mweatherhillt@printfriendly.com",
|
||||
"gender": "Female",
|
||||
"company": "Divape"
|
||||
}, {
|
||||
"id": 31,
|
||||
"first_name": "Reynard",
|
||||
"last_name": "Osgodby",
|
||||
"email": "rosgodbyu@eepurl.com",
|
||||
"gender": "Male",
|
||||
"company": "Yakijo"
|
||||
}, {
|
||||
"id": 32,
|
||||
"first_name": "Guilbert",
|
||||
"last_name": "Bilston",
|
||||
"email": "gbilstonv@devhub.com",
|
||||
"gender": "Male",
|
||||
"company": "Vinder"
|
||||
}, {
|
||||
"id": 33,
|
||||
"first_name": "Farr",
|
||||
"last_name": "Skehan",
|
||||
"email": "fskehanw@t-online.de",
|
||||
"gender": "Male",
|
||||
"company": "Linkbuzz"
|
||||
}, {
|
||||
"id": 34,
|
||||
"first_name": "Roger",
|
||||
"last_name": "Langelay",
|
||||
"email": "rlangelayx@pcworld.com",
|
||||
"gender": "Male",
|
||||
"company": "Tekfly"
|
||||
}, {
|
||||
"id": 35,
|
||||
"first_name": "Jemmy",
|
||||
"last_name": "Yggo",
|
||||
"email": "jyggoy@ifeng.com",
|
||||
"gender": "Female",
|
||||
"company": "Jabberstorm"
|
||||
}, {
|
||||
"id": 36,
|
||||
"first_name": "Cybill",
|
||||
"last_name": "Chastaing",
|
||||
"email": "cchastaingz@mayoclinic.com",
|
||||
"gender": "Female",
|
||||
"company": "Riffpedia"
|
||||
}, {
|
||||
"id": 37,
|
||||
"first_name": "Samuele",
|
||||
"last_name": "Zorzoni",
|
||||
"email": "szorzoni10@opensource.org",
|
||||
"gender": "Male",
|
||||
"company": "Devcast"
|
||||
}, {
|
||||
"id": 38,
|
||||
"first_name": "Svend",
|
||||
"last_name": "Hamill",
|
||||
"email": "shamill11@google.co.jp",
|
||||
"gender": "Male",
|
||||
"company": "Kwilith"
|
||||
}, {
|
||||
"id": 39,
|
||||
"first_name": "Carlin",
|
||||
"last_name": "Roote",
|
||||
"email": "croote12@gov.uk",
|
||||
"gender": "Male",
|
||||
"company": "Twitterlist"
|
||||
}, {
|
||||
"id": 40,
|
||||
"first_name": "Dawna",
|
||||
"last_name": "Lanfer",
|
||||
"email": "dlanfer13@mlb.com",
|
||||
"gender": "Female",
|
||||
"company": "Jayo"
|
||||
}, {
|
||||
"id": 41,
|
||||
"first_name": "Melissa",
|
||||
"last_name": "Chappelow",
|
||||
"email": "mchappelow14@usa.gov",
|
||||
"gender": "Female",
|
||||
"company": "Skibox"
|
||||
}, {
|
||||
"id": 42,
|
||||
"first_name": "George",
|
||||
"last_name": "Hargreaves",
|
||||
"email": "ghargreaves15@pagesperso-orange.fr",
|
||||
"gender": "Female",
|
||||
"company": "Zazio"
|
||||
}, {
|
||||
"id": 43,
|
||||
"first_name": "Sampson",
|
||||
"last_name": "Steaning",
|
||||
"email": "ssteaning16@state.gov",
|
||||
"gender": "Male",
|
||||
"company": "Jaxworks"
|
||||
}, {
|
||||
"id": 44,
|
||||
"first_name": "Brana",
|
||||
"last_name": "Wims",
|
||||
"email": "bwims17@icio.us",
|
||||
"gender": "Female",
|
||||
"company": "Flashset"
|
||||
}, {
|
||||
"id": 45,
|
||||
"first_name": "Shawn",
|
||||
"last_name": "Saffen",
|
||||
"email": "ssaffen18@shutterfly.com",
|
||||
"gender": "Male",
|
||||
"company": "Twitterwire"
|
||||
}, {
|
||||
"id": 46,
|
||||
"first_name": "Alessandro",
|
||||
"last_name": "Gasker",
|
||||
"email": "agasker19@slashdot.org",
|
||||
"gender": "Male",
|
||||
"company": "Dabtype"
|
||||
}, {
|
||||
"id": 47,
|
||||
"first_name": "Bobby",
|
||||
"last_name": "Mulcock",
|
||||
"email": "bmulcock1a@time.com",
|
||||
"gender": "Female",
|
||||
"company": "Quamba"
|
||||
}, {
|
||||
"id": 48,
|
||||
"first_name": "Ashlen",
|
||||
"last_name": "Midner",
|
||||
"email": "amidner1b@devhub.com",
|
||||
"gender": "Female",
|
||||
"company": "Thoughtstorm"
|
||||
}, {
|
||||
"id": 49,
|
||||
"first_name": "Mallory",
|
||||
"last_name": "Joel",
|
||||
"email": "mjoel1c@upenn.edu",
|
||||
"gender": "Male",
|
||||
"company": "Twitterbeat"
|
||||
}, {
|
||||
"id": 50,
|
||||
"first_name": "Maximilian",
|
||||
"last_name": "Scraggs",
|
||||
"email": "mscraggs1d@ucla.edu",
|
||||
"gender": "Male",
|
||||
"company": "Twiyo"
|
||||
}, {
|
||||
"id": 51,
|
||||
"first_name": "Beverlie",
|
||||
"last_name": "Pittet",
|
||||
"email": "bpittet1e@github.io",
|
||||
"gender": "Female",
|
||||
"company": "Riffpath"
|
||||
}, {
|
||||
"id": 52,
|
||||
"first_name": "Enrique",
|
||||
"last_name": "Rentilll",
|
||||
"email": "erentilll1f@smugmug.com",
|
||||
"gender": "Male",
|
||||
"company": "Blognation"
|
||||
}, {
|
||||
"id": 53,
|
||||
"first_name": "Cathrin",
|
||||
"last_name": "McKendry",
|
||||
"email": "cmckendry1g@hc360.com",
|
||||
"gender": "Female",
|
||||
"company": "Shuffledrive"
|
||||
}, {
|
||||
"id": 54,
|
||||
"first_name": "Isabeau",
|
||||
"last_name": "Quincey",
|
||||
"email": "iquincey1h@msn.com",
|
||||
"gender": "Female",
|
||||
"company": "Tagcat"
|
||||
}, {
|
||||
"id": 55,
|
||||
"first_name": "Geoffrey",
|
||||
"last_name": "Grzegorczyk",
|
||||
"email": "ggrzegorczyk1i@loc.gov",
|
||||
"gender": "Male",
|
||||
"company": "Youfeed"
|
||||
}, {
|
||||
"id": 56,
|
||||
"first_name": "Ruggiero",
|
||||
"last_name": "Vaar",
|
||||
"email": "rvaar1j@unesco.org",
|
||||
"gender": "Male",
|
||||
"company": "Babbleset"
|
||||
}, {
|
||||
"id": 57,
|
||||
"first_name": "Alfonso",
|
||||
"last_name": "Tarzey",
|
||||
"email": "atarzey1k@weebly.com",
|
||||
"gender": "Male",
|
||||
"company": "Pixonyx"
|
||||
}, {
|
||||
"id": 58,
|
||||
"first_name": "Marylin",
|
||||
"last_name": "Bissex",
|
||||
"email": "mbissex1l@microsoft.com",
|
||||
"gender": "Female",
|
||||
"company": "Tagfeed"
|
||||
}, {
|
||||
"id": 59,
|
||||
"first_name": "Cathryn",
|
||||
"last_name": "Morhall",
|
||||
"email": "cmorhall1m@irs.gov",
|
||||
"gender": "Female",
|
||||
"company": "Tagopia"
|
||||
}, {
|
||||
"id": 60,
|
||||
"first_name": "Terza",
|
||||
"last_name": "Easey",
|
||||
"email": "teasey1n@thetimes.co.uk",
|
||||
"gender": "Female",
|
||||
"company": "Devcast"
|
||||
}, {
|
||||
"id": 61,
|
||||
"first_name": "Hale",
|
||||
"last_name": "Gaudon",
|
||||
"email": "hgaudon1o@webnode.com",
|
||||
"gender": "Male",
|
||||
"company": "Quinu"
|
||||
}, {
|
||||
"id": 62,
|
||||
"first_name": "Hadley",
|
||||
"last_name": "Elwood",
|
||||
"email": "helwood1p@japanpost.jp",
|
||||
"gender": "Male",
|
||||
"company": "Plajo"
|
||||
}, {
|
||||
"id": 63,
|
||||
"first_name": "Seamus",
|
||||
"last_name": "Straker",
|
||||
"email": "sstraker1q@live.com",
|
||||
"gender": "Male",
|
||||
"company": "Yoveo"
|
||||
}, {
|
||||
"id": 64,
|
||||
"first_name": "Urbano",
|
||||
"last_name": "Gisbey",
|
||||
"email": "ugisbey1r@sitemeter.com",
|
||||
"gender": "Male",
|
||||
"company": "Quinu"
|
||||
}, {
|
||||
"id": 65,
|
||||
"first_name": "Gerri",
|
||||
"last_name": "McInulty",
|
||||
"email": "gmcinulty1s@businessinsider.com",
|
||||
"gender": "Male",
|
||||
"company": "Agimba"
|
||||
}, {
|
||||
"id": 66,
|
||||
"first_name": "Lena",
|
||||
"last_name": "Gonthier",
|
||||
"email": "lgonthier1t@newsvine.com",
|
||||
"gender": "Female",
|
||||
"company": "Geba"
|
||||
}, {
|
||||
"id": 67,
|
||||
"first_name": "Ameline",
|
||||
"last_name": "Wheatcroft",
|
||||
"email": "awheatcroft1u@163.com",
|
||||
"gender": "Female",
|
||||
"company": "Twitternation"
|
||||
}, {
|
||||
"id": 68,
|
||||
"first_name": "Kathryn",
|
||||
"last_name": "Castelli",
|
||||
"email": "kcastelli1v@about.com",
|
||||
"gender": "Female",
|
||||
"company": "Photospace"
|
||||
}, {
|
||||
"id": 69,
|
||||
"first_name": "Terrell",
|
||||
"last_name": "Walicki",
|
||||
"email": "twalicki1w@amazon.co.jp",
|
||||
"gender": "Male",
|
||||
"company": "Babblestorm"
|
||||
}, {
|
||||
"id": 70,
|
||||
"first_name": "Claudianus",
|
||||
"last_name": "Tremathack",
|
||||
"email": "ctremathack1x@walmart.com",
|
||||
"gender": "Male",
|
||||
"company": "Skyndu"
|
||||
}, {
|
||||
"id": 71,
|
||||
"first_name": "Sanford",
|
||||
"last_name": "Devinn",
|
||||
"email": "sdevinn1y@ehow.com",
|
||||
"gender": "Male",
|
||||
"company": "Dynazzy"
|
||||
}, {
|
||||
"id": 72,
|
||||
"first_name": "Gwendolen",
|
||||
"last_name": "Turfrey",
|
||||
"email": "gturfrey1z@va.gov",
|
||||
"gender": "Female",
|
||||
"company": "Yacero"
|
||||
}, {
|
||||
"id": 73,
|
||||
"first_name": "Alta",
|
||||
"last_name": "Leap",
|
||||
"email": "aleap20@ezinearticles.com",
|
||||
"gender": "Female",
|
||||
"company": "Twitterlist"
|
||||
}, {
|
||||
"id": 74,
|
||||
"first_name": "Johnath",
|
||||
"last_name": "Andrzejowski",
|
||||
"email": "jandrzejowski21@google.com.hk",
|
||||
"gender": "Female",
|
||||
"company": "Lazzy"
|
||||
}, {
|
||||
"id": 75,
|
||||
"first_name": "Yancy",
|
||||
"last_name": "Askham",
|
||||
"email": "yaskham22@sciencedaily.com",
|
||||
"gender": "Male",
|
||||
"company": "Yabox"
|
||||
}, {
|
||||
"id": 76,
|
||||
"first_name": "Paula",
|
||||
"last_name": "Warder",
|
||||
"email": "pwarder23@harvard.edu",
|
||||
"gender": "Female",
|
||||
"company": "Zooxo"
|
||||
}, {
|
||||
"id": 77,
|
||||
"first_name": "Grannie",
|
||||
"last_name": "Hauxley",
|
||||
"email": "ghauxley24@symantec.com",
|
||||
"gender": "Male",
|
||||
"company": "Yakidoo"
|
||||
}, {
|
||||
"id": 78,
|
||||
"first_name": "Enid",
|
||||
"last_name": "Grealy",
|
||||
"email": "egrealy25@homestead.com",
|
||||
"gender": "Female",
|
||||
"company": "Gabcube"
|
||||
}, {
|
||||
"id": 79,
|
||||
"first_name": "Lazarus",
|
||||
"last_name": "Itscowicz",
|
||||
"email": "litscowicz26@epa.gov",
|
||||
"gender": "Male",
|
||||
"company": "Demizz"
|
||||
}, {
|
||||
"id": 80,
|
||||
"first_name": "Marmaduke",
|
||||
"last_name": "Gilligan",
|
||||
"email": "mgilligan27@tiny.cc",
|
||||
"gender": "Male",
|
||||
"company": "Yombu"
|
||||
}, {
|
||||
"id": 81,
|
||||
"first_name": "Linnell",
|
||||
"last_name": "Chree",
|
||||
"email": "lchree28@ocn.ne.jp",
|
||||
"gender": "Female",
|
||||
"company": "Buzzshare"
|
||||
}, {
|
||||
"id": 82,
|
||||
"first_name": "Pavia",
|
||||
"last_name": "Pocke",
|
||||
"email": "ppocke29@newsvine.com",
|
||||
"gender": "Female",
|
||||
"company": "Eidel"
|
||||
}, {
|
||||
"id": 83,
|
||||
"first_name": "Golda",
|
||||
"last_name": "Stuchburie",
|
||||
"email": "gstuchburie2a@oaic.gov.au",
|
||||
"gender": "Female",
|
||||
"company": "Flipstorm"
|
||||
}, {
|
||||
"id": 84,
|
||||
"first_name": "Trace",
|
||||
"last_name": "Lodge",
|
||||
"email": "tlodge2b@whitehouse.gov",
|
||||
"gender": "Male",
|
||||
"company": "Zazio"
|
||||
}, {
|
||||
"id": 85,
|
||||
"first_name": "Nikolia",
|
||||
"last_name": "Bartolomivis",
|
||||
"email": "nbartolomivis2c@opensource.org",
|
||||
"gender": "Female",
|
||||
"company": "Devshare"
|
||||
}, {
|
||||
"id": 86,
|
||||
"first_name": "Dela",
|
||||
"last_name": "Wimp",
|
||||
"email": "dwimp2d@miitbeian.gov.cn",
|
||||
"gender": "Female",
|
||||
"company": "Wordware"
|
||||
}, {
|
||||
"id": 87,
|
||||
"first_name": "Rustie",
|
||||
"last_name": "Drury",
|
||||
"email": "rdrury2e@sun.com",
|
||||
"gender": "Male",
|
||||
"company": "Youbridge"
|
||||
}, {
|
||||
"id": 88,
|
||||
"first_name": "Phedra",
|
||||
"last_name": "Shrimptone",
|
||||
"email": "pshrimptone2f@unicef.org",
|
||||
"gender": "Female",
|
||||
"company": "Realcube"
|
||||
}, {
|
||||
"id": 89,
|
||||
"first_name": "Alaster",
|
||||
"last_name": "Vittori",
|
||||
"email": "avittori2g@senate.gov",
|
||||
"gender": "Male",
|
||||
"company": "Livepath"
|
||||
}, {
|
||||
"id": 90,
|
||||
"first_name": "Pascal",
|
||||
"last_name": "Neissen",
|
||||
"email": "pneissen2h@mediafire.com",
|
||||
"gender": "Male",
|
||||
"company": "Mydo"
|
||||
}, {
|
||||
"id": 91,
|
||||
"first_name": "Brian",
|
||||
"last_name": "Manuel",
|
||||
"email": "bmanuel2i@blog.com",
|
||||
"gender": "Male",
|
||||
"company": "Roomm"
|
||||
}, {
|
||||
"id": 92,
|
||||
"first_name": "Jen",
|
||||
"last_name": "Laydon",
|
||||
"email": "jlaydon2j@hubpages.com",
|
||||
"gender": "Female",
|
||||
"company": "Plambee"
|
||||
}, {
|
||||
"id": 93,
|
||||
"first_name": "Beltran",
|
||||
"last_name": "Brandoni",
|
||||
"email": "bbrandoni2k@digg.com",
|
||||
"gender": "Male",
|
||||
"company": "Yacero"
|
||||
}, {
|
||||
"id": 94,
|
||||
"first_name": "Sarene",
|
||||
"last_name": "Gaymar",
|
||||
"email": "sgaymar2l@surveymonkey.com",
|
||||
"gender": "Female",
|
||||
"company": "Katz"
|
||||
}, {
|
||||
"id": 95,
|
||||
"first_name": "Iormina",
|
||||
"last_name": "Peedell",
|
||||
"email": "ipeedell2m@seattletimes.com",
|
||||
"gender": "Female",
|
||||
"company": "Youfeed"
|
||||
}, {
|
||||
"id": 96,
|
||||
"first_name": "Abigail",
|
||||
"last_name": "Kielty",
|
||||
"email": "akielty2n@npr.org",
|
||||
"gender": "Female",
|
||||
"company": "Reallinks"
|
||||
}, {
|
||||
"id": 97,
|
||||
"first_name": "Hallie",
|
||||
"last_name": "Toomey",
|
||||
"email": "htoomey2o@google.com.au",
|
||||
"gender": "Female",
|
||||
"company": "Flipbug"
|
||||
}, {
|
||||
"id": 98,
|
||||
"first_name": "Sergeant",
|
||||
"last_name": "Pourveer",
|
||||
"email": "spourveer2p@amazon.co.uk",
|
||||
"gender": "Male",
|
||||
"company": "Skinix"
|
||||
}, {
|
||||
"id": 99,
|
||||
"first_name": "Elfie",
|
||||
"last_name": "McKinlay",
|
||||
"email": "emckinlay2q@xrea.com",
|
||||
"gender": "Female",
|
||||
"company": "Devcast"
|
||||
}, {
|
||||
"id": 100,
|
||||
"first_name": "Wainwright",
|
||||
"last_name": "Juggins",
|
||||
"email": "wjuggins2r@tamu.edu",
|
||||
"gender": "Male",
|
||||
"company": "Topiclounge"
|
||||
}];
|
||||
|
||||
|
||||
|
||||
const contacts = contactsData.map(d => new Person(d.id, d.first_name, d.last_name, d.email, d.gender, d.company));
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public static loadCompanies() {
|
||||
const companies = [{
|
||||
"id": 101,
|
||||
"city": "Lakshmīpur",
|
||||
"street": "36300 Lake View Center",
|
||||
"email": "ogovier0@gmpg.org",
|
||||
"company": "Fiveclub"
|
||||
}, {
|
||||
"id": 102,
|
||||
"city": "El Quetzal",
|
||||
"street": "551 New Castle Parkway",
|
||||
"email": "elander1@g.co",
|
||||
"company": "Twitterwire"
|
||||
}, {
|
||||
"id": 103,
|
||||
"city": "Mayakovski",
|
||||
"street": "1 Norway Maple Center",
|
||||
"email": "dcustance2@hostgator.com",
|
||||
"company": "Jabbersphere"
|
||||
}, {
|
||||
"id": 104,
|
||||
"city": "Al Jawādīyah",
|
||||
"street": "02 Carey Hill",
|
||||
"email": "evanoort3@sciencedaily.com",
|
||||
"company": "Zoovu"
|
||||
}, {
|
||||
"id": 105,
|
||||
"city": "Girona",
|
||||
"street": "3 Lillian Center",
|
||||
"email": "dducker4@cbsnews.com",
|
||||
"company": "Innojam"
|
||||
}, {
|
||||
"id": 106,
|
||||
"city": "Vouani",
|
||||
"street": "5165 Vera Avenue",
|
||||
"email": "orzehorz5@arizona.edu",
|
||||
"company": "Zoovu"
|
||||
}, {
|
||||
"id": 107,
|
||||
"city": "Caibarién",
|
||||
"street": "3739 Burrows Drive",
|
||||
"email": "ltrahair6@pinterest.com",
|
||||
"company": "Quamba"
|
||||
}, {
|
||||
"id": 108,
|
||||
"city": "Wushi",
|
||||
"street": "88 Bay Hill",
|
||||
"email": "plebourn7@springer.com",
|
||||
"company": "Feednation"
|
||||
}, {
|
||||
"id": 109,
|
||||
"city": "Zbýšov",
|
||||
"street": "38 Dovetail Place",
|
||||
"email": "slace8@netscape.com",
|
||||
"company": "Zoomzone"
|
||||
}, {
|
||||
"id": 110,
|
||||
"city": "Nová Cerekev",
|
||||
"street": "90194 Dixon Avenue",
|
||||
"email": "mdoxey9@goo.gl",
|
||||
"company": "Devpoint"
|
||||
}];
|
||||
|
||||
return companies.map(c => new Company(c.id, c.company, c.street, c.email));
|
||||
}
|
||||
|
||||
public static loadContacts() {
|
||||
const result = new Array<Person | Company>();
|
||||
result.push(...this.loadPersons());
|
||||
result.push(...this.loadCompanies());
|
||||
let idx = 0;
|
||||
while (idx++ < 1000) {
|
||||
const i = Math.round(Math.random() * (result.length - 1));
|
||||
const j = Math.round(Math.random() * (result.length - 1));
|
||||
const temp = result[i];
|
||||
result[i] = result[j];
|
||||
result[j] = temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const contacts = AddressBook.loadContacts();
|
||||
|
||||
debugger;
|
||||
@@ -10,49 +10,70 @@ setTimeout(() => {
|
||||
}, 0);
|
||||
|
||||
class Main {
|
||||
/** @pure */
|
||||
run() {
|
||||
const list = new DoublyLinkedList("1");
|
||||
list.setNext(new DoublyLinkedList("2"));
|
||||
list.next!.setNext(new DoublyLinkedList("3"));
|
||||
list.next!.next!.setNext(new DoublyLinkedList("4"));
|
||||
|
||||
// Use `visualize()` as expression to visualize in the Debug Visualizer!
|
||||
const visualize = () =>
|
||||
createGraphFromPointers({ list, last, cur }, i => ({
|
||||
id: i.id,
|
||||
label: i.name,
|
||||
color: finished.has(i) ? "lime" : undefined,
|
||||
edges: [
|
||||
{ to: i.next!, label: "next" },
|
||||
{ to: i.prev!, label: "prev", color: "lightgray" },
|
||||
].filter(r => !!r.to),
|
||||
}));
|
||||
|
||||
const finished = new Set();
|
||||
var cur: DoublyLinkedList | undefined = list;
|
||||
// Reverses `list`. Finished nodes have correct pointers,
|
||||
// their next node is also finished.
|
||||
var last: DoublyLinkedList | undefined = undefined;
|
||||
while (cur) {
|
||||
cur.prev = cur.next;
|
||||
cur.next = last;
|
||||
finished.add(cur);
|
||||
last = cur;
|
||||
cur = cur.prev;
|
||||
}
|
||||
id = 0;
|
||||
const head = new DoublyLinkedListNode("1");
|
||||
head.setNext(new DoublyLinkedListNode("2"));
|
||||
head.next!.setNext(new DoublyLinkedListNode("3"));
|
||||
const tail = new DoublyLinkedListNode("4");
|
||||
head.next!.next!.setNext(tail);
|
||||
reverse(new DoublyLinkedList(head, tail));
|
||||
console.log("finished");
|
||||
}
|
||||
}
|
||||
|
||||
let id = 0;
|
||||
function reverse(list: DoublyLinkedList) {
|
||||
// Open a new Debug Visualizer
|
||||
// and enter `visualize()`!
|
||||
const visualize = () =>
|
||||
createGraphFromPointers(
|
||||
{
|
||||
last,
|
||||
"list.head": list.head,
|
||||
"list.tail": list.tail
|
||||
},
|
||||
i => ({
|
||||
id: i.id,
|
||||
label: i.name,
|
||||
color: finished.has(i) ? "lime" : "lightblue",
|
||||
edges: [
|
||||
{ to: i.next!, label: "next", color: "lightblue", },
|
||||
{ to: i.prev!, label: "prev", color: "lightgray" },
|
||||
].filter(r => !!r.to),
|
||||
}));
|
||||
|
||||
// Finished nodes have correct pointers,
|
||||
// their next node is also finished.
|
||||
const finished = new Set();
|
||||
var last: DoublyLinkedListNode | null = null;
|
||||
list.tail = list.head;
|
||||
while (list.head) {
|
||||
list.head.prev = list.head.next;
|
||||
list.head.next = last;
|
||||
finished.add(list.head);
|
||||
last = list.head;
|
||||
list.head = list.head.prev;
|
||||
}
|
||||
list.head = last;
|
||||
}
|
||||
|
||||
class DoublyLinkedList {
|
||||
constructor(
|
||||
public head: DoublyLinkedListNode | null,
|
||||
public tail: DoublyLinkedListNode | null
|
||||
) { }
|
||||
}
|
||||
|
||||
let id = 0;
|
||||
class DoublyLinkedListNode {
|
||||
public readonly id = (id++).toString();
|
||||
constructor(public name: string) {}
|
||||
constructor(public name: string) { }
|
||||
|
||||
next: DoublyLinkedList | undefined;
|
||||
prev: DoublyLinkedList | undefined;
|
||||
next: DoublyLinkedListNode | null = null;
|
||||
prev: DoublyLinkedListNode | null = null;
|
||||
|
||||
public setNext(val: DoublyLinkedList): void {
|
||||
public setNext(val: DoublyLinkedListNode): void {
|
||||
val.prev = this;
|
||||
this.next = val;
|
||||
}
|
||||
|
||||
7
demos/js/src/demo_fetch.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
fetch("https://jsonplaceholder.typicode.com/users")
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
debugger;
|
||||
});
|
||||
@@ -1,12 +1,20 @@
|
||||
// visualize `data`
|
||||
const data = new Array<number>();
|
||||
let value = 0;
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
const delta = Math.random() > 0.5 ? 1 : -1;
|
||||
data.push(value);
|
||||
value += delta;
|
||||
let curValue = 0;
|
||||
|
||||
if (i % 1000 === 0) {
|
||||
debugger;
|
||||
for (let j = 0; j < 100; j++) {
|
||||
addManyRandomValues();
|
||||
}
|
||||
|
||||
function addManyRandomValues() {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
addRandomValue();
|
||||
}
|
||||
}
|
||||
|
||||
function addRandomValue() {
|
||||
const delta = Math.random()
|
||||
> 0.5 ? 1 : -1;
|
||||
data.push(curValue);
|
||||
curValue += delta;
|
||||
}
|
||||
@@ -13,9 +13,13 @@ hedietDbgVis.markedGrid(
|
||||
*/
|
||||
|
||||
// From https://github.com/AvraamMavridis/Algorithms-Data-Structures-in-Typescript/blob/master/algorithms/quickSort.md
|
||||
const array = [1, 2, 33, 31, 1, 2, 63, 123, 6, 32, 943, 346, 24];
|
||||
const sorted = quickSort(array, 0, array.length - 1);
|
||||
console.log(sorted);
|
||||
/** @pure */
|
||||
function main() {
|
||||
const array = [1, 2, 33, 31, 1, 2, 63, 123, 6, 32, 943, 346, 24];
|
||||
const sorted = quickSort(array, 0, array.length - 1);
|
||||
console.log(sorted);
|
||||
}
|
||||
main();
|
||||
|
||||
function swap(array: Array<number>, i: number, j: number) {
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
|
||||
@@ -11,13 +11,14 @@ setTimeout(() => {
|
||||
|
||||
class Main {
|
||||
run() {
|
||||
const files = new Map<string, string>([
|
||||
/*const files = new Map<string, string>([
|
||||
[
|
||||
"main.ts",
|
||||
`
|
||||
class Test1 {
|
||||
public foo(a: number) {
|
||||
const x = { a: 5 };
|
||||
const y = { a: 5 };
|
||||
}
|
||||
}
|
||||
`,
|
||||
@@ -34,7 +35,7 @@ class Test1 {
|
||||
const c = prog.getTypeChecker();
|
||||
let myValue = undefined; // Visualize `myValue` here!
|
||||
const sourceFileAst = prog.getSourceFiles()[0];
|
||||
myValue = sourceFileAst.getText();
|
||||
myValue = sourceFileAst;
|
||||
console.log("myValue is the source code of the AST");
|
||||
debugger;
|
||||
|
||||
@@ -64,7 +65,24 @@ class Test1 {
|
||||
},
|
||||
};
|
||||
console.log("myValue is AST, annotated with symbol information");
|
||||
debugger;
|
||||
debugger;*/
|
||||
require;
|
||||
|
||||
const sf = ts.createSourceFile("main.ts",
|
||||
`
|
||||
class Test1 {
|
||||
public foo(a: number) {
|
||||
const x = { a: 5 };
|
||||
const y = { a: 24 };
|
||||
}
|
||||
}
|
||||
`, ts.ScriptTarget.ESNext, true);
|
||||
const traverse = (node: ts.Node) => {
|
||||
ts.forEachChild(node, child => {
|
||||
traverse(child);
|
||||
});
|
||||
};
|
||||
traverse(sf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,11 @@ fsevents@^2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a"
|
||||
integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
@@ -296,6 +301,13 @@ has-flag@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
io-ts@^1.8.6:
|
||||
version "1.10.4"
|
||||
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2"
|
||||
@@ -310,6 +322,13 @@ is-binary-path@^2.1.0:
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.2.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
|
||||
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
@@ -368,11 +387,21 @@ nise@^1.4.10:
|
||||
lolex "^4.1.0"
|
||||
path-to-regexp "^1.7.0"
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
normalize-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
path-parse@^1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-to-regexp@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
|
||||
@@ -402,6 +431,14 @@ readdirp@^3.1.1:
|
||||
dependencies:
|
||||
picomatch "^2.0.4"
|
||||
|
||||
resolve@>=1.9.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
dependencies:
|
||||
is-core-module "^2.2.0"
|
||||
path-parse "^1.0.6"
|
||||
|
||||
sinon@^7.2.7:
|
||||
version "7.3.2"
|
||||
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.3.2.tgz#82dba3a6d85f6d2181e1eca2c10d8657c2161f28"
|
||||
@@ -447,15 +484,22 @@ to-regex-range@^5.0.1:
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
ttypescript@^1.5.12:
|
||||
version "1.5.12"
|
||||
resolved "https://registry.yarnpkg.com/ttypescript/-/ttypescript-1.5.12.tgz#27a8356d7d4e719d0075a8feb4df14b52384f044"
|
||||
integrity sha512-1ojRyJvpnmgN9kIHmUnQPlEV1gq+VVsxVYjk/NfvMlHSmYxjK5hEvOOU2MQASrbekTUiUM7pR/nXeCc8bzvMOQ==
|
||||
dependencies:
|
||||
resolve ">=1.9.0"
|
||||
|
||||
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
typescript@^3.8.2:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^3.9.0:
|
||||
version "3.9.9"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674"
|
||||
integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==
|
||||
|
||||
ws@^7.0.0:
|
||||
version "7.1.1"
|
||||
|
||||
7
demos/python/Person.py
Normal file
@@ -0,0 +1,7 @@
|
||||
class Person:
|
||||
def __init__(self, name, parents=None) -> None:
|
||||
self.name = name
|
||||
self.parents = [] if parents is None else parents
|
||||
|
||||
def addParent(self, parent: "Person"):
|
||||
self.parents.append(parent)
|
||||
44
demos/python/debugvisualizer.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from Person import Person
|
||||
import json
|
||||
from vscodedebugvisualizer import globalVisualizationFactory
|
||||
|
||||
|
||||
class PersonVisualizer:
|
||||
def checkType(self, t):
|
||||
return isinstance(t, Person)
|
||||
|
||||
def visualizePerson(self, person: Person, nodes=[], edges=[]):
|
||||
if person.name in [n["id"] for n in nodes]:
|
||||
return nodes, edges
|
||||
|
||||
nodes.append(
|
||||
{
|
||||
"id": person.name,
|
||||
"label": person.name,
|
||||
}
|
||||
)
|
||||
|
||||
for p in person.parents:
|
||||
nodes, edges = self.visualizePerson(p, nodes, edges)
|
||||
edges.append(
|
||||
{
|
||||
"from": p.name,
|
||||
"to": person.name,
|
||||
}
|
||||
)
|
||||
|
||||
return nodes, edges
|
||||
|
||||
def visualize(self, person: Person):
|
||||
jsonDict = {
|
||||
"kind": {"graph": True},
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
}
|
||||
|
||||
self.visualizePerson(person, jsonDict["nodes"], jsonDict["edges"])
|
||||
|
||||
return json.dumps(jsonDict)
|
||||
|
||||
|
||||
globalVisualizationFactory.addVisualizer(PersonVisualizer())
|
||||
123
demos/python/demo.py
Normal file
@@ -0,0 +1,123 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
# put "x" in the Debug Visualizer and use step by step
|
||||
# debugging to see the diffrent types of data visualization
|
||||
|
||||
|
||||
x = 5
|
||||
x = "asdf"
|
||||
x = 5.5
|
||||
x = [1, 2, 3, 4, 5, 6, "a", "b"]
|
||||
x = ["b", "a", "c", "d", "e"]
|
||||
x.sort()
|
||||
x = {
|
||||
"asdf1": "dasdf",
|
||||
"asdf2": "dasdf",
|
||||
"asdf3": {"foo": "bar"},
|
||||
}
|
||||
|
||||
x = [1, 2, 3, 4, 5, 6]
|
||||
# histogram
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
|
||||
|
||||
# one dimension
|
||||
x = np.arange(100)
|
||||
x = np.linspace(-np.pi, np.pi, 100)
|
||||
x = np.sin(x)
|
||||
|
||||
# 2 dimension
|
||||
x = x.reshape(5, 20)
|
||||
# 2 dimension transpose
|
||||
x = x.transpose()
|
||||
x = x.transpose()
|
||||
|
||||
# 3 dimensions
|
||||
x = x.reshape(2, 5, 10)
|
||||
|
||||
# 4 dimensions
|
||||
x = x.reshape(2, 5, 2, 5)
|
||||
|
||||
# big number
|
||||
x = np.empty(2 ** 24)
|
||||
x[0:1000000] = 1
|
||||
x[1000000:10000000] = 5
|
||||
|
||||
# pyTorch
|
||||
try:
|
||||
import torch
|
||||
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
x = torch.Tensor(x)
|
||||
pass
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# tensorflow
|
||||
|
||||
try:
|
||||
import tensorflow as tf
|
||||
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
x = tf.convert_to_tensor(x)
|
||||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# pandas
|
||||
|
||||
try:
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
|
||||
x = px.data.gapminder().query("year == 2007")
|
||||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# custom visualizer defined in ./debugvisualizer.py (this file is included automatically)
|
||||
|
||||
from Person import Person
|
||||
|
||||
x = Person("Aria")
|
||||
parent1 = Person("Eduart")
|
||||
parent2 = Person("Catelyn")
|
||||
x.addParent(parent1)
|
||||
x.addParent(parent2)
|
||||
parent1.addParent(Person("Benjen"))
|
||||
|
||||
# direct debug-visualizer json as dict with property "kind"
|
||||
|
||||
x = {
|
||||
"kind": {"dotGraph": True},
|
||||
"text": '\ndigraph G {\n subgraph cluster_0 {\n style=filled;\n color=lightgrey;\n node [style=filled,color=white];\n a0 -> a1 -> a2 -> a3;\n label = "process #1";\n }\n \n subgraph cluster_1 {\n node [style=filled];\n b0 -> b1 -> b2 -> b3;\n label = "process #2";\n color=blue\n }\n start -> a0;\n start -> b0;\n a1 -> b3;\n b2 -> a3;\n a3 -> a0;\n a3 -> end;\n b3 -> end;\n \n start [shape=Mdiamond];\n end [shape=Msquare];\n}\n',
|
||||
}
|
||||
|
||||
x = {
|
||||
"kind": {"plotly": True},
|
||||
"data": [
|
||||
{
|
||||
"mode": "lines",
|
||||
"type": "scatter",
|
||||
"x": ["A", "B", "C"],
|
||||
"xaxis": "x",
|
||||
"y": [4488916, 3918072, 3892124],
|
||||
"yaxis": "y",
|
||||
},
|
||||
{
|
||||
"cells": {"values": [["A", "B", "C"], [341319, 281489, 294786], [4488916, 3918072, 3892124]]},
|
||||
"domain": {"x": [0.0, 1.0], "y": [0.0, 0.60]},
|
||||
"header": {"align": "left", "font": {"size": 10}, "values": ["Date", "Number", "Output"]},
|
||||
"type": "table",
|
||||
},
|
||||
],
|
||||
"layout": {"xaxis": {"anchor": "y", "domain": [0.0, 1.0]}, "yaxis": {"anchor": "x", "domain": [0.75, 1.0]}},
|
||||
}
|
||||
pass
|
||||
@@ -23,10 +23,9 @@ for i in range(2,100):
|
||||
# connects the node to a random edge
|
||||
targetId = str(randint(1, i - 1))
|
||||
graph["edges"].append({"from": id, "to": targetId})
|
||||
json_graph = dumps(graph)
|
||||
print("i is " + str(i))
|
||||
# try setting a breakpoint right above
|
||||
# then put json_graph into the visualization console and press enter
|
||||
# then put graph into the visualization console and press enter
|
||||
# when you step through the code each time you hit the breakpoint
|
||||
# the graph should automatically refresh!
|
||||
|
||||
|
||||
38
demos/python/insertion_sort.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""Python demo for sorting using VS Code Debug Visualizer."""
|
||||
|
||||
|
||||
def serialize(arr):
|
||||
"""Serialize an array into a format the visualizer can understand."""
|
||||
formatted = {
|
||||
"kind": {"grid": True},
|
||||
"rows": [
|
||||
{
|
||||
"columns": [
|
||||
{"content": str(value), "tag": str(value)} for value in arr
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
return formatted
|
||||
|
||||
|
||||
arr = [6, 9, 3, 12, 1, 11, 5, 13, 8, 14, 2, 4, 10, 0, 7]
|
||||
|
||||
# Put serialized into the Debug Visualizer console
|
||||
serialized = serialize(arr)
|
||||
|
||||
# Set a breakpoint on the line below and go through the code in debug mode to
|
||||
# watch it update
|
||||
for target_idx in range(1, len(arr)):
|
||||
target_value = arr[target_idx]
|
||||
compare_idx = target_idx - 1
|
||||
|
||||
while compare_idx >= 0 and arr[compare_idx] > target_value:
|
||||
arr[compare_idx + 1] = arr[compare_idx]
|
||||
serialized = serialize(arr)
|
||||
compare_idx -= 1
|
||||
|
||||
arr[compare_idx + 1] = target_value
|
||||
serialized = serialize(arr)
|
||||
|
||||
assert arr == sorted(arr)
|
||||
14
demos/rust/.vscode/launch.json
vendored
@@ -7,8 +7,18 @@
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/target/debug/rust_demo",
|
||||
"name": "run rust demo",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=rust_demo",
|
||||
"--package=rust_demo",
|
||||
],
|
||||
"filter": {
|
||||
"name": "rust_demo",
|
||||
"kind": "bin",
|
||||
},
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
|
||||
8
demos/rust/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"debugVisualizer.debugAdapterConfigurations": {
|
||||
"lldb": {
|
||||
"expressionTemplate": "${expr}",
|
||||
"context": "watch"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +1,77 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Label {
|
||||
label: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Row {
|
||||
label: Option<String>,
|
||||
columns: Vec<Column>,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Column {
|
||||
content: Option<String>,
|
||||
tag: Option<String>,
|
||||
color: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Marker {
|
||||
id: String,
|
||||
row: u64,
|
||||
column: u64,
|
||||
rows: Option<u64>,
|
||||
columns: Option<u64>,
|
||||
label: Option<String>,
|
||||
color: Option<String>,
|
||||
}
|
||||
// expected to mirror
|
||||
// https://hediet.github.io/visualization/docs/visualization-data-schema.json
|
||||
//
|
||||
// implements the grid visualizer as an example
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!*b
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Kind {
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
array: bool,
|
||||
}
|
||||
pub type Label = String;
|
||||
|
||||
/// `GridVisualizationData` in schema
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Grid {
|
||||
kind: Kind,
|
||||
#[serde(rename = "columnLabels")]
|
||||
column_labels: Option<Vec<Label>>,
|
||||
rows: Vec<Row>,
|
||||
markers: Option<Vec<Marker>>,
|
||||
|
||||
#[serde(rename = "columnLabels")]
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
column_labels: Option<Vec<Label>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Kind {
|
||||
grid: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Row {
|
||||
columns: Vec<Column>,
|
||||
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
label: Option<Label>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Column {
|
||||
/// value to display to the user
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
content: Option<String>,
|
||||
|
||||
/// unique value to identify this cell, if desired
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
tag: Option<String>,
|
||||
|
||||
// TODO: valid values / syntax?
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
color: Option<String>,
|
||||
}
|
||||
|
||||
fn show_arr(a: &[i32]) -> String {
|
||||
let n = a.len();
|
||||
let labels: Vec<Label> = (0..n)
|
||||
.map(|i| Label {
|
||||
label: Some(i.to_string()),
|
||||
})
|
||||
.collect();
|
||||
let columns: Vec<Column> = a
|
||||
.iter()
|
||||
.map(|x| Column {
|
||||
content: Some(format!("{:?}", x)),
|
||||
tag: Some(x.to_string()),
|
||||
color: None,
|
||||
tag: None,
|
||||
|
||||
// TODO: `color` value doesn't seem to change visualizer's output at all
|
||||
color: "who-knows".to_owned().into(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let row = Row {
|
||||
label: None,
|
||||
columns,
|
||||
// TODO: visualizer doesn't seem to render this
|
||||
label: "my row".to_owned().into(),
|
||||
};
|
||||
let kind = Kind { array: true };
|
||||
|
||||
let kind = Kind { grid: true };
|
||||
|
||||
let grid = Grid {
|
||||
kind,
|
||||
column_labels: Some(labels),
|
||||
rows: vec![row],
|
||||
markers: None,
|
||||
|
||||
// TODO: visualizer doesn't seem to work if this is non-`None`
|
||||
column_labels: None,
|
||||
};
|
||||
|
||||
serde_json::to_string(&grid).unwrap()
|
||||
@@ -81,9 +81,9 @@ fn main() {
|
||||
let mut arr = vec![1, 2, 3];
|
||||
let mut _s = show_arr(&arr);
|
||||
for _ in 0..5 {
|
||||
arr.swap(0, 2); // break point
|
||||
arr.swap(0, 2); // set a break-point here, to easily observe the change
|
||||
_s = show_arr(&arr);
|
||||
}
|
||||
dbg!(arr); // break point
|
||||
println!("Hello, world!");
|
||||
dbg!(arr);
|
||||
println!("break-point here, too");
|
||||
}
|
||||
295
docs/architecture.drawio.svg
Normal file
@@ -0,0 +1,295 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="241px" height="971px" viewBox="-0.5 -0.5 241 971" content="<mxfile host="localhost" modified="2020-05-18T21:20:25.100Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.45.1 Chrome/78.0.3904.130 Electron/7.2.4 Safari/537.36" etag="wIym9edGWidWU1rSQKSs" version="13.0.9"><diagram id="6hGFLwfOUW9BJ-s0fimq" name="Page-1">7Vpbd6I6FP41Pp4uCKDlcWq9HqVntBXlDUgqseGyIAr660+QoCB06prRdpw1L0o2ySbZ++PbF21IbTfphWbgjH2ISAMIMGlIjw0AZKXFPlPBNhMogpwJliGGmUg8CqZ4h7hQ4NI1higqTaS+TygOykLb9zxk05LMDEM/Lk979Un5qYG5RBXB1DZJVapjSJ1Meg9aR3kf4aWTP1lsqtkd18wn85NEjgn9uCCSOg2pHfo+za7cpI1IarvcLtm67jt3DxsLkUfPWtDMVmxMsuaH4xuj2/y0ob/2IEoXCA3pIXYwRdPAtNO7MXMvkznUJWwksstXTEjbJ37Ixp7vsUkP1T3xbW5QSFFSEPE99pDvIhpu2RR+V+Lm4nhpNvk4PlofyFzmFCwvtbjQ5B5fHlQfjcIuuF3esRG4RRsp4EwbieASNhJu0UbnwkiWL2CiKoo6CUVehH3v10yVjhDEiI6w94bgo0lNdgdth6LtvuEnPHRNPSFP+H4zAmMyFjqb2VwjtjfeTHuzteWStbFVO8ZcC2FPjUfAIUaPENhWgQ20jdF7oUZvhqFuU+iSHdS7kTkPiI1VgvoTYnlaYAF5Y0vD1QjMZMh02oBsLKwI9nYQDTxNWMyHwmAVtAautrHag+a4HWOrx547ZdcrQx21h8Ryv+MnZo0FUKPByk+YLDJ1hbBrZSyJ6gW8r5a9L4pV9x9ehqL7mxfwvlTx/jDqpCOTMgB0vCVmGP9TYCDMwEJPxMV8wmTKnH2vzG76LCNYgJfNZG44Vn/mwB6DydtsbYAUCuq/dnld13JhYLkva9gfYwaHHex1t5DBh51pZYEYP62+RQM3Seew6478Oo2xoStvJahNC1Dbaerr99S0nKAuCqcDuXwGnOT34cQo8y+OfgFHY/FDHD0PpNe5cBUUSconokipoCj1NgtLoWmntDRB0ZrQG4XTG3O/M9JZROoPnYU0KcBqwiDT7TK4bRe6Jpi6un5xZ2xtIoy8yW6AY2xLE8f2ShEpLkSkuBYi+AgRrX+NiKV8JjiqlYFYQQLy4Le0nmIjP0Be2fPsJi/dWAYtPUTUDGk+G2LT9T347GAvv5XPlXNBF5ODKsjqL/5YP6SOv/Q9k3SO0geT4CVT9UjQK7PBQ2pizAq2b1xs+ZT6bqopwXSeIvdO4aNF/gx2/ZhwUO8HWz5415GRvw5tVErv2MaXKK8+MlG6+XOc/Y9YZoJsFCLCUoRN2QV1fuV6//Mx2+VBKWiVISSfQiM7A19VrBRPFInKO+EuV5SdvKJoD7PDYc9CXquCvHaITMoq/o/xdwI6aEbOnqnEE2xeCwZSFQbymTAovtS5l34X78vSB4ou53214n2e2KT+FzpJEKIoK5qEZ/8GECFXEaF8HSIOenKWEX6WD04VXQ8RoliBBHq3dGaRlZY9m4cGm3kJhTXBwcUQ7oMIQxbemdZeVerfIN38/jjKQ0N5THWtqR9xNH1WS6JVE+Av0rOp1qSRw44C/zSb1rXClGsZtVqZ7dvfdyylobda4UuGPgSmPpNGkrZdbFVqAVaptVUt+/5Bo6dQNWmPk7pGz66QVgtP82ukzU31E9NmsZq9xMjaYBT/aa9VXRe+eaXXClRDwBTtw9GX5wA391JquNh97dTWsk+lDm3no3ZHMd9RqvmO+IUp8GmeIp/C8WcTHuV6CU9eTBbQPsPRmrHDDoV3zPoQhW2fUYR3q82ZU/CuLCDGFlCI5U12I9EIbGnmMAA2jflgDfuafEZTJnmaC9HAna2NUm+vU+jtvSjX6tvd1+QYYl2ScYkYU9P+nexBkeZuwqCaFP9lxK9kRLGmBMxf8d+BEpuXosTWaTF5QUqs9qpzStz/gsZZ8UbpEJhzTVzoSYDc7r4fzRGsH+Wz7Q9608VfSxOtX0eD47g85xqJtirXkKB0LRKsJtpXIsG/hHZCaDl5lQjtK5tainrXUsrx+P5nOa0l3IGyLlW5FK2x4fG/Xdn04x/kpM7/</diagram></mxfile>">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="0" y="600" width="240" height="370" fill="none" stroke="#000000" pointer-events="all"/>
|
||||
<rect x="0" y="460" width="240" height="120" fill="none" stroke="#000000" pointer-events="all"/>
|
||||
<rect x="0" y="0" width="240" height="440" fill="none" stroke="#000000" pointer-events="all"/>
|
||||
<rect x="60" y="50" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 80px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
Extension
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="84" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Extension
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="170" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 200px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
JsEvaluationEngine
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="204" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
JsEvaluationEngine
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="290" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 320px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
JsEvaluator
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="324" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
JsEvaluator
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="490" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 520px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
DataExtractionResult
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="524" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
DataExtractionResult
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 120 125.99 L 120 167.76" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 120 110.99 L 124.41 118.49 L 120 125.99 L 115.59 118.49 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 113.5 155.88 L 120 168.88 L 126.5 155.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 107px; margin-left: 125px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="125" y="107" fill="#000000" font-family="Helvetica" font-size="11px">
|
||||
1
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 120 230 L 120 287.76" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 113.5 275.88 L 120 288.88 L 126.5 275.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 260px; margin-left: 120px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Creates
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="263" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Creates
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 120 350 L 120 487.76" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 113.5 475.88 L 120 488.88 L 126.5 475.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 420px; margin-left: 120px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Evaluates Expression To
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="423" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Evaluates Expression To
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="0" width="70" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 10px; margin-left: 35px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">
|
||||
extension
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="35" y="14" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
extension
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="460" width="50" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 470px; margin-left: 25px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">
|
||||
shared
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="25" y="474" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
shared
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="630" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 660px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
Model.state
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="664" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Model.state
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="600" width="60" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 610px; margin-left: 30px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">
|
||||
webview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="30" y="614" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
webview
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 120 550 L 120 627.76" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 113.5 615.88 L 120 628.88 L 126.5 615.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 590px; margin-left: 120px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Sent To
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="120" y="593" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Sent To
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="760" width="150" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 790px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
Visualizer.renderContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="135" y="794" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Visualizer.renderContent
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 120 690 L 130.17 757.79" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 121.98 747 L 130.33 758.89 L 134.83 745.07" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 725px; margin-left: 125px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Rendered In
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="125" y="728" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Rendered In
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="60" y="880" width="130" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 910px; margin-left: 61px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
|
||||
Visualization.render
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="125" y="914" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Visualization.render
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 132.5 820 L 127.69 877.77" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 122.19 865.39 L 127.59 878.89 L 135.15 866.47" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 850px; margin-left: 130px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Rendered In
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="130" y="853" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Rendered In
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Viewer does not support full SVG 1.1
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/ast-demo.gif
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
docs/custom-script-map.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/custom-scripts.gif
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 17 KiB |
@@ -3,15 +3,13 @@
|
||||
[data-extraction]
|
||||
[webview]
|
||||
[extension]
|
||||
[visualization]
|
||||
[visualization-playground]
|
||||
[hediet/visualization]
|
||||
|
||||
[webview] ..> [extension]: "Uses RPC contracts, connects using websockets"
|
||||
[extension] ..> [webview]: "Loads into a Webview in VS Code"
|
||||
|
||||
[webview] ..> [visualization]: "Uses React Components"
|
||||
[webview] ..> [hediet/visualization]: "Uses React Components"
|
||||
|
||||
[visualization-playground] ..> [visualization]: "Uses React Components"
|
||||
|
||||
[extension] ..> [data-extraction]: "Injects Runtime into JavaScript applications"
|
||||
|
||||
|
||||
BIN
docs/table-demo.gif
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
docs/visualization-plotly-random-walk.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
@@ -1,2 +1 @@
|
||||
node_modules
|
||||
ui/node_modules
|
||||
./node_modules
|
||||
@@ -1,5 +1,26 @@
|
||||
# Change Log
|
||||
|
||||
## 2.3.0
|
||||
|
||||
- [Improves support for python](https://github.com/hediet/vscode-debug-visualizer/pull/122)
|
||||
|
||||
## 2.2.0
|
||||
|
||||
- Adds setting `debugVisualizer.js.customScriptPaths` to inject custom scripts when debugging JavaScript. Such scripts are reloaded automatically when changed.
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Adds text diff and text decoration viewer.
|
||||
- Supports more js debug adapters
|
||||
|
||||
## 2.0.6
|
||||
|
||||
- Fixes grid extractor bug
|
||||
- Fixes Perspective.js bug
|
||||
- Fixes AST visualizer bug
|
||||
- Adds table data extractor
|
||||
- Improves typescript data extractor
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- Support for the new JS debug adapters
|
||||
|
||||
@@ -3,218 +3,77 @@
|
||||
[](https://twitter.com/intent/follow?screen_name=hediet_dev)
|
||||
|
||||
A VS Code extension for visualizing data structures while debugging.
|
||||
Like the VS Code's watch view, but with custom visualizations.
|
||||
Like the VS Code's watch view, but with rich visualizations of the watched value.
|
||||
|
||||

|
||||
|
||||
## [Visualization Playground](https://hediet.github.io/visualization/)
|
||||
|
||||
Click [here](https://hediet.github.io/visualization/) to explore all available visualizations.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
See [demos](../demos/) for demos. These languages and debuggers are verified to work with this extension:
|
||||
|
||||
- JavaScript/TypeScript/... using `node`/`node2`/`extensionHost` or `chrome` debug adapter: [⭐ Full Support](../demos/js)
|
||||
- Go using `go` (Delve) debug adapter: [✅ Rudimentary Support](../demos/golang)
|
||||
- Python using `python` debug adapter: [✅ Rudimentary Support](../demos/python)
|
||||
- C# using `coreclr` debug adapter: [✅ Rudimentary Support](../demos/csharp)
|
||||
- PHP using `php` debug adapter: [✅ Rudimentary Support](../demos/php)
|
||||
- Java using `java` debug adapter: [✅ Rudimentary Support](../demos/java)
|
||||
- C++ using `cppdbg` debug adapter: [✅ Rudimentary Support](../demos/cpp)
|
||||
- Swift using `lldb` debug adapter: [✅ Rudimentary Support](../demos/swift)
|
||||
- Rust using `lldb` debug adapter: [✅ Rudimentary Support](../demos/rust)
|
||||
- JavaScript/TypeScript/... using `node`/`node2`/`extensionHost`/`chrome`/`pwa-chrome`/`pwa-node` debug adapter: [⭐ Full Support](../demos/js)
|
||||
- Dart/Flutter using `dart` debug adapter: [✅ Basic Support](../demos/dart)
|
||||
- Go using `go` (Delve) debug adapter: [✅ Basic Support](../demos/golang)
|
||||
- Python using `python` debug adapter: [✅ Basic Support](../demos/python)
|
||||
- C# using `coreclr` debug adapter: [✅ Basic Support](../demos/csharp) (work in progress for Full Support)
|
||||
- PHP using `php` debug adapter: [✅ Basic Support](../demos/php)
|
||||
- Java using `java` debug adapter: [✅ Basic Support](../demos/java)
|
||||
- C++ using `cppdbg` debug adapter: [✅ Basic Support](../demos/cpp)
|
||||
- Swift using `lldb` debug adapter: [✅ Basic Support](../demos/swift)
|
||||
- Rust using `lldb` debug adapter: [✅ Basic Support](../demos/rust)
|
||||
|
||||
All other languages and debuggers might work too.
|
||||
For languages with _Rudimentary Support_, only JSON strings can be visualized - you must implement some logic that builds this JSON for your data structure!
|
||||
For languages with _Basic Support_, only JSON strings can be visualized - you must implement some logic that builds this JSON for your data structure!
|
||||
Fully supported languages offer _Data Extractors_ which convert some well known data structures to json.
|
||||
|
||||
## Usage
|
||||
|
||||
After installing this extension, use the command `Debug Visualizer: New View` to open a new visualizer view.
|
||||
In this view you can enter an expression that is evaluated and visualized while stepping through your application.
|
||||
This view works the same as the watch view of VS Code, except that the resulting value is presented visually rather than textually.
|
||||
This view works the same as the watch view of VS Code, except that the resulting value is presented visually rather than textually and you can only watch one expression (but you can still open multiple windows).
|
||||
|
||||
You can refresh the evaluation and open the current visualizer view into a new browser window by using the top right buttons.
|
||||
You can also unfold the details pane to select a _Data Extractor_ and a _Visualizer_.
|
||||
|
||||
Use the command `Debug Visualizer: Use Selection as Expression` (Shift + F1) to use the currently selected text as expression
|
||||
Use the command `Debug Visualizer: Use Selection as Expression` _(Shift + F1)_ to use the currently selected text as expression
|
||||
in the most recently opened debug visualizer.
|
||||
|
||||
## Supported Values
|
||||
|
||||
Visualizers consume specific JSON data. See [Integrated Visualizers](#Integrated%20Visualizers) for the schema of supported JSON data.
|
||||
Not all values can be processed.
|
||||
Visualizers consume specific JSON data. This extension uses [hediet/visualization](https://github.com/hediet/visualization), a generic visualization framework.
|
||||
You can see in its [playground](https://hediet.github.io/visualization/) which data can be visualized and how the visualization looks like.
|
||||
|
||||
The currently watched expression should evaluate to a JSON Object string,
|
||||
matching the schema of one of the supported visualizers. This JSON string may be surrounded by single or double quotation marks (or none at all) and must not be escaped.
|
||||
matching the [schema](https://hediet.github.io/visualization/docs/visualization-data-schema.json) of one of the supported visualizers. This JSON string may be surrounded by single or double quotation marks (or none at all) and must not be escaped.
|
||||
A valid example is `"{ "kind": { "text": true }, "text": "some text\nmore text" }"`.
|
||||
Use the watch window to see what an expression evaluates to. This extension simply interprets that result.
|
||||
|
||||
For some languages (TypeScript/JavaScript), runtime code is injected to support _Data Extractors_.
|
||||
A Data Extractor lifts the requirement for the visualized value to be a JSON string
|
||||
and acts as a bridge between custom data structures and the JSON data processed by the visualizers.
|
||||
When multiple Data Extractors are applicable, a preferred one can be selected in the visualization view.
|
||||
|
||||
## Integrated Visualizers
|
||||
There is a [JSON Schema for all supported visualizations](https://hediet.github.io/visualization/docs/visualization-data-schema.json) and a [typescript declaration file](https://hediet.github.io/visualization/docs/visualization-data.ts).
|
||||
|
||||
The following visualizers are built into this extension.
|
||||
## Selected Demos
|
||||
|
||||
### Graph Visualization
|
||||
### Reversing a Doubly Linked List
|
||||
|
||||
[Demo](https://hediet.github.io/vscode-debug-visualizer/?value=%7B%0A++++%22kind%22%3A+%7B+%22graph%22%3A+true+%7D%2C%0A++++%22nodes%22%3A+%5B%0A++++++++%7B+%22id%22%3A+%221%22%2C+%22label%22%3A+%221%22+%7D%2C%0A++++++++%7B+%22id%22%3A+%222%22%2C+%22label%22%3A+%222%22%2C+%22color%22%3A+%22orange%22+%7D%2C%0A++++++++%7B+%22id%22%3A+%223%22%2C+%22label%22%3A+%223%22+%7D%0A++++%5D%2C%0A++++%22edges%22%3A+%5B%0A++++++++%7B+%22from%22%3A+%221%22%2C+%22to%22%3A+%222%22%2C+%22color%22%3A+%22red%22+%7D%2C%0A++++++++%7B+%22from%22%3A+%221%22%2C+%22to%22%3A+%223%22+%7D%0A++++%5D%0A%7D).
|
||||
The Graphviz and vis.js visualizers render data that matches the `Graph` interface.
|
||||

|
||||
|
||||
```ts
|
||||
interface Graph {
|
||||
kind: { graph: true };
|
||||
nodes: NodeGraphData[];
|
||||
edges: EdgeGraphData[];
|
||||
}
|
||||
### Random Walk
|
||||
|
||||
interface NodeGraphData {
|
||||
id: string;
|
||||
label?: string;
|
||||
color?: string;
|
||||
shape?: "ellipse" | "box";
|
||||
}
|
||||

|
||||
|
||||
interface EdgeGraphData {
|
||||
from: string;
|
||||
to: string;
|
||||
label?: string;
|
||||
id?: string;
|
||||
color?: string;
|
||||
dashes?: boolean;
|
||||
}
|
||||
```
|
||||
### Ast Visualizer
|
||||
|
||||
The graphviz visualizer uses the SVG viewer to render the SVG created by `viz.js`.
|
||||

|
||||
|
||||

|
||||

|
||||
### Table Visualizer
|
||||
|
||||
### Plotly Visualization
|
||||
|
||||
[Demo](https://hediet.github.io/vscode-debug-visualizer/?value=%7B%22kind%22%3A%7B%22plotly%22%3Atrue%7D%2C%22data%22%3A%5B%7B%22y%22%3A%5B0%2C1%2C2%2C3%2C4%2C3%2C4%2C3%2C2%2C1%2C2%5D%7D%5D%7D).
|
||||
The plotly visualizer uses plotly and can visualize JSON data matching the following interface:
|
||||
|
||||
```ts
|
||||
export interface Plotly {
|
||||
kind: { plotly: true };
|
||||
data: Partial<Plotly.Data>[];
|
||||
}
|
||||
// See plotly docs for Plotly.Data.
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Tree Visualization
|
||||
|
||||
The tree visualizer renders data that matches the `Tree` interface.
|
||||
|
||||
```ts
|
||||
interface Tree<TData = unknown> {
|
||||
kind: { tree: true };
|
||||
root: TreeNode<TData>;
|
||||
}
|
||||
interface TreeNode<TExtraData> {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
value: string | undefined;
|
||||
emphasizedValue: string | undefined;
|
||||
children: TreeNode<TExtraData>[];
|
||||
data: TExtraData;
|
||||
isMarked: boolean;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### AST Visualization
|
||||
|
||||
[Demo](https://hediet.github.io/vscode-debug-visualizer/?value=%7B%22kind%22%3A%7B%22text%22%3Atrue%2C%22tree%22%3Atrue%2C%22ast%22%3Atrue%7D%2C%22root%22%3A%7B%22name%22%3A%22SourceFile%22%2C%22id%22%3A%22root%22%2C%22children%22%3A%5B%7B%22name%22%3A%22ClassDeclaration%22%2C%22id%22%3A%22statements%22%2C%22children%22%3A%5B%7B%22name%22%3A%22Identifier%22%2C%22id%22%3A%22name%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A6%2C%22position%22%3A6%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%22Test1%22%7D%2C%7B%22name%22%3A%22MethodDeclaration%22%2C%22id%22%3A%22members%22%2C%22children%22%3A%5B%7B%22name%22%3A%22PublicKeyword%22%2C%22id%22%3A%22modifiers%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A8%2C%22position%22%3A14%7D%2C%22isMarked%22%3Afalse%7D%2C%7B%22name%22%3A%22Identifier%22%2C%22id%22%3A%22name%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A4%2C%22position%22%3A22%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%22foo%22%7D%2C%7B%22name%22%3A%22Parameter%22%2C%22id%22%3A%22parameters%22%2C%22children%22%3A%5B%7B%22name%22%3A%22Identifier%22%2C%22id%22%3A%22name%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A1%2C%22position%22%3A27%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%22a%22%7D%2C%7B%22name%22%3A%22NumberKeyword%22%2C%22id%22%3A%22type%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A7%2C%22position%22%3A29%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A9%2C%22position%22%3A27%7D%2C%22isMarked%22%3Afalse%7D%2C%7B%22name%22%3A%22Block%22%2C%22id%22%3A%22body%22%2C%22children%22%3A%5B%7B%22name%22%3A%22VariableStatement%22%2C%22id%22%3A%22statements%22%2C%22children%22%3A%5B%7B%22name%22%3A%22VariableDeclarationList%22%2C%22id%22%3A%22declarationList%22%2C%22children%22%3A%5B%7B%22name%22%3A%22VariableDeclaration%22%2C%22id%22%3A%22declarations%22%2C%22children%22%3A%5B%7B%22name%22%3A%22Identifier%22%2C%22id%22%3A%22name%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A2%2C%22position%22%3A47%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%22x%22%7D%2C%7B%22name%22%3A%22ObjectLiteralExpression%22%2C%22id%22%3A%22initializer%22%2C%22children%22%3A%5B%7B%22name%22%3A%22PropertyAssignment%22%2C%22id%22%3A%22properties%22%2C%22children%22%3A%5B%7B%22name%22%3A%22Identifier%22%2C%22id%22%3A%22name%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A2%2C%22position%22%3A53%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%22a%22%7D%2C%7B%22name%22%3A%22FirstLiteralToken%22%2C%22id%22%3A%22initializer%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A2%2C%22position%22%3A56%7D%2C%22isMarked%22%3Afalse%2C%22value%22%3A%225%22%7D%5D%2C%22data%22%3A%7B%22length%22%3A5%2C%22position%22%3A53%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A9%2C%22position%22%3A51%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A13%2C%22position%22%3A47%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A21%2C%22position%22%3A39%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A22%2C%22position%22%3A39%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A27%2C%22position%22%3A37%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A50%2C%22position%22%3A14%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A66%2C%22position%22%3A0%7D%2C%22isMarked%22%3Afalse%7D%2C%7B%22name%22%3A%22EndOfFileToken%22%2C%22id%22%3A%22endOfFileToken%22%2C%22children%22%3A%5B%5D%2C%22data%22%3A%7B%22length%22%3A1%2C%22position%22%3A66%7D%2C%22isMarked%22%3Afalse%7D%5D%2C%22data%22%3A%7B%22length%22%3A67%2C%22position%22%3A0%7D%2C%22isMarked%22%3Atrue%7D%2C%22text%22%3A%22%5Cnclass+Test1+%7B%5Cn%5Ctpublic+foo%28a%3A+number%29+%7B%5Cn%5Ct%5Ctconst+x+%3D+%7B+a%3A+5+%7D%3B%5Cn%5Ct%7D%5Cn%7D%5Cn%22%2C%22fileName%22%3A%22index.ts%22%7D).
|
||||
The AST (Abstract Syntax Tree) visualizer renders data that matches the `Ast` interface.
|
||||
|
||||
```ts
|
||||
interface Ast
|
||||
extends Tree<{
|
||||
position: number;
|
||||
length: number;
|
||||
}>,
|
||||
Text {
|
||||
kind: { text: true; tree: true; ast: true };
|
||||
}
|
||||
```
|
||||
|
||||
Additionally to the tree view, the source code is rendered and when selecting an AST node,
|
||||
its span in the source code is highlighted.
|
||||
|
||||

|
||||
|
||||
### Grid Visualization
|
||||
|
||||
Visualizes data matching the following interface:
|
||||
|
||||
```ts
|
||||
export interface Grid {
|
||||
kind: { array: true };
|
||||
columnLabels?: { label?: string }[];
|
||||
rows: {
|
||||
label?: string;
|
||||
columns: {
|
||||
content?: string;
|
||||
tag?: string;
|
||||
color?: string;
|
||||
}[];
|
||||
}[];
|
||||
markers?: {
|
||||
id: string;
|
||||
|
||||
row: number;
|
||||
column: number;
|
||||
rows?: number;
|
||||
columns?: number;
|
||||
|
||||
label?: string;
|
||||
color?: string;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Text Visualization
|
||||
|
||||
The text visualizer renders data that matches the `Text` interface.
|
||||
|
||||
```ts
|
||||
interface Text {
|
||||
kind: { text: true };
|
||||
text: string;
|
||||
mimeType?: string;
|
||||
fileName?: string;
|
||||
}
|
||||
```
|
||||
|
||||
The `mimeType` and the file extension of `fileName` are used for syntax highlighting.
|
||||
|
||||
### SVG Visualization
|
||||
|
||||
The SVG visualizer renders data that matches the `Svg` interface.
|
||||
The actual SVG data must be stored in `text`.
|
||||
|
||||
```ts
|
||||
interface Svg extends Text {
|
||||
kind: { text: true; svg: true };
|
||||
}
|
||||
```
|
||||
|
||||
### Dot Graph Visualization
|
||||
|
||||
The Graphviz Dot visualizer renders data that matches the `DotGraph` interface.
|
||||
|
||||
```ts
|
||||
interface DotGraph extends Text {
|
||||
kind: { text: true; dotGraph: true };
|
||||
}
|
||||
```
|
||||
|
||||
`Viz.js` (Graphviz) is used for rendering.
|
||||

|
||||
|
||||
## JavaScript/TypeScript Integrated Data Extractors
|
||||
|
||||
@@ -224,35 +83,22 @@ Custom data extractors can be registered too.
|
||||
See the package `@hediet/debug-visualizer-data-extraction` and its [README](../data-extraction/README.md) for the implementation and its API.
|
||||
Also, a global object of name `hedietDbgVis` with helper functions is injected.
|
||||
|
||||
### ToString
|
||||
|
||||
Just calls `.toString()` on values and treats the result as text.
|
||||
|
||||
### TypeScript AST
|
||||
|
||||
- Direct Visualization of `ts.Node`s
|
||||
- Visualization of `Record<string, ts.Node>` and `[ts.Node]`. If the record contains a key `fn`, its value is displayed for each node.
|
||||
|
||||
### As Is Data Extractor
|
||||
|
||||
Treats the data as direct input to the visualizer.
|
||||
|
||||
### Use Method 'getDebugVisualization'
|
||||
|
||||
Calls `.getDebugVisualization()` on values and treats the result as direct input to the visualizer.
|
||||
|
||||
### Plotly y-Values
|
||||
|
||||
Uses plotly to plot an array of numbers.
|
||||
|
||||
### Object Graph
|
||||
|
||||
Constructs a graph containing all objects reachable from object the expression evaluates to.
|
||||
Graph is constructed using a breadth search. Stops after 50 nodes.
|
||||
|
||||
### Array Grid
|
||||
|
||||
Creates Grid visualization data for an array.
|
||||
- **ToString**
|
||||
- Just calls `.toString()` on values and treats the result as text.
|
||||
- **TypeScript AST**
|
||||
- Direct Visualization of `ts.Node`s
|
||||
- Visualization of `Record<string, ts.Node>` and `ts.Node[]`. If the record contains a key `fn`, its value is displayed for each node.
|
||||
- **As Is Data Extractor**
|
||||
- Treats the data as direct input to the visualizer.
|
||||
- **Use Method 'getVisualizationData'**
|
||||
- Calls `.getVisualizationData()` on values and treats the result as direct input to the visualizer.
|
||||
- **Plotly y-Values**
|
||||
- Uses plotly to plot an array of numbers.
|
||||
- **Object Graph**
|
||||
- Constructs a graph containing all objects reachable from object the expression evaluates to.
|
||||
Graph is constructed using a breadth search. Stops after 50 nodes.
|
||||
- **Array Grid**
|
||||
- Creates Grid visualization data for an array.
|
||||
|
||||
## UI Features
|
||||
|
||||
@@ -266,25 +112,71 @@ Creates Grid visualization data for an array.
|
||||
|
||||
This extension provides these configuration options:
|
||||
|
||||
- `debugVisualizer.debugAdapterConfigurations`
|
||||
### **debugVisualizer.debugAdapterConfigurations**
|
||||
|
||||
Allows to set expression templates for specific debug adapter types.
|
||||
Example:
|
||||
Allows to set expression templates for specific debug adapter types.
|
||||
Example:
|
||||
|
||||
```json
|
||||
"debugVisualizer.debugAdapterConfigurations": {
|
||||
"lldb": {
|
||||
"expressionTemplate": "script to_json(\"${expr}\")",
|
||||
"context": "repl"
|
||||
}
|
||||
```json
|
||||
"debugVisualizer.debugAdapterConfigurations": {
|
||||
"lldb": {
|
||||
"expressionTemplate": "script to_json(\"${expr}\")",
|
||||
"context": "repl"
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
Configurations here overwrite the built-in support for the corresponding debug adapter type.
|
||||
Configurations here overwrite the built-in support for the corresponding debug adapter type.
|
||||
|
||||
* `debugVisualizer.useChromeKioskMode`
|
||||
### **debugVisualizer.useChromeKioskMode**
|
||||
|
||||
Specifies whether to pop out Debug Visualization Views with Chrome in Kiosk Mode. Uses the default browser otherwise or if Chrome is not found. Defaults to `true`.
|
||||
Specifies whether to pop out Debug Visualization Views with Chrome in Kiosk Mode. Uses the default browser otherwise or if Chrome is not found. Defaults to `true`.
|
||||
|
||||
### **debugVisualizer.js.customScriptPaths**
|
||||
|
||||
Specifies a list of JavaScript files that are injected into the debugee when debugging JavaScript.
|
||||
Each script must assign `module.exports` with a function of type `import("@hediet/debug-visualizer-data-extraction").LoadDataExtractorsFn`.
|
||||
Paths must be absolute and can use the variable `${workspaceFolder}`.
|
||||
Scripts are automatically reloaded when they are changed.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {import("@hediet/debug-visualizer-data-extraction").LoadDataExtractorsFn}
|
||||
*/
|
||||
module.exports = (register, helpers) => {
|
||||
register({
|
||||
id: "map",
|
||||
getExtractions(data, collector, context) {
|
||||
if (!(data instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collector.addExtraction({
|
||||
priority: 1000,
|
||||
id: "map",
|
||||
name: "Map",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { table: true },
|
||||
rows: [...data].map(([k, v]) => ({
|
||||
key: k,
|
||||
value: v,
|
||||
})),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||

|
||||
|
||||
Use the `helpers` object to access some magic functions.
|
||||
`helpers.find(val => ...)` can be used to find an object that is reachable by any variable in scope.
|
||||
|
||||
# See Also
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"displayName": "Debug Visualizer",
|
||||
"description": "A visual watch window that lets you visualize your data structures while debugging.",
|
||||
"icon": "docs/logo.drawio.png",
|
||||
"version": "2.0.0",
|
||||
"version": "2.3.0",
|
||||
"license": "GPL-3.0",
|
||||
"engines": {
|
||||
"vscode": "^1.46.0"
|
||||
@@ -41,6 +41,11 @@
|
||||
"onDebug",
|
||||
"onWebviewPanel:debugVisualizer"
|
||||
],
|
||||
"capabilities": {
|
||||
"untrustedWorkspaces": {
|
||||
"supported": false
|
||||
}
|
||||
},
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
@@ -63,6 +68,14 @@
|
||||
"configuration": {
|
||||
"title": "Debug Visualizer",
|
||||
"properties": {
|
||||
"debugVisualizer.js.customScriptPaths": {
|
||||
"type": "array",
|
||||
"description": "Defines JavaScript sources that are injected into the debuggee.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The file path to a custom js script. Must be absolute. You can use ${workspaceFolder}."
|
||||
}
|
||||
},
|
||||
"debugVisualizer.useChromeKioskMode": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
@@ -123,6 +136,7 @@
|
||||
"webpack": "^4.41.6",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"ts-loader": "^6.2.1",
|
||||
"shx": "^0.3.2"
|
||||
"shx": "^0.3.2",
|
||||
"vsce": "^1.93.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,29 @@ export class Config {
|
||||
{ serializer: serializerWithDefault<DebugAdapterConfigs>({}) }
|
||||
);
|
||||
|
||||
private readonly _customScriptPaths = new VsCodeSetting(
|
||||
"debugVisualizer.js.customScriptPaths",
|
||||
{ serializer: serializerWithDefault<string[]>([]) }
|
||||
);
|
||||
|
||||
public get customScriptPaths(): string[] {
|
||||
return this._customScriptPaths.get().map(p => {
|
||||
const tpl = new SimpleTemplate(p);
|
||||
return tpl.render({
|
||||
workspaceFolder: () => {
|
||||
const workspaceFolder = (workspace.workspaceFolders ||
|
||||
[])[0];
|
||||
if (!workspaceFolder) {
|
||||
throw new Error(
|
||||
`Cannot get workspace folder - '${p}' cannot be evaluated!`
|
||||
);
|
||||
}
|
||||
return workspaceFolder.uri.fsPath;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@observable
|
||||
private _vsCodeTheme = window.activeColorTheme;
|
||||
|
||||
@@ -85,3 +108,13 @@ function evaluateTemplate(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class SimpleTemplate {
|
||||
constructor(private readonly str: string) {}
|
||||
|
||||
render(data: Record<string, () => string>): string {
|
||||
return this.str.replace(/\$\{([a-zA-Z0-9]+)\}/g, (substr, grp1) => {
|
||||
return data[grp1]();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
|
||||
export class ComposedEvaluationEngine implements EvaluationEngine {
|
||||
constructor(public readonly engines: EvaluationEngine[]) {}
|
||||
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
for (const f of this.engines) {
|
||||
const evaluator = f.createEvaluator(session);
|
||||
if (evaluator) {
|
||||
return evaluator;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||
import { Config, DebugAdapterConfig } from "../../Config";
|
||||
import { GenericEvaluator } from "./GenericEvaluationEngine";
|
||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
||||
constructor(private readonly config: Config) {}
|
||||
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
const config = this.config.getDebugAdapterConfig(session.session.type);
|
||||
if (!config) {
|
||||
return undefined;
|
||||
}
|
||||
return new ConfiguredEvaluator(session, config);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfiguredEvaluator extends GenericEvaluator {
|
||||
constructor(
|
||||
session: EnhancedDebugSession,
|
||||
private readonly config: DebugAdapterConfig
|
||||
) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
protected getContext() {
|
||||
return this.config.context;
|
||||
}
|
||||
|
||||
protected getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}) {
|
||||
return this.config.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
|
||||
export interface EvaluationEngine {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined;
|
||||
}
|
||||
|
||||
export interface Evaluator {
|
||||
evaluate(
|
||||
args: EvaluationArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
>;
|
||||
|
||||
/**
|
||||
* The language that expressions must be written in.
|
||||
*/
|
||||
readonly languageId: string | undefined;
|
||||
}
|
||||
|
||||
export interface EvaluationArgs {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
frameId: number | undefined;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import {
|
||||
EvaluationEngine,
|
||||
Evaluator,
|
||||
EvaluationArgs,
|
||||
} from "./EvaluationEngine";
|
||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class GenericEvaluationEngine implements EvaluationEngine {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
return new GenericEvaluator(session);
|
||||
}
|
||||
}
|
||||
|
||||
export class GenericEvaluator implements Evaluator {
|
||||
public readonly languageId = "text";
|
||||
|
||||
constructor(private readonly session: EnhancedDebugSession) {}
|
||||
|
||||
public async evaluate({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
frameId,
|
||||
}: EvaluationArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const finalExpression = this.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
let reply;
|
||||
try {
|
||||
reply = await this.session.evaluate({
|
||||
expression: finalExpression,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
} catch (error) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: [
|
||||
"An error occurred while evaluating the expression:",
|
||||
error.message,
|
||||
`Used debug adapter: ${this.session.session.configuration.type}`,
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Evaluated expression is",
|
||||
{ kind: "code", content: finalExpression },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return parseEvaluationResultFromGenericDebugAdapter(reply.result, {
|
||||
debugAdapterType: this.session.session.configuration.type,
|
||||
});
|
||||
}
|
||||
|
||||
protected getFinalExpression(args: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}): string {
|
||||
return args.expression;
|
||||
}
|
||||
|
||||
protected getContext(): "watch" | "repl" {
|
||||
// we will use "repl" as default so that results are not truncated.
|
||||
return "repl";
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import {
|
||||
getExpressionForDataExtractorApi,
|
||||
DataResult,
|
||||
ApiHasNotBeenInitializedCode,
|
||||
getExpressionToInitializeDataExtractorApi,
|
||||
DataExtractionResult,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import {
|
||||
EvaluationEngine,
|
||||
Evaluator,
|
||||
EvaluationArgs,
|
||||
} from "./EvaluationEngine";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class JsEvaluationEngine implements EvaluationEngine {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
const supportedDebugAdapters = [
|
||||
"node",
|
||||
"node2",
|
||||
"extensionHost",
|
||||
"chrome",
|
||||
"pwa-chrome",
|
||||
"pwa-node",
|
||||
];
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new JsEvaluator(session);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class JsEvaluator implements Evaluator {
|
||||
public readonly languageId = "javascript";
|
||||
|
||||
constructor(private readonly session: EnhancedDebugSession) {}
|
||||
|
||||
private getContext(): "copy" | "repl" {
|
||||
if (this.session.session.type.startsWith("pwa-")) {
|
||||
return "copy";
|
||||
}
|
||||
return "repl";
|
||||
}
|
||||
|
||||
public async evaluate({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
frameId,
|
||||
}: EvaluationArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
while (true) {
|
||||
try {
|
||||
const preferredExtractorExpr = preferredExtractorId
|
||||
? `"${preferredExtractorId}"`
|
||||
: "undefined";
|
||||
|
||||
const body = `${getExpressionForDataExtractorApi()}.getData(
|
||||
e => (${expression}),
|
||||
expr => eval(expr),
|
||||
${preferredExtractorExpr}
|
||||
)`;
|
||||
|
||||
const wrappedExpr = `
|
||||
(() => {
|
||||
try {
|
||||
return ${body};
|
||||
} catch (e) {
|
||||
return JSON.stringify({
|
||||
kind: "Error",
|
||||
message: e.message,
|
||||
stack: e.stack
|
||||
});
|
||||
}
|
||||
})()
|
||||
`;
|
||||
|
||||
const reply = await this.session.evaluate({
|
||||
expression: wrappedExpr,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
const resultStr = reply.result;
|
||||
const jsonData =
|
||||
this.getContext() === "copy"
|
||||
? resultStr
|
||||
: resultStr.substr(1, resultStr.length - 2);
|
||||
const result = JSON.parse(jsonData) as DataResult;
|
||||
|
||||
if (result.kind === "NoExtractors") {
|
||||
throw new Error("No extractors");
|
||||
} else if (result.kind === "Error") {
|
||||
throw new Error(result.message);
|
||||
} else if (result.kind === "Data") {
|
||||
return {
|
||||
kind: "data",
|
||||
result: result.extractionResult,
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid Data");
|
||||
}
|
||||
} catch (error) {
|
||||
const msg = error.message as string | undefined;
|
||||
if (msg && msg.includes(ApiHasNotBeenInitializedCode)) {
|
||||
if (await this.initializeApi(frameId)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "error",
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeApi(frameId: number | undefined): Promise<boolean> {
|
||||
try {
|
||||
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten.
|
||||
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`;
|
||||
|
||||
await this.session.evaluate({
|
||||
expression,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export * from "./EvaluationEngine";
|
||||
|
||||
export { ComposedEvaluationEngine } from "./ComposedEvaluationEngine";
|
||||
export { JsEvaluationEngine } from "./JsEvaluationEngine";
|
||||
export { GenericEvaluationEngine } from "./GenericEvaluationEngine";
|
||||
export { ConfiguredEvaluationEngine } from "./ConfiguredEvaluationEngine";
|
||||
@@ -1,146 +0,0 @@
|
||||
import {
|
||||
EvaluationWatchService,
|
||||
EvaluationWatcher,
|
||||
EvaluationWatcherOptions,
|
||||
} from "./EvaluationWatchService";
|
||||
import { observable, autorun, action } from "mobx";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
import { hotClass } from "@hediet/node-reload";
|
||||
import { VsCodeDebuggerView } from "../debugger/VsCodeDebuggerView";
|
||||
import { EvaluationEngine } from "./EvaluationEngine/EvaluationEngine";
|
||||
|
||||
@hotClass(module)
|
||||
export class EvaluationWatchServiceImpl implements EvaluationWatchService {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly watchers = new Set<ObservableEvaluationWatcher>();
|
||||
|
||||
constructor(
|
||||
private readonly vsCodeDebuggerView: VsCodeDebuggerView,
|
||||
private readonly evaluationEngine: EvaluationEngine
|
||||
) {
|
||||
this.dispose.track({
|
||||
dispose: autorun(() => {
|
||||
if (
|
||||
vsCodeDebuggerView.activeDebugSession &&
|
||||
vsCodeDebuggerView.activeFrameId !== undefined
|
||||
) {
|
||||
for (const w of this.watchers) {
|
||||
w.refresh();
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public createEvaluationWatcher(
|
||||
expression: string,
|
||||
options: EvaluationWatcherOptions
|
||||
): EvaluationWatcher {
|
||||
const w = new ObservableEvaluationWatcher(expression, this, options);
|
||||
this.watchers.add(w);
|
||||
this.refresh(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
public removeWatcher(w: ObservableEvaluationWatcher) {
|
||||
this.watchers.delete(w);
|
||||
}
|
||||
|
||||
private lastLanguageId: string | undefined = undefined;
|
||||
|
||||
get languageId(): string | undefined {
|
||||
const session = this.vsCodeDebuggerView.activeDebugSession;
|
||||
if (!session) {
|
||||
return this.lastLanguageId;
|
||||
}
|
||||
const evaluator = this.evaluationEngine.createEvaluator(session);
|
||||
if (!evaluator) {
|
||||
return this.lastLanguageId;
|
||||
}
|
||||
this.lastLanguageId = evaluator.languageId;
|
||||
return evaluator.languageId;
|
||||
}
|
||||
|
||||
public async refresh(w: ObservableEvaluationWatcher): Promise<void> {
|
||||
const session = this.vsCodeDebuggerView.activeDebugSession;
|
||||
if (!session) {
|
||||
w._state = { kind: "noDebugSession" };
|
||||
return;
|
||||
}
|
||||
|
||||
const frameId = this.vsCodeDebuggerView.activeFrameId;
|
||||
|
||||
w._state = { kind: "loading" };
|
||||
|
||||
const evaluator = this.evaluationEngine.createEvaluator(session);
|
||||
if (!evaluator) {
|
||||
w._state = {
|
||||
kind: "error",
|
||||
message: `The debug adapter "${session.session.type}" is not supported.`,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await evaluator.evaluate({
|
||||
expression: w.expression,
|
||||
frameId,
|
||||
preferredExtractorId: w.preferredDataExtractor,
|
||||
});
|
||||
|
||||
w._state = result;
|
||||
}
|
||||
|
||||
public async getCompletions(
|
||||
text: string,
|
||||
column: number
|
||||
): Promise<CompletionItem[]> {
|
||||
const session = this.vsCodeDebuggerView.activeDebugSession;
|
||||
if (!session) {
|
||||
return [];
|
||||
}
|
||||
return await session.getCompletions({
|
||||
text,
|
||||
frameId: this.vsCodeDebuggerView.activeFrameId,
|
||||
column,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ObservableEvaluationWatcher implements EvaluationWatcher {
|
||||
constructor(
|
||||
public readonly expression: string,
|
||||
private readonly source: EvaluationWatchServiceImpl,
|
||||
options: EvaluationWatcherOptions
|
||||
) {
|
||||
this._preferredDataExtractor = options.preferredDataExtractor;
|
||||
}
|
||||
|
||||
@observable
|
||||
private _preferredDataExtractor: DataExtractorId | undefined = undefined;
|
||||
|
||||
public get preferredDataExtractor(): DataExtractorId | undefined {
|
||||
return this._preferredDataExtractor;
|
||||
}
|
||||
|
||||
@action
|
||||
public setPreferredDataExtractor(id: DataExtractorId | undefined): void {
|
||||
this._preferredDataExtractor = id;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
this.source.refresh(this);
|
||||
}
|
||||
|
||||
@observable
|
||||
public _state: DataExtractionState = { kind: "loading" };
|
||||
public get state(): DataExtractionState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.source.removeWatcher(this);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from "./EvaluationWatchServiceImpl";
|
||||
export * from "./EvaluationWatchService";
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
} from "./VisualizationBackend";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
|
||||
export class ComposedVisualizationSupport
|
||||
implements DebugSessionVisualizationSupport {
|
||||
constructor(public readonly engines: DebugSessionVisualizationSupport[]) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
for (const f of this.engines) {
|
||||
const evaluator = f.createBackend(session);
|
||||
if (evaluator) {
|
||||
return evaluator;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
} from "./VisualizationBackend";
|
||||
import { Config, DebugAdapterConfig } from "../Config";
|
||||
import { GenericVisualizationBackend } from "./GenericVisualizationSupport";
|
||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class ConfigurableVisualizationSupport
|
||||
implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
private readonly debuggerView: DebuggerViewProxy
|
||||
) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
const config = this.config.getDebugAdapterConfig(session.session.type);
|
||||
if (!config) {
|
||||
return undefined;
|
||||
}
|
||||
return new ConfiguredVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfiguredVisualizationBackend extends GenericVisualizationBackend {
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: DebugAdapterConfig
|
||||
) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
protected getContext() {
|
||||
return this.config.context;
|
||||
}
|
||||
|
||||
protected getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}) {
|
||||
return this.config.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { DataExtractionResult } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { CompletionItem, FormattedMessage } from "../webviewContract";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackend,
|
||||
} from "./VisualizationBackend";
|
||||
import { EventEmitter, EventSource } from "@hediet/std/events";
|
||||
import { debug } from "vscode";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { autorun, reaction } from "mobx";
|
||||
|
||||
export class DispatchingVisualizationBackend implements VisualizationBackend {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly visualizationBackends = new Map<
|
||||
DebugSessionProxy,
|
||||
VisualizationBackend
|
||||
>();
|
||||
|
||||
protected readonly onChangeEmitter = new EventEmitter();
|
||||
public readonly onChange = this.onChangeEmitter;
|
||||
|
||||
constructor(
|
||||
private readonly visualizationSupport: DebugSessionVisualizationSupport,
|
||||
private readonly debuggerView: DebuggerViewProxy
|
||||
) {
|
||||
this.dispose.track(
|
||||
debug.onDidTerminateDebugSession(session => {
|
||||
const existing = [...this.visualizationBackends].find(
|
||||
v => v[0].session === session
|
||||
);
|
||||
if (existing) {
|
||||
this.visualizationBackends.delete(existing[0]);
|
||||
existing[1].dispose();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.dispose.track({
|
||||
dispose: () => {
|
||||
for (const backend of this.visualizationBackends.values()) {
|
||||
backend.dispose();
|
||||
}
|
||||
this.visualizationBackends.clear();
|
||||
},
|
||||
});
|
||||
|
||||
this.dispose.track({
|
||||
dispose: reaction(
|
||||
() => this.debuggerView.activeDebugSession,
|
||||
debugSession => {
|
||||
if (debugSession !== undefined) {
|
||||
this.onChangeEmitter.emit();
|
||||
}
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private getVisualizationBackend(
|
||||
debugSession: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
const existing = this.visualizationBackends.get(debugSession);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const newBackend = this.visualizationSupport.createBackend(
|
||||
debugSession
|
||||
);
|
||||
|
||||
if (!newBackend) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
newBackend.onChange.sub(() => {
|
||||
this.onChangeEmitter.emit();
|
||||
});
|
||||
|
||||
this.visualizationBackends.set(debugSession, newBackend);
|
||||
|
||||
return newBackend;
|
||||
}
|
||||
|
||||
private get activeVisualizationBackend(): VisualizationBackend | undefined {
|
||||
const activeDebugSession = this.debuggerView.activeDebugSession;
|
||||
if (!activeDebugSession) {
|
||||
return undefined;
|
||||
}
|
||||
const backend = this.getVisualizationBackend(activeDebugSession);
|
||||
return backend;
|
||||
}
|
||||
|
||||
public async getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const activeDebugSession = this.debuggerView.activeDebugSession;
|
||||
if (!activeDebugSession) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: "No active debug session.",
|
||||
};
|
||||
}
|
||||
const backend = this.getVisualizationBackend(activeDebugSession);
|
||||
if (!backend) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: `The debug adapter "${activeDebugSession.session.type}" is not supported.`,
|
||||
};
|
||||
}
|
||||
return await backend.getVisualizationData(args);
|
||||
}
|
||||
|
||||
public get expressionLanguageId(): string | undefined {
|
||||
return this.activeVisualizationBackend?.expressionLanguageId;
|
||||
}
|
||||
|
||||
public async getCompletions(
|
||||
text: string,
|
||||
column: number
|
||||
): Promise<CompletionItem[]> {
|
||||
return (
|
||||
(await this.activeVisualizationBackend?.getCompletions(
|
||||
text,
|
||||
column
|
||||
)) || []
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
GraphNode,
|
||||
GraphVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class GenericVisualizationSupport
|
||||
implements DebugSessionVisualizationSupport {
|
||||
constructor(private readonly debuggerView: DebuggerViewProxy) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
return new GenericVisualizationBackend(session, this.debuggerView);
|
||||
}
|
||||
}
|
||||
|
||||
export class GenericVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = "text";
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy
|
||||
) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
public async getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
|
||||
const finalExpression = this.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
let reply: { result: string; variablesReference: number };
|
||||
try {
|
||||
reply = await this.debugSession.evaluate({
|
||||
expression: finalExpression,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
// Use structural information about variables
|
||||
// from the evaluation response if present.
|
||||
if (reply.variablesReference) {
|
||||
const graph = await this.constructGraphFromVariablesReference(
|
||||
reply.result,
|
||||
reply.variablesReference
|
||||
);
|
||||
|
||||
return {
|
||||
kind: "data",
|
||||
result: {
|
||||
availableExtractors: [],
|
||||
usedExtractor: {
|
||||
id: "generic" as any,
|
||||
name: "Generic",
|
||||
priority: 1,
|
||||
},
|
||||
data: graph,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return parseEvaluationResultFromGenericDebugAdapter(
|
||||
reply.result,
|
||||
{
|
||||
debugAdapterType: this.debugSession.session
|
||||
.configuration.type,
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: [
|
||||
"An error occurred while evaluating the expression:",
|
||||
error.message,
|
||||
`Used debug adapter: ${this.debugSession.session.configuration.type}`,
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Evaluated expression is",
|
||||
{ kind: "code", content: finalExpression },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs GraphVisualizationData from a DAP variables
|
||||
* reference by successively querying the debug adapter for
|
||||
* variables. Objects are considered to be equivalent if
|
||||
* they share the same variables reference (this is important
|
||||
* for representing cyclic relationships).
|
||||
*
|
||||
* @param rootLabel - The root object's label
|
||||
* @param rootVariablesReference - The root object's DAP variables reference
|
||||
* @param maxDepth - The maximum depth to search at
|
||||
* @param maxKnownNodes - The maximum number of nodes
|
||||
*/
|
||||
private async constructGraphFromVariablesReference(
|
||||
rootLabel: string,
|
||||
rootVariablesReference: number,
|
||||
maxDepth: number = 30,
|
||||
maxKnownNodes: number = 50
|
||||
): Promise<GraphVisualizationData> {
|
||||
// Perform a breadth-first search on the object to construct the graph
|
||||
|
||||
const graph: GraphVisualizationData = {
|
||||
kind: { graph: true },
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
const knownNodeIds: { [ref: number]: string } = {};
|
||||
const bfsQueue: {
|
||||
source: { id: string; name: string } | undefined;
|
||||
label: string;
|
||||
variablesReference: number;
|
||||
depth: number;
|
||||
}[] = [
|
||||
{
|
||||
source: undefined,
|
||||
label: rootLabel,
|
||||
variablesReference: rootVariablesReference,
|
||||
depth: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let knownCount: number = 0;
|
||||
|
||||
do {
|
||||
const variable = bfsQueue.shift()!;
|
||||
const hasChilds = variable.variablesReference > 0;
|
||||
|
||||
if (variable.depth > maxDepth) {
|
||||
break;
|
||||
}
|
||||
|
||||
let nodeId: string;
|
||||
|
||||
if (!hasChilds || !(variable.variablesReference in knownNodeIds)) {
|
||||
// The variable is a leaf or an unvisited object: create the node.
|
||||
|
||||
const node: GraphNode = {
|
||||
id: hasChilds
|
||||
? `${variable.variablesReference}`
|
||||
: `__${variable.label}@${knownCount}__`,
|
||||
label: variable.label,
|
||||
color: variable.depth == 0 ? "lightblue" : undefined,
|
||||
shape: "box",
|
||||
};
|
||||
|
||||
graph.nodes.push(node);
|
||||
knownCount++;
|
||||
|
||||
if (hasChilds) {
|
||||
knownNodeIds[variable.variablesReference] = node.id;
|
||||
|
||||
for (const child of await this.debugSession.getVariables({
|
||||
variablesReference: variable.variablesReference,
|
||||
})) {
|
||||
bfsQueue.push({
|
||||
source: { id: node.id, name: child.name },
|
||||
label: child.value,
|
||||
variablesReference: child.variablesReference,
|
||||
depth: variable.depth + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
nodeId = node.id;
|
||||
} else {
|
||||
// The variable is a visited object (e.g. due to a cyclic reference)
|
||||
|
||||
nodeId = knownNodeIds[variable.variablesReference];
|
||||
}
|
||||
|
||||
if (variable.source) {
|
||||
graph.edges.push({
|
||||
from: variable.source.id,
|
||||
to: nodeId,
|
||||
label: variable.source.name,
|
||||
});
|
||||
}
|
||||
} while (bfsQueue.length > 0 && knownCount <= maxKnownNodes);
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
protected getFinalExpression(args: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}): string {
|
||||
return args.expression;
|
||||
}
|
||||
|
||||
protected getContext(): "watch" | "repl" {
|
||||
// we will use "repl" as default so that results are not truncated.
|
||||
return "repl";
|
||||
}
|
||||
}
|
||||
430
extension/src/VisualizationBackend/JsVisualizationSupport.ts
Normal file
@@ -0,0 +1,430 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataResult,
|
||||
getExpressionForDataExtractorApi,
|
||||
getExpressionToInitializeDataExtractorApi,
|
||||
ApiHasNotBeenInitializedCode,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { existsSync, readFileSync, watch } from "fs";
|
||||
import { observable, reaction } from "mobx";
|
||||
import { window, workspace } from "vscode";
|
||||
import { Config } from "../Config";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackend,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class JsEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
const supportedDebugAdapters = [
|
||||
"node",
|
||||
"node2",
|
||||
"extensionHost",
|
||||
"chrome",
|
||||
"pwa-chrome",
|
||||
"pwa-node",
|
||||
"pwa-extensionHost",
|
||||
"node-terminal",
|
||||
"pwa-msedge",
|
||||
];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new JsVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
this.config
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = "javascript";
|
||||
private initializePromise: Promise<void> | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
private getContext(): "copy" | "repl" {
|
||||
if (this.debugSession.session.type.startsWith("pwa-")) {
|
||||
return "copy";
|
||||
}
|
||||
return "repl";
|
||||
}
|
||||
|
||||
public async getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const result = await this._getVisualizationData(args);
|
||||
|
||||
if (result.kind === "not-initialized") {
|
||||
await this.initializeApiSafe();
|
||||
|
||||
const result2 = await this._getVisualizationData(args);
|
||||
|
||||
if (result2.kind !== "not-initialized") {
|
||||
return result2;
|
||||
} else {
|
||||
return {
|
||||
kind: "error",
|
||||
message: "Could not initialize API",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
| { kind: "not-initialized" }
|
||||
> {
|
||||
try {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
|
||||
const variableNames = new Array<string>();
|
||||
if (frameId) {
|
||||
const scopes = await this.debugSession.getScopes({ frameId });
|
||||
const scopeVariables = await Promise.all(
|
||||
scopes
|
||||
.filter((s) => !s.expensive && s.name !== "Global")
|
||||
.map((s) =>
|
||||
this.debugSession.getVariables({
|
||||
variablesReference: s.variablesReference,
|
||||
})
|
||||
)
|
||||
);
|
||||
for (const variables of scopeVariables) {
|
||||
variableNames.push(
|
||||
...variables
|
||||
.filter((v) => v.value !== "undefined")
|
||||
.map((v) => v.name)
|
||||
);
|
||||
}
|
||||
|
||||
if (variableNames.length > 50) {
|
||||
variableNames.length = 50;
|
||||
}
|
||||
}
|
||||
|
||||
const preferredExtractorExpr = preferredExtractorId
|
||||
? `"${preferredExtractorId}"`
|
||||
: "undefined";
|
||||
|
||||
const body = `${getExpressionForDataExtractorApi()}.getData(
|
||||
e => (${expression}),
|
||||
expr => eval(expr),
|
||||
${preferredExtractorExpr},
|
||||
{${variableNames.map((n) => `${n}: () => ${n}`).join(",")}},
|
||||
)`;
|
||||
|
||||
const wrappedExpr = `
|
||||
(() => {
|
||||
try {
|
||||
return ${body};
|
||||
} catch (e) {
|
||||
return JSON.stringify({
|
||||
kind: "Error",
|
||||
message: e.message,
|
||||
stack: e.stack
|
||||
});
|
||||
}
|
||||
})()
|
||||
`;
|
||||
|
||||
const reply = await this.debugSession.evaluate({
|
||||
expression: wrappedExpr,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
const resultStr = reply.result;
|
||||
const jsonData =
|
||||
this.getContext() === "copy"
|
||||
? resultStr
|
||||
: resultStr.substr(1, resultStr.length - 2);
|
||||
const result = JSON.parse(jsonData) as DataResult;
|
||||
|
||||
if (result.kind === "NoExtractors") {
|
||||
throw new Error("No extractors");
|
||||
} else if (result.kind === "Error") {
|
||||
throw new Error(result.message);
|
||||
} else if (result.kind === "Data") {
|
||||
return {
|
||||
kind: "data",
|
||||
result: result.extractionResult,
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid Data");
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
typeof error.message === "string" &&
|
||||
error.message.indexOf(ApiHasNotBeenInitializedCode) !== -1
|
||||
) {
|
||||
return {
|
||||
kind: "not-initialized",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "error",
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private initializeApiSafe(): Promise<void> {
|
||||
if (!this.initializePromise) {
|
||||
this.initializePromise = this.initializeApi().finally(
|
||||
() => (this.initializePromise = undefined)
|
||||
);
|
||||
}
|
||||
return this.initializePromise;
|
||||
}
|
||||
|
||||
private async initializeApi(): Promise<void> {
|
||||
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten.
|
||||
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`;
|
||||
|
||||
await this.debugSession.evaluate({
|
||||
expression,
|
||||
frameId: undefined,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
await this.initializeCustomScripts();
|
||||
}
|
||||
|
||||
private customScripts: undefined | CustomScripts;
|
||||
|
||||
private async initializeCustomScripts(): Promise<void> {
|
||||
this.dispose.untrack(this.customScripts);
|
||||
this.customScripts?.dispose();
|
||||
this.customScripts = this.dispose.track(new CustomScripts(this.debugSession, this.config, () => this.onChangeEmitter.emit()));
|
||||
}
|
||||
}
|
||||
|
||||
class CustomScripts {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
@observable
|
||||
private filesVersionId: number = 0;
|
||||
|
||||
private readonly fileChangeDebouncedScheduler = new DebouncedScheduler(100);
|
||||
|
||||
constructor(private readonly debugSession: DebugSessionProxy, private readonly config: Config, private readonly changeHandler: () => void) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
const watchers = new Map<string, WatchedFile>();
|
||||
this.dispose.track({
|
||||
dispose: () => {
|
||||
for (const w of watchers.values()) {
|
||||
w.dispose();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.dispose.track({
|
||||
dispose: reaction(
|
||||
() => ({
|
||||
customScriptPaths: [...this.config.customScriptPaths],
|
||||
version: this.filesVersionId,
|
||||
}),
|
||||
async ({ customScriptPaths }) => {
|
||||
const promises = new Array<
|
||||
Promise<[Result, WatchedFile]>
|
||||
>();
|
||||
|
||||
const activePaths = new Set<string>();
|
||||
for (const fsPath of customScriptPaths) {
|
||||
activePaths.add(fsPath);
|
||||
let entry = watchers.get(fsPath);
|
||||
if (!entry) {
|
||||
entry = new WatchedFile(
|
||||
fsPath,
|
||||
this.debugSession,
|
||||
() => {
|
||||
this.fileChangeDebouncedScheduler.run(
|
||||
() => {
|
||||
this.filesVersionId++;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
watchers.set(fsPath, entry);
|
||||
}
|
||||
|
||||
promises.push(entry.update().then((e) => [e, entry!]));
|
||||
}
|
||||
|
||||
for (const [fsPath, entry] of watchers) {
|
||||
if (!activePaths.has(fsPath)) {
|
||||
promises.push(
|
||||
entry.remove().then((e) => [e, entry])
|
||||
);
|
||||
entry.dispose();
|
||||
watchers.delete(fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
const errors = results.filter(([r]) => r.error);
|
||||
for (const [e, watcher] of errors) {
|
||||
window.showErrorMessage(
|
||||
`Error while evaluating "${watcher.fsPath}": ${
|
||||
e.error!.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
if (promises.length > 0) {
|
||||
this.changeHandler();
|
||||
}
|
||||
},
|
||||
{
|
||||
fireImmediately: true,
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface Result {
|
||||
error?: { message: string };
|
||||
}
|
||||
|
||||
class WatchedFile {
|
||||
public static nextId = 0;
|
||||
private readonly watcher = watch(
|
||||
this.fsPath,
|
||||
{ encoding: "utf-8" },
|
||||
this.changeHandler
|
||||
);
|
||||
public readonly id = WatchedFile.nextId++;
|
||||
public lastContent: string | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
public readonly fsPath: string,
|
||||
private readonly debugSession: DebugSessionProxy,
|
||||
private readonly changeHandler: () => void
|
||||
) {}
|
||||
|
||||
public dispose(): void {
|
||||
this.watcher.close();
|
||||
}
|
||||
|
||||
public async update(): Promise<Result> {
|
||||
if (!existsSync(this.fsPath)) {
|
||||
return {
|
||||
error: { message: `File does not exist.` },
|
||||
};
|
||||
}
|
||||
|
||||
const scriptContent = readFileSync(this.fsPath, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
|
||||
if (this.lastContent === scriptContent) {
|
||||
return {};
|
||||
}
|
||||
this.lastContent = scriptContent;
|
||||
|
||||
const expression = `
|
||||
(
|
||||
load => {
|
||||
var module = {};
|
||||
load(module);
|
||||
${getExpressionForDataExtractorApi()}.registerDataExtractorsSource("${
|
||||
this.id
|
||||
}", module.exports);
|
||||
}
|
||||
)
|
||||
(
|
||||
function (module) { ${scriptContent} }
|
||||
)`;
|
||||
try {
|
||||
await this.debugSession.evaluate({
|
||||
expression,
|
||||
frameId: undefined,
|
||||
context: "repl",
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: { message: e.message } };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public async remove(): Promise<Result> {
|
||||
const expression = `${getExpressionForDataExtractorApi()}.unregisterDataExtractorsSource("${
|
||||
this.id
|
||||
}")`;
|
||||
try {
|
||||
await this.debugSession.evaluate({
|
||||
expression,
|
||||
frameId: undefined,
|
||||
context: "repl",
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: { message: e.message } };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
class DebouncedScheduler {
|
||||
private timeout: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(private readonly debounceTimeout: number) {}
|
||||
|
||||
public run(action: () => void): void {
|
||||
this.clear();
|
||||
this.timeout = setTimeout(action, this.debounceTimeout);
|
||||
}
|
||||
|
||||
private clear() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
149
extension/src/VisualizationBackend/PyVisualizationSupport.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
GraphNode,
|
||||
GraphVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
import { Config } from "../Config";
|
||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class PyEvaluationEngine
|
||||
implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) { }
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
|
||||
const supportedDebugAdapters = [
|
||||
"python",
|
||||
];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new PyVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
this.config
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = "python";
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
protected getContext(): "watch" | "repl" {
|
||||
// we will use "repl" as default so that results are not truncated.
|
||||
return "repl";
|
||||
}
|
||||
|
||||
public async getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
|
||||
const finalExpression = this.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
let reply: { result: string; variablesReference: number };
|
||||
try {
|
||||
// inject vscodedebugvisualizer for python
|
||||
await this.debugSession.evaluate({
|
||||
expression: 'from vscodedebugvisualizer import visualize\ntry:\n import debugvisualizer\nexcept ImportError:\n pass',
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
reply = await this.debugSession.evaluate({
|
||||
expression: finalExpression,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
let result = reply.result;
|
||||
// remove the initial escape by the the debug session e.g. `''{"kind": {"text": true}, "text": "{"asdf1\'"}''`
|
||||
result = result.replace(/\\'/g, "'");
|
||||
result = result.replace(/\\\\/g, "\\");
|
||||
|
||||
return parseEvaluationResultFromGenericDebugAdapter(
|
||||
result,
|
||||
{
|
||||
debugAdapterType: this.debugSession.session
|
||||
.configuration.type,
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
let errorTyped = error as Error;
|
||||
if (errorTyped.message.includes("ModuleNotFoundError: No module named 'vscodedebugvisualizer'")) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: ["Pleas make sure vscodedebugvisualizer is installed: `pip install vscodedebugvisualizer`"],
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: [
|
||||
"An error occurred while evaluating the expression:",
|
||||
errorTyped.message,
|
||||
`Used debug adapter: ${this.debugSession.session.configuration.type}`,
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Evaluated expression is",
|
||||
{ kind: "code", content: finalExpression },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected getFinalExpression(args: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}): string {
|
||||
// wrap expression with visualize function
|
||||
let pythonInject = "";
|
||||
pythonInject += "visualize(" + args.expression + ")";
|
||||
return pythonInject;
|
||||
}
|
||||
|
||||
}
|
||||
80
extension/src/VisualizationBackend/VisualizationBackend.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { CompletionItem, FormattedMessage } from "../webviewContract";
|
||||
import { reaction } from "mobx";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { EventEmitter, EventSource } from "@hediet/std/events";
|
||||
|
||||
export interface DebugSessionVisualizationSupport {
|
||||
createBackend(session: DebugSessionProxy): VisualizationBackend | undefined;
|
||||
}
|
||||
|
||||
export interface VisualizationBackend extends Disposable {
|
||||
readonly onChange: EventSource;
|
||||
|
||||
getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
>;
|
||||
|
||||
/**
|
||||
* The language that expressions must be written in.
|
||||
*/
|
||||
readonly expressionLanguageId: string | undefined;
|
||||
|
||||
getCompletions(text: string, column: number): Promise<CompletionItem[]>;
|
||||
}
|
||||
|
||||
export interface GetVisualizationDataArgs {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}
|
||||
|
||||
export abstract class VisualizationBackendBase implements VisualizationBackend {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
protected readonly onChangeEmitter = new EventEmitter();
|
||||
public readonly onChange = this.onChangeEmitter;
|
||||
|
||||
constructor(
|
||||
protected readonly debugSession: DebugSessionProxy,
|
||||
protected readonly debuggerView: DebuggerViewProxy
|
||||
) {
|
||||
this.dispose.track({
|
||||
dispose: reaction(
|
||||
() => debuggerView.getActiveStackFrameId(debugSession),
|
||||
activeStackFrameId => {
|
||||
if (activeStackFrameId !== undefined) {
|
||||
this.onChangeEmitter.emit();
|
||||
}
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
public abstract readonly expressionLanguageId: string | undefined;
|
||||
|
||||
public abstract getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
>;
|
||||
|
||||
public async getCompletions(
|
||||
text: string,
|
||||
column: number
|
||||
): Promise<CompletionItem[]> {
|
||||
return await this.debugSession.getCompletions({
|
||||
text,
|
||||
frameId: this.debuggerView.getActiveStackFrameId(this.debugSession),
|
||||
column,
|
||||
});
|
||||
}
|
||||
}
|
||||
7
extension/src/VisualizationBackend/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from "./VisualizationBackend";
|
||||
|
||||
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
|
||||
export { JsEvaluationEngine } from "./JsVisualizationSupport";
|
||||
export { PyEvaluationEngine } from "./PyVisualizationSupport";
|
||||
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
|
||||
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
DataExtractionResult,
|
||||
isVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
|
||||
export interface ParseEvaluationResultContext {
|
||||
debugAdapterType: string;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
|
||||
export interface EvaluationWatchService {
|
||||
createEvaluationWatcher(
|
||||
export interface VisualizationWatchModel {
|
||||
createWatch(
|
||||
expression: string,
|
||||
options: EvaluationWatcherOptions
|
||||
): EvaluationWatcher;
|
||||
options: VisualizationWatchOptions
|
||||
): VisualizationWatch;
|
||||
|
||||
getCompletions(text: string, column: number): Promise<CompletionItem[]>;
|
||||
|
||||
@@ -17,14 +17,17 @@ export interface EvaluationWatchService {
|
||||
readonly languageId: string | undefined;
|
||||
}
|
||||
|
||||
export interface EvaluationWatcherOptions {
|
||||
export interface VisualizationWatchOptions {
|
||||
preferredDataExtractor?: DataExtractorId | undefined;
|
||||
}
|
||||
|
||||
export interface EvaluationWatcher {
|
||||
export interface VisualizationWatch {
|
||||
/** This field is constant. */
|
||||
readonly expression: string;
|
||||
|
||||
/** This field is observable */
|
||||
readonly state: DataExtractionState;
|
||||
|
||||
/** This field is observable */
|
||||
readonly preferredDataExtractor: DataExtractorId | undefined;
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass } from "@hediet/node-reload";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
import { wait } from "@hediet/std/timer";
|
||||
import { action, observable } from "mobx";
|
||||
import { CancellationToken, CancellationTokenSource } from "vscode";
|
||||
import { VisualizationBackend } from "../VisualizationBackend/VisualizationBackend";
|
||||
import { CompletionItem, DataExtractionState } from "../webviewContract";
|
||||
import {
|
||||
VisualizationWatch,
|
||||
VisualizationWatchModel,
|
||||
VisualizationWatchOptions,
|
||||
} from "./VisualizationWatchModel";
|
||||
|
||||
@hotClass(module)
|
||||
export class VisualizationWatchModelImpl implements VisualizationWatchModel {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly watchers = new Set<ObservableVisualizationWatch>();
|
||||
|
||||
constructor(private readonly visualizationBackend: VisualizationBackend) {
|
||||
this.dispose.track(
|
||||
visualizationBackend.onChange.sub(() => {
|
||||
if (visualizationBackend.expressionLanguageId !== undefined) {
|
||||
this.lastLanguageId =
|
||||
visualizationBackend.expressionLanguageId;
|
||||
}
|
||||
})
|
||||
);
|
||||
this.lastLanguageId = visualizationBackend.expressionLanguageId;
|
||||
}
|
||||
|
||||
public createWatch(
|
||||
expression: string,
|
||||
options: VisualizationWatchOptions
|
||||
): VisualizationWatch {
|
||||
const w = new ObservableVisualizationWatch(
|
||||
expression,
|
||||
options,
|
||||
this.visualizationBackend
|
||||
);
|
||||
w.onDispose.sub(() => {
|
||||
this.watchers.delete(w);
|
||||
});
|
||||
this.watchers.add(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
@observable
|
||||
private lastLanguageId: string | undefined = undefined;
|
||||
|
||||
get languageId(): string | undefined {
|
||||
return this.lastLanguageId;
|
||||
}
|
||||
|
||||
public getCompletions(
|
||||
text: string,
|
||||
column: number
|
||||
): Promise<CompletionItem[]> {
|
||||
return this.visualizationBackend.getCompletions(text, column);
|
||||
}
|
||||
}
|
||||
|
||||
class ObservableVisualizationWatch implements VisualizationWatch {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly onDisposeEmitter = new EventEmitter();
|
||||
public readonly onDispose = this.onDisposeEmitter.asEvent();
|
||||
|
||||
constructor(
|
||||
public readonly expression: string,
|
||||
options: VisualizationWatchOptions,
|
||||
private readonly visualizationBackend: VisualizationBackend
|
||||
) {
|
||||
this._preferredDataExtractor = options.preferredDataExtractor;
|
||||
|
||||
this.dispose.track(
|
||||
visualizationBackend.onChange.sub(() => {
|
||||
this.refresh();
|
||||
})
|
||||
);
|
||||
|
||||
this.dispose.track({
|
||||
dispose: () => {
|
||||
this.onDisposeEmitter.emit();
|
||||
},
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
private _preferredDataExtractor: DataExtractorId | undefined = undefined;
|
||||
|
||||
public get preferredDataExtractor(): DataExtractorId | undefined {
|
||||
return this._preferredDataExtractor;
|
||||
}
|
||||
|
||||
@action
|
||||
public setPreferredDataExtractor(id: DataExtractorId | undefined): void {
|
||||
this._preferredDataExtractor = id;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
private runningRefreshOperation: Disposable | undefined;
|
||||
|
||||
public refresh(): void {
|
||||
if (this.runningRefreshOperation) {
|
||||
this.runningRefreshOperation.dispose();
|
||||
}
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
this.runningRefreshOperation = {
|
||||
dispose: () => {
|
||||
tokenSource.cancel();
|
||||
},
|
||||
};
|
||||
this._refresh(tokenSource.token);
|
||||
}
|
||||
|
||||
private async _refresh(token: CancellationToken): Promise<void> {
|
||||
/*const session = this.debuggerView.activeDebugSession;
|
||||
if (!session) {
|
||||
this._state = { kind: "noDebugSession" };
|
||||
return;
|
||||
}*/
|
||||
|
||||
//const frameId = this.debuggerView.activeFrameId;
|
||||
|
||||
this._state = { kind: "loading" };
|
||||
|
||||
/*
|
||||
const visualizationBackend = this.getVisualizationBackend();
|
||||
if (!visualizationBackend) {
|
||||
this._state = {
|
||||
kind: "error",
|
||||
message: `The debug adapter "${session.session.type}" is not supported.`,
|
||||
};
|
||||
return;
|
||||
}*/
|
||||
|
||||
const result = await this.visualizationBackend.getVisualizationData({
|
||||
expression: this.expression,
|
||||
//frameId,
|
||||
preferredExtractorId: this.preferredDataExtractor,
|
||||
});
|
||||
|
||||
if (result.kind === "error") {
|
||||
// give cancellation requested more time in case of errors
|
||||
await wait(330);
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state = result;
|
||||
}
|
||||
|
||||
@observable
|
||||
public _state: DataExtractionState = { kind: "loading" };
|
||||
public get state(): DataExtractionState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
2
extension/src/VisualizationWatchModel/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./VisualizationWatchModelImpl";
|
||||
export * from "./VisualizationWatchModel";
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { observable, action } from "mobx";
|
||||
import { VsCodeDebugger } from "./VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||
|
||||
/**
|
||||
* Tracks the currently active debug session and its currently active stack frame.
|
||||
* This is currently all faked. A proper API would be nice.
|
||||
*/
|
||||
export class VsCodeDebuggerView {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
@observable private _activeDebugSession: EnhancedDebugSession | undefined;
|
||||
|
||||
public get activeDebugSession(): EnhancedDebugSession | undefined {
|
||||
return this._activeDebugSession;
|
||||
}
|
||||
|
||||
public get activeFrameId(): number | undefined {
|
||||
if (!this._activeDebugSession) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._activeDebugSession["activeStackFrame"];
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private vsCodeDebugger: VsCodeDebugger) {
|
||||
this.dispose.track(
|
||||
debug.onDidChangeActiveDebugSession(activeSession => {
|
||||
this.updateActiveDebugSession(activeSession);
|
||||
})
|
||||
);
|
||||
this.updateActiveDebugSession(debug.activeDebugSession);
|
||||
}
|
||||
|
||||
@action
|
||||
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
|
||||
if (!activeSession) {
|
||||
this._activeDebugSession = undefined;
|
||||
} else {
|
||||
const s = this.vsCodeDebugger.getEnhancedDebugSession(
|
||||
activeSession
|
||||
);
|
||||
this._activeDebugSession = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,17 @@ registerUpdateReconciler(module);
|
||||
import { InternalWebviewManager } from "./webview/InternalWebviewManager";
|
||||
import { WebviewServer } from "./webview/WebviewServer";
|
||||
import { Config } from "./Config";
|
||||
import { VsCodeDebugger } from "./debugger/VsCodeDebugger";
|
||||
import { VsCodeDebuggerView } from "./debugger/VsCodeDebuggerView";
|
||||
import { EvaluationWatchServiceImpl } from "./EvaluationWatchService";
|
||||
import { DebuggerProxy } from "./proxies/DebuggerProxy";
|
||||
import { DebuggerViewProxy } from "./proxies/DebuggerViewProxy";
|
||||
import { VisualizationWatchModelImpl } from "./VisualizationWatchModel";
|
||||
import {
|
||||
ComposedEvaluationEngine,
|
||||
ComposedVisualizationSupport,
|
||||
JsEvaluationEngine,
|
||||
GenericEvaluationEngine,
|
||||
ConfiguredEvaluationEngine,
|
||||
} from "./EvaluationWatchService/EvaluationEngine";
|
||||
PyEvaluationEngine,
|
||||
GenericVisualizationSupport,
|
||||
ConfigurableVisualizationSupport,
|
||||
} from "./VisualizationBackend";
|
||||
import { DispatchingVisualizationBackend } from "./VisualizationBackend/DispatchingVisualizationBackend";
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
@@ -31,24 +33,30 @@ export function activate(context: ExtensionContext) {
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
export function deactivate() { }
|
||||
|
||||
export class Extension {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
private readonly config = new Config();
|
||||
private readonly debugger = this.dispose.track(new VsCodeDebugger());
|
||||
private readonly debugger = this.dispose.track(new DebuggerProxy());
|
||||
private readonly debuggerView = this.dispose.track(
|
||||
new VsCodeDebuggerView(this.debugger)
|
||||
new DebuggerViewProxy(this.debugger)
|
||||
);
|
||||
|
||||
public readonly dataSource = new EvaluationWatchServiceImpl(
|
||||
this.debuggerView,
|
||||
new ComposedEvaluationEngine([
|
||||
new ConfiguredEvaluationEngine(this.config),
|
||||
new JsEvaluationEngine(),
|
||||
new GenericEvaluationEngine(),
|
||||
])
|
||||
public readonly dataSource = new VisualizationWatchModelImpl(
|
||||
new DispatchingVisualizationBackend(
|
||||
new ComposedVisualizationSupport([
|
||||
new ConfigurableVisualizationSupport(
|
||||
this.config,
|
||||
this.debuggerView
|
||||
),
|
||||
new JsEvaluationEngine(this.debuggerView, this.config),
|
||||
new PyEvaluationEngine(this.debuggerView, this.config),
|
||||
new GenericVisualizationSupport(this.debuggerView),
|
||||
]),
|
||||
this.debuggerView
|
||||
)
|
||||
);
|
||||
|
||||
private readonly server = new WebviewServer(this.dataSource, this.config);
|
||||
@@ -82,7 +90,26 @@ export class Extension {
|
||||
}
|
||||
|
||||
const selection = editor.selection;
|
||||
const selectedText = editor.document.getText(selection);
|
||||
|
||||
let selectedText;
|
||||
if (selection.isEmpty) {
|
||||
const lineText = editor.document.lineAt(selection.start)
|
||||
.text;
|
||||
const regexp = /`(.*)`/g;
|
||||
selectedText = "";
|
||||
let match;
|
||||
while ((match = regexp.exec(lineText))) {
|
||||
if (
|
||||
match.index <= selection.start.character &&
|
||||
selection.start.character <=
|
||||
match.index + match[0].length
|
||||
) {
|
||||
selectedText = match[1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectedText = editor.document.getText(selection);
|
||||
}
|
||||
|
||||
if (!selectedText) {
|
||||
return;
|
||||
|
||||
@@ -2,8 +2,8 @@ import { DebugSession } from "vscode";
|
||||
import { CompletionItem } from "../webviewContract";
|
||||
import { observable } from "mobx";
|
||||
|
||||
export class EnhancedDebugSession {
|
||||
@observable protected activeStackFrame: number | undefined;
|
||||
export class DebugSessionProxy {
|
||||
@observable private _activeStackFrameId: number | undefined;
|
||||
|
||||
constructor(public readonly session: DebugSession) {}
|
||||
|
||||
@@ -46,6 +46,38 @@ export class EnhancedDebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
public async getScopes(args: { frameId: number }): Promise<Scope[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("scopes", {
|
||||
frameId: args.frameId,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
return reply.scopes;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async getVariables(args: {
|
||||
variablesReference: number;
|
||||
}): Promise<Variable[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("variables", {
|
||||
variablesReference: args.variablesReference,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
return reply.variables;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given expression.
|
||||
* If context is "watch", long results are usually shortened.
|
||||
@@ -55,15 +87,31 @@ export class EnhancedDebugSession {
|
||||
expression: string;
|
||||
frameId: number | undefined;
|
||||
context: "watch" | "repl" | "copy";
|
||||
}): Promise<{ result: string }> {
|
||||
}): Promise<{ result: string; variablesReference: number }> {
|
||||
const reply = await this.session.customRequest("evaluate", {
|
||||
expression: args.expression,
|
||||
frameId: args.frameId,
|
||||
context: args.context,
|
||||
});
|
||||
return { result: reply.result };
|
||||
return {
|
||||
result: reply.result,
|
||||
variablesReference: reply.variablesReference,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface Scope {
|
||||
name: string;
|
||||
expensive: boolean;
|
||||
variablesReference: number;
|
||||
}
|
||||
|
||||
interface Variable {
|
||||
name: string;
|
||||
value: string;
|
||||
variablesReference: number;
|
||||
}
|
||||
|
||||
interface StackFrame {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -2,26 +2,24 @@ import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
import { runInAction } from "mobx";
|
||||
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||
import { DebugSessionProxy } from "./DebugSessionProxy";
|
||||
|
||||
/**
|
||||
* Decorates DebugSession instances and emits an event for new debug sessions.
|
||||
*/
|
||||
export class VsCodeDebugger {
|
||||
export class DebuggerProxy {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly sessions = new Map<DebugSession, EnhancedDebugSession>();
|
||||
private readonly sessions = new Map<DebugSession, DebugSessionProxy>();
|
||||
|
||||
private readonly _onDidStartDebugSession = new EventEmitter<{
|
||||
session: EnhancedDebugSession;
|
||||
session: DebugSessionProxy;
|
||||
}>();
|
||||
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
||||
|
||||
public getEnhancedDebugSession(
|
||||
session: DebugSession
|
||||
): EnhancedDebugSession {
|
||||
public getDebugSessionProxy(session: DebugSession): DebugSessionProxy {
|
||||
let result = this.sessions.get(session);
|
||||
if (!result) {
|
||||
result = new EnhancedDebugSession(session);
|
||||
result = new DebugSessionProxy(session);
|
||||
this.sessions.set(session, result);
|
||||
}
|
||||
return result;
|
||||
@@ -38,9 +36,7 @@ export class VsCodeDebugger {
|
||||
}),
|
||||
debug.registerDebugAdapterTrackerFactory("*", {
|
||||
createDebugAdapterTracker: session => {
|
||||
const extendedSession = this.getEnhancedDebugSession(
|
||||
session
|
||||
);
|
||||
const extendedSession = this.getDebugSessionProxy(session);
|
||||
return {
|
||||
onDidSendMessage: async msg => {
|
||||
type Message =
|
||||
@@ -90,7 +86,7 @@ export class VsCodeDebugger {
|
||||
startFrame: 0,
|
||||
levels: 1,
|
||||
});
|
||||
extendedSession["activeStackFrame"] =
|
||||
extendedSession["_activeStackFrameId"] =
|
||||
r.stackFrames.length > 0
|
||||
? r.stackFrames[0].id
|
||||
: undefined;
|
||||
@@ -103,7 +99,7 @@ export class VsCodeDebugger {
|
||||
m.command === "stepOut"
|
||||
) {
|
||||
extendedSession[
|
||||
"activeStackFrame"
|
||||
"_activeStackFrameId"
|
||||
] = undefined;
|
||||
}
|
||||
}
|
||||
41
extension/src/proxies/DebuggerViewProxy.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { observable, action } from "mobx";
|
||||
import { DebuggerProxy } from "./DebuggerProxy";
|
||||
import { DebugSessionProxy } from "./DebugSessionProxy";
|
||||
|
||||
/**
|
||||
* Tracks the currently active debug session and its currently active stack frame.
|
||||
* This is currently all faked. A proper API would be nice.
|
||||
*/
|
||||
export class DebuggerViewProxy {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
@observable private _activeDebugSession: DebugSessionProxy | undefined;
|
||||
|
||||
public get activeDebugSession(): DebugSessionProxy | undefined {
|
||||
return this._activeDebugSession;
|
||||
}
|
||||
|
||||
public getActiveStackFrameId(
|
||||
session: DebugSessionProxy
|
||||
): number | undefined {
|
||||
return session["_activeStackFrameId"];
|
||||
}
|
||||
|
||||
constructor(private debuggerProxy: DebuggerProxy) {
|
||||
this.dispose.track(
|
||||
debug.onDidChangeActiveDebugSession(activeSession => {
|
||||
this.updateActiveDebugSession(activeSession);
|
||||
})
|
||||
);
|
||||
this.updateActiveDebugSession(debug.activeDebugSession);
|
||||
}
|
||||
|
||||
@action
|
||||
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
|
||||
this._activeDebugSession = activeSession
|
||||
? this.debuggerProxy.getDebugSessionProxy(activeSession)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
@@ -90,11 +90,11 @@ function getDebugVisualizerWebviewHtml(
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline'; worker-src * data: 'unsafe-inline' 'unsafe-eval'; font-src * 'unsafe-inline' 'unsafe-eval';">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline'; worker-src * data: blob: data: 'unsafe-inline' 'unsafe-eval'; font-src * 'unsafe-inline' 'unsafe-eval' 'self' data: blob:;">
|
||||
<style>
|
||||
html { height: 100%; width: 100%; padding: 0; margin: 0; }
|
||||
body { height: 100%; width: 100%; padding: 0; margin: 0; }
|
||||
iframe { height: 100%; width: 100%; padding: 0; margin: 0; border: 0; display: block; }
|
||||
iframe { height: 100%; width: 100%; padding: 0; margin: 0; border: 0; display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -112,11 +112,11 @@ function getDebugVisualizerWebviewHtml(
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window.frames[0]) {
|
||||
if (event.data.command === "setState") {
|
||||
console.log("setState", event.data.state);
|
||||
// console.log("setState", event.data.state);
|
||||
api.setState(event.data.state);
|
||||
}
|
||||
if (event.data.command === "getState") {
|
||||
console.log("getState, sent ", api.getState());
|
||||
// console.log("getState, sent ", api.getState());
|
||||
window.frames[0].postMessage({ command: "getStateResult", state: api.getState() }, "*");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Disposable } from "@hediet/std/disposable";
|
||||
import { webviewContract } from "../webviewContract";
|
||||
import { ConsoleRpcLogger, RequestHandlingError } from "@hediet/typed-json-rpc";
|
||||
import {
|
||||
EvaluationWatcher,
|
||||
EvaluationWatchService,
|
||||
} from "../EvaluationWatchService";
|
||||
VisualizationWatch,
|
||||
VisualizationWatchModel,
|
||||
} from "../VisualizationWatchModel";
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import { observable, autorun } from "mobx";
|
||||
import { WebviewServer } from "./WebviewServer";
|
||||
@@ -15,12 +15,12 @@ import { Config } from "../Config";
|
||||
export class WebviewConnection {
|
||||
public readonly dispose = Disposable.fn();
|
||||
@observable
|
||||
private watcher: EvaluationWatcher | undefined = undefined;
|
||||
private watcher: VisualizationWatch | undefined = undefined;
|
||||
|
||||
private readonly client: typeof webviewContract["TClientInterface"];
|
||||
|
||||
constructor(
|
||||
evaluationWatchService: EvaluationWatchService,
|
||||
evaluationWatchService: VisualizationWatchModel,
|
||||
stream: WebSocketStream,
|
||||
server: WebviewServer,
|
||||
config: Config,
|
||||
@@ -55,19 +55,16 @@ export class WebviewConnection {
|
||||
setExpression: async ({ newExpression }) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
let oldPreferredDataExtractor: EvaluationWatcher["preferredDataExtractor"];
|
||||
let oldPreferredDataExtractor: VisualizationWatch["preferredDataExtractor"];
|
||||
if (this.watcher) {
|
||||
oldPreferredDataExtractor = this.watcher
|
||||
.preferredDataExtractor;
|
||||
this.dispose.untrack(this.watcher).dispose();
|
||||
}
|
||||
this.watcher = this.dispose.track(
|
||||
evaluationWatchService.createEvaluationWatcher(
|
||||
newExpression,
|
||||
{
|
||||
preferredDataExtractor: oldPreferredDataExtractor,
|
||||
}
|
||||
)
|
||||
evaluationWatchService.createWatch(newExpression, {
|
||||
preferredDataExtractor: oldPreferredDataExtractor,
|
||||
})
|
||||
);
|
||||
},
|
||||
openInBrowser: async ({}) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as serveStatic from "serve-static";
|
||||
import { Config } from "../Config";
|
||||
import cryptoRandomString = require("crypto-random-string");
|
||||
import { distPath } from "debug-visualizer-webview";
|
||||
import { EvaluationWatchService } from "../EvaluationWatchService/EvaluationWatchService";
|
||||
import { VisualizationWatchModel } from "../VisualizationWatchModel/VisualizationWatchModel";
|
||||
import { URLSearchParams } from "url";
|
||||
import { autorun, reaction } from "mobx";
|
||||
|
||||
@@ -19,7 +19,7 @@ export class WebviewServer {
|
||||
public readonly connections = new Set<WebviewConnection>();
|
||||
|
||||
constructor(
|
||||
dataSource: EvaluationWatchService,
|
||||
dataSource: VisualizationWatchModel,
|
||||
private readonly config: Config
|
||||
) {
|
||||
const app = express();
|
||||
@@ -28,11 +28,13 @@ export class WebviewServer {
|
||||
|
||||
this.server = app.listen();
|
||||
|
||||
/*
|
||||
console.log(
|
||||
`Serving "${distPath}" on port ${
|
||||
(this.server.address() as AddressInfo).port
|
||||
}`
|
||||
);
|
||||
*/
|
||||
|
||||
const wss = new WebSocket.Server({ server: this.server });
|
||||
wss.on("connection", async ws => {
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
"classnames": "^2.2.6",
|
||||
"mobx": "^5.15.4",
|
||||
"mobx-react": "^6.1.8",
|
||||
"monaco-editor": "^0.20.0",
|
||||
"monaco-editor": "^0.25.2",
|
||||
"popper.js": "^1.16.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-measure": "^2.3.0",
|
||||
"@hediet/debug-visualizer-data-extraction": "*",
|
||||
"@hediet/visualization-bundle": "^0.1.1",
|
||||
"@hediet/visualization-core": "^0.1.0",
|
||||
"@hediet/visualization-bundle": "^0.2.3",
|
||||
"@hediet/visualization-core": "^0.2.0",
|
||||
"@knuddels/mobx-logger": "^1.1.1",
|
||||
"less-loader": "^6.2.0"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"file-loader": "^5.1.0",
|
||||
"fork-ts-checker-webpack-plugin": "^4.0.4",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
||||
"monaco-editor-webpack-plugin": "^4.0.0",
|
||||
"sass": "^1.25.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
|
||||
@@ -17,7 +17,6 @@ export class App extends React.Component {
|
||||
require("../vscode-dark.scss");
|
||||
}
|
||||
}
|
||||
console.log(this.model.theme);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -108,7 +108,7 @@ export class ExpressionInput extends React.Component<{ model: Model }> {
|
||||
@action.bound
|
||||
submit() {
|
||||
const val = this.model.getValue();
|
||||
console.log(val);
|
||||
//console.log(val);
|
||||
this.props.model.setExpression(val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import "./style.scss";
|
||||
import { App } from "./components/App";
|
||||
import * as monaco from "monaco-editor";
|
||||
|
||||
(globalThis as any).monaco = monaco;
|
||||
|
||||
const elem = document.createElement("div");
|
||||
elem.className = "react-root";
|
||||
|
||||
@@ -37,7 +37,6 @@ export class MonacoBridge {
|
||||
);
|
||||
|
||||
for (const file of commonTypes.keys()) {
|
||||
console.log(file);
|
||||
this.dispose.track(
|
||||
monaco.languages.typescript.javascriptDefaults.addExtraLib(
|
||||
commonTypes(file).default,
|
||||
|
||||
402
yarn.lock
@@ -487,10 +487,10 @@
|
||||
stacktracey "^1.2.112"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@hediet/monaco-editor-react@^0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.1.0.tgz#b00dfa28e2ffa09ecc748e35f4befc0e887947f2"
|
||||
integrity sha512-EFAmKW1u9ks2iqTP63qqLPUezKMAnK+UvZrvoRmUMCpRRxG9uduYGPQfASaHcIQYzOqzWaVKvCQN5wQQ+lSxIQ==
|
||||
"@hediet/monaco-editor-react@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.2.0.tgz#4a98102baf1942f210fc19fd406b6c82da3bf583"
|
||||
integrity sha512-EA71+iv9sV25+YgBQDJ4Ou17sOJCXQat7r5AOD5STZHcUQdSuQufI2t9ue+wJIm4048P9sQ/ruW3pYmwxF9Amw==
|
||||
|
||||
"@hediet/node-reload@^0.7.3":
|
||||
version "0.7.3"
|
||||
@@ -507,10 +507,10 @@
|
||||
stack-trace "^0.0.10"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@hediet/semantic-json@^0.3.11":
|
||||
version "0.3.11"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/semantic-json/-/semantic-json-0.3.11.tgz#9420a89d30645ba333dfd4f3f2c120f973297ee2"
|
||||
integrity sha512-KKw6ZcYHiyfhanNmln/WlG4KvZnMi/Ojy8O6HB6sqfDC5XCcNYjccUMMtaX23F/Hox0dWJPFqXYs54KJAA5itg==
|
||||
"@hediet/semantic-json@^0.3.15":
|
||||
version "0.3.15"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/semantic-json/-/semantic-json-0.3.15.tgz#e38f799b1a7dd2624a3ee2e05bca58cfe76cb58d"
|
||||
integrity sha512-8t1NXiYDF2wqIt1jEknxNnpucdtgFAI2MGSWnnewDhaYqWOJLMY/MxH60gDX03vycf2ySb6vEYpEwsiB6r0O3Q==
|
||||
|
||||
"@hediet/std@^0.6.0":
|
||||
version "0.6.0"
|
||||
@@ -550,22 +550,22 @@
|
||||
"@hediet/std" "^0.6.0"
|
||||
io-ts "^1.8.6"
|
||||
|
||||
"@hediet/visualization-bundle@^0.1.1":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.1.1.tgz#dc36d772def977cb97292890afb3b369760c620b"
|
||||
integrity sha512-X0Y0MQQHpZkMDSM9naLC5F3LVlSq+nQ6dNYkNz+3sF6PdvSf6xxUbRL4qbum2QCLqNRoiEu/HO6fjCU/gBvysA==
|
||||
"@hediet/visualization-bundle@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.3.tgz#8323706dcaaafc973fdc57da68e6527265e5bfcc"
|
||||
integrity sha512-RpzAST/K5O9FaEaCshHEY7VeGRVlwoTLLcWPkgxzeuZlRr5CNTw8d7Lznh+RaLD1rbz/UEezq2pI3qFYnq/z4w==
|
||||
dependencies:
|
||||
"@finos/perspective-viewer" "^0.5.2"
|
||||
"@finos/perspective-viewer-d3fc" "^0.5.2"
|
||||
"@finos/perspective-viewer-datagrid" "^0.5.2"
|
||||
"@hediet/monaco-editor-react" "^0.1.0"
|
||||
"@hediet/semantic-json" "^0.3.11"
|
||||
"@hediet/monaco-editor-react" "^0.2.0"
|
||||
"@hediet/semantic-json" "^0.3.15"
|
||||
"@hediet/std" "^0.6.0"
|
||||
classnames "^2.2.6"
|
||||
line-column "^1.0.2"
|
||||
mobx "^5.15.4"
|
||||
mobx-react "^6.1.8"
|
||||
monaco-editor "^0.20.0"
|
||||
monaco-editor "^0.25.2"
|
||||
plotly.js "^1.54.7"
|
||||
react "^16.12.0"
|
||||
react-dom "^16.12.0"
|
||||
@@ -576,12 +576,12 @@
|
||||
vis-network "^8.0.0"
|
||||
viz.js "^2.1.2"
|
||||
|
||||
"@hediet/visualization-core@^0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-core/-/visualization-core-0.1.0.tgz#f23706cbc6a9ffd411c300767077ca4c0c494c15"
|
||||
integrity sha512-v7aZnyJ0ax09x6mBqhVaJQVwos1X7nyEfymr85/wbvLuleDjisKUuLHBxGpmWYSBRFMZUKIHllaY48DEHjP8Ug==
|
||||
"@hediet/visualization-core@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-core/-/visualization-core-0.2.0.tgz#8fca0044d5d91e8c45354984ae9c131ef7850a76"
|
||||
integrity sha512-r/SYDUipaUSdu1JXL1X0Le5ZkBXVGbKXo6jkX0jFq3NPz/60+B4MnIQnk8D7TCWLXftGFtE6MSFejrE+miup4w==
|
||||
dependencies:
|
||||
"@hediet/semantic-json" "^0.3.11"
|
||||
"@hediet/semantic-json" "^0.3.15"
|
||||
"@hediet/std" "^0.6.0"
|
||||
react "^16.12.0"
|
||||
react-dom "^16.12.0"
|
||||
@@ -1623,6 +1623,14 @@ aws4@^1.8.0:
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
||||
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
|
||||
|
||||
azure-devops-node-api@^10.2.2:
|
||||
version "10.2.2"
|
||||
resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-10.2.2.tgz#9f557e622dd07bbaa9bd5e7e84e17c761e2151b2"
|
||||
integrity sha512-4TVv2X7oNStT0vLaEfExmy3J4/CzfuXolEcQl/BRUmvGySqKStTG2O55/hUQ0kM7UJlZBLgniM0SBq4d/WkKow==
|
||||
dependencies:
|
||||
tunnel "0.0.6"
|
||||
typed-rest-client "^1.8.4"
|
||||
|
||||
babel-code-frame@^6.22.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||
@@ -1789,7 +1797,7 @@ bonjour@^3.5.0:
|
||||
multicast-dns "^6.0.1"
|
||||
multicast-dns-service-types "^1.1.0"
|
||||
|
||||
boolbase@~1.0.0:
|
||||
boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
@@ -1930,6 +1938,11 @@ bubleify@^1.1.0, bubleify@^1.2.0:
|
||||
dependencies:
|
||||
buble "^0.19.3"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@@ -2020,6 +2033,14 @@ caching-transform@^4.0.0:
|
||||
package-hash "^4.0.0"
|
||||
write-file-atomic "^3.0.0"
|
||||
|
||||
call-bind@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
camel-case@3.0.x:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||
@@ -2096,6 +2117,30 @@ check-error@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
|
||||
|
||||
cheerio-select@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"
|
||||
integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==
|
||||
dependencies:
|
||||
css-select "^4.1.3"
|
||||
css-what "^5.0.1"
|
||||
domelementtype "^2.2.0"
|
||||
domhandler "^4.2.0"
|
||||
domutils "^2.7.0"
|
||||
|
||||
cheerio@^1.0.0-rc.9:
|
||||
version "1.0.0-rc.10"
|
||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e"
|
||||
integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==
|
||||
dependencies:
|
||||
cheerio-select "^1.5.0"
|
||||
dom-serializer "^1.3.2"
|
||||
domhandler "^4.2.0"
|
||||
htmlparser2 "^6.1.0"
|
||||
parse5 "^6.0.1"
|
||||
parse5-htmlparser2-tree-adapter "^6.0.1"
|
||||
tslib "^2.2.0"
|
||||
|
||||
chevrotain@^6.5.0:
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-6.5.0.tgz#dcbef415516b0af80fd423cc0d96b28d3f11374e"
|
||||
@@ -2431,6 +2476,11 @@ commander@^2.12.1, commander@^2.20.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
|
||||
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
|
||||
|
||||
commander@^6.1.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@~2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
@@ -2813,6 +2863,17 @@ css-select@^1.1.0:
|
||||
domutils "1.5.1"
|
||||
nth-check "~1.0.1"
|
||||
|
||||
css-select@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
|
||||
integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
css-what "^5.0.0"
|
||||
domhandler "^4.2.0"
|
||||
domutils "^2.6.0"
|
||||
nth-check "^2.0.0"
|
||||
|
||||
css-system-font-keywords@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz#85c6f086aba4eb32c571a3086affc434b84823ed"
|
||||
@@ -2823,6 +2884,11 @@ css-what@2.1:
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
|
||||
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
|
||||
|
||||
css-what@^5.0.0, css-what@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
|
||||
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
|
||||
|
||||
csscolorparser@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b"
|
||||
@@ -3396,6 +3462,11 @@ delegates@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
||||
|
||||
denodeify@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
|
||||
integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE=
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@@ -3507,6 +3578,15 @@ dom-serializer@0:
|
||||
domelementtype "^1.3.0"
|
||||
entities "^1.1.1"
|
||||
|
||||
dom-serializer@^1.0.1, dom-serializer@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
|
||||
integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.2.0"
|
||||
entities "^2.0.0"
|
||||
|
||||
dom4@^2.1.5:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.5.tgz#f98a94eb67b340f0fa5b42b0ee9c38cda035428e"
|
||||
@@ -3522,6 +3602,11 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
|
||||
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
|
||||
|
||||
domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
||||
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
||||
|
||||
domhandler@^2.3.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
|
||||
@@ -3529,6 +3614,13 @@ domhandler@^2.3.0:
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domhandler@^4.0.0, domhandler@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
|
||||
integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
domutils@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
|
||||
@@ -3545,6 +3637,15 @@ domutils@^1.5.1:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442"
|
||||
integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==
|
||||
dependencies:
|
||||
dom-serializer "^1.0.1"
|
||||
domelementtype "^2.2.0"
|
||||
domhandler "^4.2.0"
|
||||
|
||||
dotignore@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
|
||||
@@ -3681,6 +3782,16 @@ entities@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
|
||||
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
|
||||
|
||||
entities@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||
|
||||
entities@~2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
||||
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
||||
|
||||
errno@^0.1.1, errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||
@@ -4054,6 +4165,13 @@ faye-websocket@~0.11.1:
|
||||
dependencies:
|
||||
websocket-driver ">=0.5.1"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
figgy-pudding@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||
@@ -4387,6 +4505,15 @@ get-func-name@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
|
||||
|
||||
get-intrinsic@^1.0.2:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
get-node-dimensions@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz#fb7b4bb57060fb4247dd51c9d690dfbec56b0823"
|
||||
@@ -4837,6 +4964,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.6:
|
||||
version "7.1.7"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.1.6, glob@~7.1.6:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
@@ -5305,6 +5444,16 @@ htmlparser2@^3.3.0:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
htmlparser2@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
|
||||
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.0.0"
|
||||
domutils "^2.5.2"
|
||||
entities "^2.0.0"
|
||||
|
||||
http-deceiver@^1.2.7:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
|
||||
@@ -5416,6 +5565,11 @@ ignore@^3.3.5:
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
|
||||
|
||||
ignore@^5.1.8:
|
||||
version "5.1.8"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
|
||||
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
|
||||
|
||||
image-palette@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/image-palette/-/image-palette-2.1.0.tgz#d976525a1df75964ca125d2dba2741e92905547f"
|
||||
@@ -6132,6 +6286,11 @@ less@^3.11.3:
|
||||
native-request "^1.0.5"
|
||||
source-map "~0.6.0"
|
||||
|
||||
leven@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
||||
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
|
||||
|
||||
levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
@@ -6156,6 +6315,13 @@ line-column@^1.0.2:
|
||||
isarray "^1.0.0"
|
||||
isobject "^2.0.0"
|
||||
|
||||
linkify-it@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
|
||||
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
|
||||
dependencies:
|
||||
uc.micro "^1.0.1"
|
||||
|
||||
lit-html@^1.1.2:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.2.1.tgz#1fb933dc1e2ddc095f60b8086277d4fcd9d62cc8"
|
||||
@@ -6368,6 +6534,17 @@ marching-simplex-table@^1.0.0:
|
||||
dependencies:
|
||||
convex-hull "^1.0.3"
|
||||
|
||||
markdown-it@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
|
||||
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
entities "~2.0.0"
|
||||
linkify-it "^2.0.0"
|
||||
mdurl "^1.0.1"
|
||||
uc.micro "^1.0.5"
|
||||
|
||||
marky@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.1.tgz#a3fcf82ffd357756b8b8affec9fdbf3a30dc1b02"
|
||||
@@ -6423,6 +6600,11 @@ md5.js@^1.3.4:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
mdurl@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
@@ -6507,7 +6689,7 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
|
||||
dependencies:
|
||||
mime-db "1.40.0"
|
||||
|
||||
mime@1.6.0, mime@^1.4.1:
|
||||
mime@1.6.0, mime@^1.3.4, mime@^1.4.1:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
@@ -6679,17 +6861,17 @@ moment@^2.19.1:
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||
|
||||
monaco-editor-webpack-plugin@^1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.9.0.tgz#5b547281b9f404057dc5d8c5722390df9ac90be6"
|
||||
integrity sha512-tOiiToc94E1sb50BgZ8q8WK/bxus77SRrwCqIpAB5er3cpX78SULbEBY4YPOB8kDolOzKRt30WIHG/D6gz69Ww==
|
||||
monaco-editor-webpack-plugin@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.0.0.tgz#95be3f48f4220999b909266a9997727f0deab947"
|
||||
integrity sha512-4BT9XDRQXraMQjxEUjR+uuubRe3RIPkvVoGw8zwWG++s7wq6TAiXaSOMdkdS9TrjCREgSnygCOlVzY6MS8RPuA==
|
||||
dependencies:
|
||||
loader-utils "^1.2.3"
|
||||
loader-utils "^2.0.0"
|
||||
|
||||
monaco-editor@^0.20.0:
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
|
||||
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
|
||||
monaco-editor@^0.25.2:
|
||||
version "0.25.2"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.25.2.tgz#119e2b15bbd968a1a99c03cac9c329316d7c37e9"
|
||||
integrity sha512-5iylzSJevCnzJn9UVsW8yOZ3yHjmAs4TfvH3zsbftKiFKmHG0xirGN6DK9Kk04VSWxYCZZAIafYJoNJJMAU1KA==
|
||||
|
||||
monotone-convex-hull-2d@^1.0.1:
|
||||
version "1.0.1"
|
||||
@@ -6776,6 +6958,11 @@ murmurhash-js@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51"
|
||||
integrity sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=
|
||||
|
||||
mute-stream@~0.0.4:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
||||
|
||||
nan@^2.12.1:
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
@@ -7053,6 +7240,13 @@ npmlog@^4.0.2:
|
||||
gauge "~2.7.3"
|
||||
set-blocking "~2.0.0"
|
||||
|
||||
nth-check@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
|
||||
integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
|
||||
nth-check@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
|
||||
@@ -7135,6 +7329,11 @@ object-inspect@^1.7.0, object-inspect@~1.7.0:
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
|
||||
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
|
||||
|
||||
object-inspect@^1.9.0:
|
||||
version "1.10.3"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369"
|
||||
integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==
|
||||
|
||||
object-is@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4"
|
||||
@@ -7279,7 +7478,7 @@ os-tmpdir@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
osenv@^0.1.4:
|
||||
osenv@^0.1.3, osenv@^0.1.4:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
|
||||
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
|
||||
@@ -7426,6 +7625,13 @@ parse-rect@^1.2.0:
|
||||
dependencies:
|
||||
pick-by-alias "^1.2.0"
|
||||
|
||||
parse-semver@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8"
|
||||
integrity sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=
|
||||
dependencies:
|
||||
semver "^5.1.0"
|
||||
|
||||
parse-svg-path@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb"
|
||||
@@ -7436,6 +7642,18 @@ parse-unit@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/parse-unit/-/parse-unit-1.0.1.tgz#7e1bb6d5bef3874c28e392526a2541170291eecf"
|
||||
integrity sha1-fhu21b7zh0wo45JSaiVBFwKR7s8=
|
||||
|
||||
parse5-htmlparser2-tree-adapter@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
|
||||
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
|
||||
dependencies:
|
||||
parse5 "^6.0.1"
|
||||
|
||||
parse5@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||
|
||||
parseurl@~1.3.2, parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
@@ -7534,6 +7752,11 @@ pbkdf2@^3.0.3:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
@@ -7981,6 +8204,13 @@ qs@6.7.0:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.9.1:
|
||||
version "6.10.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a"
|
||||
integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
@@ -8157,6 +8387,13 @@ react@^16.12.0:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
read@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
|
||||
integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=
|
||||
dependencies:
|
||||
mute-stream "~0.0.4"
|
||||
|
||||
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
@@ -8827,16 +9064,16 @@ selfsigned@^1.10.7:
|
||||
dependencies:
|
||||
node-forge "0.9.0"
|
||||
|
||||
semver@^5.1.0, semver@^5.4.1, semver@^5.7.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^5.3.0, semver@^5.5.0, semver@^5.6.0:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||
|
||||
semver@^5.4.1, semver@^5.7.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.0.0, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
@@ -8981,6 +9218,15 @@ shx@^0.3.2:
|
||||
minimist "^1.2.0"
|
||||
shelljs "^0.8.1"
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
|
||||
dependencies:
|
||||
call-bind "^1.0.0"
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
@@ -9681,6 +9927,13 @@ tinyqueue@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
|
||||
integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
|
||||
|
||||
tmp@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
|
||||
integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
|
||||
dependencies:
|
||||
rimraf "^3.0.0"
|
||||
|
||||
to-array-buffer@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/to-array-buffer/-/to-array-buffer-3.2.0.tgz#cb684dd691a7368c3b249c2348d75227f7d4dbb4"
|
||||
@@ -9840,6 +10093,11 @@ tslib@^1.9.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
||||
|
||||
tslib@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||
|
||||
tslib@~1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
@@ -9883,6 +10141,11 @@ tunnel-agent@^0.6.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tunnel@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||
|
||||
turntable-camera-controller@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/turntable-camera-controller/-/turntable-camera-controller-3.0.1.tgz#8dbd3fe00550191c65164cb888971049578afd99"
|
||||
@@ -9947,6 +10210,15 @@ type@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3"
|
||||
integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==
|
||||
|
||||
typed-rest-client@^1.8.4:
|
||||
version "1.8.4"
|
||||
resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.4.tgz#ba3fb788e5b9322547406392533f12d660a5ced6"
|
||||
integrity sha512-MyfKKYzk3I6/QQp6e1T50py4qg+c+9BzOEl2rBmQIpStwNUoqQ73An+Tkfy9YuV7O+o2mpVVJpe+fH//POZkbg==
|
||||
dependencies:
|
||||
qs "^6.9.1"
|
||||
tunnel "0.0.6"
|
||||
underscore "^1.12.1"
|
||||
|
||||
typed-styles@^0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9"
|
||||
@@ -9977,6 +10249,11 @@ typescript@^3.8.2:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.2.tgz#91d6868aaead7da74f493c553aeff76c0c0b1d5a"
|
||||
integrity sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||
|
||||
uglify-js@3.4.x:
|
||||
version "3.4.10"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
|
||||
@@ -9985,6 +10262,11 @@ uglify-js@3.4.x:
|
||||
commander "~2.19.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
underscore@^1.12.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
|
||||
integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||
@@ -10092,6 +10374,11 @@ urix@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
||||
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
|
||||
|
||||
url-join@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78"
|
||||
integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=
|
||||
|
||||
url-parse@^1.4.3:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
@@ -10322,6 +10609,32 @@ vm-browserify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019"
|
||||
integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==
|
||||
|
||||
vsce@^1.93.0:
|
||||
version "1.93.0"
|
||||
resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.93.0.tgz#676395f24f27f850f63240b339ed551e2e1d80db"
|
||||
integrity sha512-RgmxwybXenP6tTF0PLh97b/RRLp1RkzjAHNya3QAfv1EZVg+lfoBiAaXogpmOGjYr8OskkqP5tIa3D/Q6X9lrw==
|
||||
dependencies:
|
||||
azure-devops-node-api "^10.2.2"
|
||||
chalk "^2.4.2"
|
||||
cheerio "^1.0.0-rc.9"
|
||||
commander "^6.1.0"
|
||||
denodeify "^1.2.1"
|
||||
glob "^7.0.6"
|
||||
ignore "^5.1.8"
|
||||
leven "^3.1.0"
|
||||
lodash "^4.17.15"
|
||||
markdown-it "^10.0.0"
|
||||
mime "^1.3.4"
|
||||
osenv "^0.1.3"
|
||||
parse-semver "^1.1.1"
|
||||
read "^1.0.7"
|
||||
semver "^5.1.0"
|
||||
tmp "^0.2.1"
|
||||
typed-rest-client "^1.8.4"
|
||||
url-join "^1.1.0"
|
||||
yauzl "^2.3.1"
|
||||
yazl "^2.2.2"
|
||||
|
||||
vt-pbf@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.1.tgz#b0f627e39a10ce91d943b898ed2363d21899fb82"
|
||||
@@ -10738,6 +11051,21 @@ yargs@^15.0.2:
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^18.1.1"
|
||||
|
||||
yauzl@^2.3.1:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
yazl@^2.2.2:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
|
||||
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
|
||||