58 Commits

Author SHA1 Message Date
Henning Dieterichs
9fb1e8127a v2.3.0 2022-03-09 13:48:08 +01:00
Henning Dieterichs
a2a5d1b873 Update changelog for 2.2.4 2022-03-09 13:46:05 +01:00
Henning Dieterichs
e7e76ccfb9 Update dependencies. 2022-03-09 13:45:59 +01:00
Henning Dieterichs
e01c09eac5 Merge pull request #122 from fehrlich/master
python support
2021-08-19 11:06:32 +02:00
fehrlich
86e71ae534 remove replaceA
ll
2021-08-18 13:06:08 +02:00
fehrlich
e46cdef402 minor 2021-08-18 12:52:47 +02:00
fehrlich
2af8ef9a53 some cleanup 2021-08-18 12:20:11 +02:00
fehrlich
aa3412e6ac init python support 2021-08-18 11:32:21 +02:00
Henning Dieterichs
6543180591 Version 2.2.4. 2021-06-22 10:05:03 +02:00
Henning Dieterichs
cd88168211 Fixes issues with global. 2021-06-22 09:59:50 +02:00
Henning Dieterichs
683f1e1f76 Updates readme. 2021-06-18 21:12:30 +02:00
Henning Dieterichs
f2b4f620fe Version 2.2.1. 2021-06-18 21:08:06 +02:00
Henning Dieterichs
d22e5d4111 Version 2.2.0.
* Adds support for custom injected scripts with automatic reload
* Refactors extension
2021-06-18 20:26:51 +02:00
Henning Dieterichs
6ce5d666a3 * Updates visualization library.
* Adds support for more js debug adapters.
* Minor improvements.
2021-06-12 16:02:54 +02:00
Henning Dieterichs
6da44bd722 Adds diagram. 2021-04-29 10:40:51 +02:00
Henning Dieterichs
db1e5a8086 Adds fetch demo. 2020-10-07 17:13:50 +02:00
Henning Dieterichs
28d02ca4ab Fixes marked grid. 2020-10-07 17:13:50 +02:00
Henning Dieterichs
1e65dd1990 Updates common data types. 2020-10-07 17:13:50 +02:00
Henning Dieterichs
76fa30f71a v0.13.1 2020-10-07 17:13:50 +02:00
Henning Dieterichs
3c8ccd9a53 Merge pull request #90 from DanTup/add-dart-samples
Add a Dart demo script
2020-10-03 19:59:43 +02:00
Henning Dieterichs
9c70e06ee7 Merge pull request #84 from fwcd/master
Generate object graph visualization in a fully language-agnostic way in GenericEvaluator
2020-09-29 18:52:54 +02:00
Danny Tuppeny
8f83a1d96e Add link to readme 2020-09-22 11:16:55 +01:00
Danny Tuppeny
9cd290ab57 Add a Dart demo script 2020-09-22 11:11:31 +01:00
Henning Dieterichs
061ccdca33 Update opened-issues-triage.yml
Adds repo token back again.
2020-09-19 19:52:35 +02:00
Henning Dieterichs
12099d6152 Update opened-issues-triage.yml
Removes github auth token.
2020-09-19 19:47:14 +02:00
fwcd
b47efb7176 Reduce default node limit in GenericEvaluator to 50 2020-09-03 00:37:07 +02:00
fwcd
ee9f405033 Use breadth-first-search in GenericEvaluationEngine
A BFS generates a better graph if the graph is very deep since a DFS
would get stuck in the first branch (if the limit is low).
2020-09-02 22:41:55 +02:00
fwcd
cc272acb06 Remove redundant imports in GenericEvaluationEngine 2020-09-02 22:04:48 +02:00
fwcd
4a81e9910b Apply correct labels to graph edges in generic evaluator 2020-09-02 20:36:05 +02:00
fwcd
d8840bf33b Construct graph in generic evaluator explicitly 2020-09-02 20:22:10 +02:00
fwcd
e882593cea Limit amount of scanned objects in generic evaluator 2020-09-02 19:44:13 +02:00
fwcd
7ab4e0c4b5 Improve node labelling in generic evaluator 2020-09-02 19:40:58 +02:00
fwcd
4aca50f67a Use createGraph to represent object graphs constructed from variables 2020-09-02 19:29:33 +02:00
fwcd
6ef0ecb60f Add maxRecursionDepth in GenericEvaluationEngine 2020-09-02 18:07:22 +02:00
fwcd
0ce1b02b2c Handle cyclic references in GenericEvaluationEngine 2020-09-02 17:36:17 +02:00
fwcd
abfa44b4f9 Add new object construction in GenericEvaluationEngine 2020-09-02 17:28:08 +02:00
fwcd
fa1432ffc8 Add EnhancedDebugSession.getVariables 2020-09-02 17:10:04 +02:00
fwcd
fc142926b8 Return 'variablesReference' in EnhancedDebugSession.evaluate 2020-09-02 17:05:51 +02:00
Henning Dieterichs
d68d3b4076 Merge pull request #80 from suzil/master
Add Python grid demo (insertion sort)
2020-08-31 23:47:57 +02:00
Susannah Klaneček
f8c3b73d4f Add Python grid demo (insertion sort) 2020-08-31 17:33:50 -04:00
Henning Dieterichs
a8e1a5a36c Merge pull request #75 from Phrohdoh/fix-rust-demo
Fix the Rust demo
2020-08-29 19:22:17 +02:00
Taryn Hill
d421fa1497 chore: update Rust demo to work with schema change 2020-08-29 11:19:37 -05:00
Taryn Hill
118d02e797 chore: add Visual Studio Code LLDB config for the Rust demo 2020-08-29 11:19:37 -05:00
Henning Dieterichs
c420ff7f6f Improves docs. 2020-08-24 17:06:00 +02:00
Henning Dieterichs
7aaebf0332 Selects expression enclosed in backticks when nothing is selected and shift+F1 is pressed. 2020-08-24 17:05:54 +02:00
Henning Dieterichs
2f44c2d6f8 Fixes font csp issues. 2020-08-24 17:05:31 +02:00
Henning Dieterichs
f5f6f5d56d Improves js demos. 2020-08-24 17:05:03 +02:00
Henning Dieterichs
3e64d3b05c Adds setting to js demos. 2020-08-24 17:04:55 +02:00
Henning Dieterichs
0429eac23e Adds table with type extractor. 2020-08-24 17:04:45 +02:00
Henning Dieterichs
dc9b07a4b6 v2.0.6 2020-08-23 18:38:32 +02:00
Henning Dieterichs
2b6dbf52e9 Updates version. 2020-08-23 16:54:05 +02:00
Henning Dieterichs
e4606a4ae3 Updates bundled visualizations. 2020-08-23 16:53:56 +02:00
Henning Dieterichs
508062768c v2.0.2 2020-08-23 14:41:46 +02:00
Henning Dieterichs
d7dc1f27e0 Allows to load webworkers from data/blob. 2020-08-23 14:41:38 +02:00
Henning Dieterichs
4525c226c8 Adds asData helper function. 2020-08-23 14:41:12 +02:00
Henning Dieterichs
c09dadb199 Updates readme. 2020-08-22 19:57:48 +02:00
Henning Dieterichs
c88e98cc24 Updates readme. 2020-08-22 19:54:37 +02:00
Henning Dieterichs
17be602906 Updates docs. 2020-08-22 19:47:15 +02:00
93 changed files with 4283 additions and 1066 deletions

7
.gitignore vendored
View File

@@ -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
View File

@@ -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"
},
]
}

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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"

View 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");
}

View File

@@ -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 {

View File

@@ -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);
}
}

View 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;

View File

@@ -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 })) }],
}),
});
}

View File

@@ -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]
})
})
}
}
}

View File

@@ -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 })),
});
},
});
}
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -1,3 +1,4 @@
export * from "./DataExtractorApi";
export * from "./injection";
export { DataExtractorApiImpl } from "./DataExtractorApiImpl";
export * from "./LoadDataExtractorsFn";

View File

@@ -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 };
}

View File

@@ -0,0 +1,6 @@
import { KnownVisualizationData } from "../../CommonDataTypes";
import { VisualizationData } from "../../DataExtractionResult";
export function asData(data: KnownVisualizationData): VisualizationData {
return data;
}

View File

@@ -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();

View File

@@ -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 });
}
}
}

View 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;
}

View File

@@ -3,3 +3,5 @@ export * from "./createGraphFromPointers";
export * from "./tryEval";
export * from "./markedGrid";
export * from "./cache";
export * from "./asData";
export * from "./find";

View File

@@ -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),
};
}

View File

@@ -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) {}
}
}

View 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
View 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;
}
}

View File

@@ -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>&#xD;
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>&#xD;
</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
View File

@@ -0,0 +1,8 @@
{
"editor.minimap.enabled": false,
"git.enabled": false,
"typescript.tsc.autoDetect": "off",
"debugVisualizer.js.customScriptPaths": [
"${workspaceFolder}/custom-visualizer.js",
]
}

View 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
);
}

View File

@@ -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": {

View 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;

View File

@@ -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;
}

View File

@@ -0,0 +1,7 @@
const fetch = require("node-fetch");
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((json) => {
debugger;
});

View File

@@ -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;
}

View File

@@ -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]];

View File

@@ -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);
}
}

View File

@@ -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
View 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)

View 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
View 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

View File

@@ -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!

View 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)

View File

@@ -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
View File

@@ -0,0 +1,8 @@
{
"debugVisualizer.debugAdapterConfigurations": {
"lldb": {
"expressionTemplate": "${expr}",
"context": "watch"
}
}
}

View File

@@ -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");
}

View 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="&lt;mxfile host=&quot;localhost&quot; modified=&quot;2020-05-18T21:20:25.100Z&quot; agent=&quot;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&quot; etag=&quot;wIym9edGWidWU1rSQKSs&quot; version=&quot;13.0.9&quot;&gt;&lt;diagram id=&quot;6hGFLwfOUW9BJ-s0fimq&quot; name=&quot;Page-1&quot;&gt;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/&lt;/diagram&gt;&lt;/mxfile&gt;">
<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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
docs/custom-script-map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/custom-scripts.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 MiB

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -1,2 +1 @@
node_modules
ui/node_modules
./node_modules

View File

@@ -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

View File

@@ -3,218 +3,77 @@
[![](https://img.shields.io/twitter/follow/hediet_dev.svg?style=social)](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.
![](../docs/demo.gif)
## [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.
![](../docs/doubly-linked-list-reverse-demo.gif)
```ts
interface Graph {
kind: { graph: true };
nodes: NodeGraphData[];
edges: EdgeGraphData[];
}
### Random Walk
interface NodeGraphData {
id: string;
label?: string;
color?: string;
shape?: "ellipse" | "box";
}
![](../docs/visualization-plotly-random-walk.gif)
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`.
![](../docs/ast-demo.gif)
![](../docs/visualization-graphviz.png)
![](../docs/visualization-visjs.png)
### 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.
```
![](../docs/visualization-plotly-random-walk.png)
### 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;
}
```
![](../docs/visualization-tree.png)
### 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.
![](../docs/visualization-ast.png)
### 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;
}[];
}
```
![](../docs/visualization-grid.gif)
### 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.
![](../docs/table-demo.gif)
## 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,
})),
});
},
});
},
});
};
```
![](../docs/custom-script-map.png)
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

View File

@@ -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"
}
}

View File

@@ -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]();
});
}
}

View File

@@ -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;
}
}

View File

@@ -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,
});
}
}

View File

@@ -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;
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}
}

View File

@@ -1,6 +0,0 @@
export * from "./EvaluationEngine";
export { ComposedEvaluationEngine } from "./ComposedEvaluationEngine";
export { JsEvaluationEngine } from "./JsEvaluationEngine";
export { GenericEvaluationEngine } from "./GenericEvaluationEngine";
export { ConfiguredEvaluationEngine } from "./ConfiguredEvaluationEngine";

View File

@@ -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);
}
}

View File

@@ -1,2 +0,0 @@
export * from "./EvaluationWatchServiceImpl";
export * from "./EvaluationWatchService";

View File

@@ -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;
}
}

View File

@@ -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,
});
}
}

View File

@@ -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
)) || []
);
}
}

View File

@@ -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";
}
}

View 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();
}
}

View 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;
}
}

View 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,
});
}
}

View 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";

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -0,0 +1,2 @@
export * from "./VisualizationWatchModelImpl";
export * from "./VisualizationWatchModel";

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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() }, "*");
}
}

View File

@@ -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 ({}) => {

View File

@@ -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 => {

View File

@@ -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",

View File

@@ -17,7 +17,6 @@ export class App extends React.Component {
require("../vscode-dark.scss");
}
}
console.log(this.model.theme);
}
render() {

View File

@@ -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);
}
}

View File

@@ -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";

View File

@@ -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
View File

@@ -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"