Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fb1e8127a | ||
|
|
a2a5d1b873 | ||
|
|
e7e76ccfb9 | ||
|
|
e01c09eac5 | ||
|
|
86e71ae534 | ||
|
|
e46cdef402 | ||
|
|
2af8ef9a53 | ||
|
|
aa3412e6ac | ||
|
|
6543180591 | ||
|
|
cd88168211 | ||
|
|
683f1e1f76 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -80,4 +80,9 @@ typings/
|
|||||||
.dynamodb/
|
.dynamodb/
|
||||||
|
|
||||||
dist/
|
dist/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
# Python byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|||||||
@@ -228,6 +228,16 @@ export type PlotlyVisualizationData = {
|
|||||||
text?: string | string[];
|
text?: string | string[];
|
||||||
xaxis?: string;
|
xaxis?: string;
|
||||||
yaxis?: string;
|
yaxis?: string;
|
||||||
|
cells?: {
|
||||||
|
values?: string[][];
|
||||||
|
};
|
||||||
|
header?: {
|
||||||
|
values?: string[];
|
||||||
|
};
|
||||||
|
domain?: {
|
||||||
|
x?: number[],
|
||||||
|
y?: number[],
|
||||||
|
};
|
||||||
x?: (string | number | null)[] | (string | number | null)[][];
|
x?: (string | number | null)[] | (string | number | null)[][];
|
||||||
y?: (string | number | null)[] | (string | number | null)[][];
|
y?: (string | number | null)[] | (string | number | null)[][];
|
||||||
z?: (string | number | null)[] | (string | number | null)[][];
|
z?: (string | number | null)[] | (string | number | null)[][];
|
||||||
@@ -257,7 +267,8 @@ export type PlotlyVisualizationData = {
|
|||||||
| "waterfall"
|
| "waterfall"
|
||||||
| "funnel"
|
| "funnel"
|
||||||
| "funnelarea"
|
| "funnelarea"
|
||||||
| "scattermapbox";
|
| "scattermapbox"
|
||||||
|
| "table";
|
||||||
mode?:
|
mode?:
|
||||||
| "lines"
|
| "lines"
|
||||||
| "markers"
|
| "markers"
|
||||||
|
|||||||
11
data-extraction/src/getGlobal.ts
Normal file
11
data-extraction/src/getGlobal.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export function getGlobal(): any {
|
||||||
|
if (typeof globalThis === "object") {
|
||||||
|
return globalThis;
|
||||||
|
} else if (typeof global === "object") {
|
||||||
|
return global;
|
||||||
|
} else if (typeof window === "object") {
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("No global available");
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
VisualizationData,
|
VisualizationData,
|
||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
} from "../../DataExtractionResult";
|
} from "../../DataExtractionResult";
|
||||||
|
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||||
|
|
||||||
export interface DataExtractorApi {
|
export interface DataExtractorApi {
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +32,9 @@ export interface DataExtractorApi {
|
|||||||
* @preferExisting if `true`, existing extractors with the same id are not overwritten.
|
* @preferExisting if `true`, existing extractors with the same id are not overwritten.
|
||||||
*/
|
*/
|
||||||
registerDefaultExtractors(preferExisting?: boolean): void;
|
registerDefaultExtractors(preferExisting?: boolean): void;
|
||||||
|
|
||||||
|
registerDataExtractorsSource(id: string, fn: LoadDataExtractorsFn): void;
|
||||||
|
unregisterDataExtractorsSource(id: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataResult =
|
export type DataResult =
|
||||||
@@ -70,7 +74,7 @@ export interface DataExtractorContext {
|
|||||||
*/
|
*/
|
||||||
evalFn: <TEval>(expression: string) => TEval;
|
evalFn: <TEval>(expression: string) => TEval;
|
||||||
|
|
||||||
variablesInScope: Record<string, unknown>;
|
variablesInScope: Record<string, () => unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataExtraction {
|
export interface DataExtraction {
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ import {
|
|||||||
DataExtractor,
|
DataExtractor,
|
||||||
DataExtraction,
|
DataExtraction,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
|
DataExtractorContext,
|
||||||
} from "./DataExtractorApi";
|
} from "./DataExtractorApi";
|
||||||
import { DataExtractorInfo } from "../../DataExtractionResult";
|
import { DataExtractorInfo } from "../../DataExtractionResult";
|
||||||
import { registerDefaultExtractors } from "./default-extractors";
|
import { registerDefaultExtractors } from "./default-extractors";
|
||||||
|
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||||
|
import * as helpers from "../helpers";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class DataExtractorApiImpl implements DataExtractorApi {
|
export class DataExtractorApiImpl implements DataExtractorApi {
|
||||||
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
|
public static lastContext: DataExtractorContext | undefined = undefined;
|
||||||
public static lastVariablesInScope:
|
|
||||||
| Record<string, unknown>
|
|
||||||
| undefined = undefined;
|
|
||||||
|
|
||||||
private readonly extractors = new Map<string, DataExtractor>();
|
private readonly extractors = new Map<string, DataExtractor>();
|
||||||
|
private readonly extractorSources = new Map<string, LoadDataExtractorsFn>();
|
||||||
|
|
||||||
private toJson<TData>(data: TData): JSONString<TData> {
|
private toJson<TData>(data: TData): JSONString<TData> {
|
||||||
return JSON.stringify(data) as any;
|
return JSON.stringify(data) as any;
|
||||||
@@ -38,7 +39,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
valueFn: () => unknown,
|
valueFn: () => unknown,
|
||||||
evalFn: <T>(expression: string) => T,
|
evalFn: <T>(expression: string) => T,
|
||||||
preferredDataExtractorId: string | undefined,
|
preferredDataExtractorId: string | undefined,
|
||||||
variablesInScope: Record<string, unknown>
|
variablesInScope: Record<string, () => unknown>
|
||||||
): JSONString<DataResult> {
|
): JSONString<DataResult> {
|
||||||
const extractions = new Array<DataExtraction>();
|
const extractions = new Array<DataExtraction>();
|
||||||
const extractionCollector: ExtractionCollector = {
|
const extractionCollector: ExtractionCollector = {
|
||||||
@@ -47,19 +48,27 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
DataExtractorApiImpl.lastVariablesInScope = variablesInScope;
|
const context: DataExtractorContext = {
|
||||||
DataExtractorApiImpl.lastEvalFn = evalFn;
|
evalFn,
|
||||||
|
variablesInScope,
|
||||||
|
};
|
||||||
|
|
||||||
|
DataExtractorApiImpl.lastContext = context;
|
||||||
const value = valueFn();
|
const value = valueFn();
|
||||||
|
|
||||||
for (const e of this.extractors.values()) {
|
const extractors = new Array<DataExtractor>();
|
||||||
e.getExtractions(value, extractionCollector, {
|
|
||||||
evalFn,
|
for (const fn of this.extractorSources.values()) {
|
||||||
variablesInScope,
|
fn((extractor) => {
|
||||||
});
|
extractors.push(extractor);
|
||||||
|
}, helpers);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataExtractorApiImpl.lastVariablesInScope = undefined;
|
for (const e of [...this.extractors.values(), ...extractors]) {
|
||||||
DataExtractorApiImpl.lastEvalFn = undefined;
|
e.getExtractions(value, extractionCollector, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataExtractorApiImpl.lastContext = undefined;
|
||||||
|
|
||||||
extractions.sort((a, b) => b.priority - a.priority);
|
extractions.sort((a, b) => b.priority - a.priority);
|
||||||
let usedExtraction = extractions[0];
|
let usedExtraction = extractions[0];
|
||||||
@@ -69,7 +78,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
|
|
||||||
if (preferredDataExtractorId) {
|
if (preferredDataExtractorId) {
|
||||||
const preferred = extractions.find(
|
const preferred = extractions.find(
|
||||||
e => e.id === preferredDataExtractorId
|
(e) => e.id === preferredDataExtractorId
|
||||||
);
|
);
|
||||||
if (preferred) {
|
if (preferred) {
|
||||||
usedExtraction = preferred;
|
usedExtraction = preferred;
|
||||||
@@ -99,4 +108,15 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
// TODO consider preferExisting
|
// TODO consider preferExisting
|
||||||
registerDefaultExtractors(this);
|
registerDefaultExtractors(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerDataExtractorsSource(
|
||||||
|
id: string,
|
||||||
|
fn: LoadDataExtractorsFn
|
||||||
|
): void {
|
||||||
|
this.extractorSources.set(id, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unregisterDataExtractorsSource(id: string): void {
|
||||||
|
this.extractorSources.delete(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import {
|
|
||||||
DataExtractor,
|
|
||||||
ExtractionCollector,
|
|
||||||
DataExtractorContext,
|
|
||||||
LoadDataExtractorsFn,
|
|
||||||
} from "..";
|
|
||||||
import * as helpers from "../../helpers";
|
|
||||||
|
|
||||||
export class InjectedExtractor implements DataExtractor {
|
|
||||||
readonly id = "injected-extractor";
|
|
||||||
|
|
||||||
getExtractions(
|
|
||||||
data: unknown,
|
|
||||||
extractionCollector: ExtractionCollector,
|
|
||||||
context: DataExtractorContext
|
|
||||||
): void {
|
|
||||||
const key = "@hediet/debug-visualizer/injectedScripts";
|
|
||||||
const injectedExtractorsFns = Object.values(
|
|
||||||
(global as any)[key]
|
|
||||||
) as LoadDataExtractorsFn[];
|
|
||||||
|
|
||||||
const extractors = new Array<DataExtractor>();
|
|
||||||
|
|
||||||
for (const fn of injectedExtractorsFns) {
|
|
||||||
fn(extractor => {
|
|
||||||
extractors.push(extractor);
|
|
||||||
}, helpers);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const extractor of extractors) {
|
|
||||||
extractor.getExtractions(data, extractionCollector, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import { getDataExtractorApi } from "../injection";
|
|||||||
import { GridExtractor } from "./GridExtractor";
|
import { GridExtractor } from "./GridExtractor";
|
||||||
import { TableDataExtractor } from "./TableExtractor";
|
import { TableDataExtractor } from "./TableExtractor";
|
||||||
import { StringDiffExtractor } from "./StringDiffExtractor";
|
import { StringDiffExtractor } from "./StringDiffExtractor";
|
||||||
import { InjectedExtractor } from "./InjectedExtractor";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default data extractors should be registered by VS Code automatically.
|
* The default data extractors should be registered by VS Code automatically.
|
||||||
@@ -28,7 +27,6 @@ export function registerDefaultExtractors(
|
|||||||
new GridExtractor(),
|
new GridExtractor(),
|
||||||
new TableDataExtractor(),
|
new TableDataExtractor(),
|
||||||
new StringDiffExtractor(),
|
new StringDiffExtractor(),
|
||||||
new InjectedExtractor(),
|
|
||||||
]) {
|
]) {
|
||||||
api.registerExtractor(item);
|
api.registerExtractor(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { DataExtractorApi } from "./DataExtractorApi";
|
|||||||
import { DataExtractorApiImpl } from "./DataExtractorApiImpl";
|
import { DataExtractorApiImpl } from "./DataExtractorApiImpl";
|
||||||
import * as helpers from "../helpers";
|
import * as helpers from "../helpers";
|
||||||
import * as globalHelpers from "../global-helpers";
|
import * as globalHelpers from "../global-helpers";
|
||||||
|
import { getGlobal } from "../../getGlobal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns standalone JS code representing an expression that initializes the data extraction API.
|
* Returns standalone JS code representing an expression that initializes the data extraction API.
|
||||||
@@ -28,16 +29,11 @@ export function getExpressionForDataExtractorApi(): string {
|
|||||||
return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`;
|
return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExpressionToDetectDataExtractorApiPresence(): string {
|
const apiKey = "@hediet/data-extractor-api/v3";
|
||||||
return `((${selfContainedIsDataExtractorApiInitialized.toString()})())`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = "@hediet/data-extractor-api/v2";
|
|
||||||
|
|
||||||
export function getDataExtractorApi(): DataExtractorApi {
|
export function getDataExtractorApi(): DataExtractorApi {
|
||||||
installHelpers();
|
installHelpers();
|
||||||
const globalObj =
|
const globalObj = getGlobal();
|
||||||
typeof window === "object" ? (window as any) : (global as any);
|
|
||||||
if (!globalObj[apiKey]) {
|
if (!globalObj[apiKey]) {
|
||||||
globalObj[apiKey] = new DataExtractorApiImpl();
|
globalObj[apiKey] = new DataExtractorApiImpl();
|
||||||
}
|
}
|
||||||
@@ -45,33 +41,31 @@ export function getDataExtractorApi(): DataExtractorApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This code is used to detect if the API has not been initialized yet.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function selfContainedIsDataExtractorApiInitialized(): boolean {
|
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
|
||||||
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];
|
|
||||||
return api !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
||||||
const globalObj =
|
const globalObj =
|
||||||
typeof window === "object" ? (window as any) : (global as any);
|
typeof window === "object" ? (window as any) : (global as any);
|
||||||
const key: typeof apiKey = "@hediet/data-extractor-api/v2";
|
const key: typeof apiKey = "@hediet/data-extractor-api/v3";
|
||||||
let api: DataExtractorApi | undefined = globalObj[key];
|
let api: DataExtractorApi | undefined = globalObj[key];
|
||||||
if (!api) {
|
if (!api) {
|
||||||
throw new Error(`Data Extractor API has not been initialized.`);
|
const code: typeof ApiHasNotBeenInitializedCode =
|
||||||
}
|
"EgH0cybXij1jYUozyakO";
|
||||||
return api;
|
throw new Error(
|
||||||
}
|
`Data Extractor API has not been initialized. Code: ${code}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
export function installHelpers(): void {
|
export function installHelpers(): void {
|
||||||
const globalObj =
|
const globalObj = getGlobal();
|
||||||
typeof window === "object" ? (window as any) : (global as any);
|
|
||||||
// `hediet` as prefix to avoid name collision (I own `hediet.de`).
|
// `hediet` as prefix to avoid name collision (I own `hediet.de`).
|
||||||
globalObj["hedietDbgVis"] = { ...helpers, ...globalHelpers };
|
globalObj["hedietDbgVis"] = { ...helpers, ...globalHelpers };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export function cache<T>(
|
|||||||
let resultFn: () => any;
|
let resultFn: () => any;
|
||||||
let key: string;
|
let key: string;
|
||||||
if (typeof expression === "string") {
|
if (typeof expression === "string") {
|
||||||
const evalFn = DataExtractorApiImpl.lastEvalFn!;
|
const context = DataExtractorApiImpl.lastContext!;
|
||||||
resultFn = () => evalFn(expression);
|
resultFn = () => context.evalFn(expression);
|
||||||
key = JSON.stringify({ expr: expression, id });
|
key = JSON.stringify({ expr: expression, id });
|
||||||
} else {
|
} else {
|
||||||
resultFn = () => expression();
|
resultFn = () => expression();
|
||||||
|
|||||||
@@ -2,12 +2,22 @@ import { DataExtractorApiImpl } from "../api";
|
|||||||
|
|
||||||
export function find(predicate: (obj: unknown) => boolean): unknown {
|
export function find(predicate: (obj: unknown) => boolean): unknown {
|
||||||
const processed = new Set();
|
const processed = new Set();
|
||||||
if (!DataExtractorApiImpl.lastVariablesInScope) {
|
if (!DataExtractorApiImpl.lastContext) {
|
||||||
throw new Error("No variables in scope!");
|
throw new Error("No data extractor context!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = Object.values(DataExtractorApiImpl.lastVariablesInScope);
|
const values = Object.values(
|
||||||
const queue = [...values];
|
DataExtractorApiImpl.lastContext.variablesInScope
|
||||||
|
);
|
||||||
|
const queue = [
|
||||||
|
...values.map((v) => {
|
||||||
|
try {
|
||||||
|
return v();
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
let i = 10000;
|
let i = 10000;
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ export function tryEval(
|
|||||||
obj: Record<string, string> | string[] | string
|
obj: Record<string, string> | string[] | string
|
||||||
): Record<string, unknown> | unknown {
|
): Record<string, unknown> | unknown {
|
||||||
const result: Record<string, unknown> = {};
|
const result: Record<string, unknown> = {};
|
||||||
const evalFn = DataExtractorApiImpl.lastEvalFn!;
|
const context = DataExtractorApiImpl.lastContext!;
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
for (const val of obj) {
|
for (const val of obj) {
|
||||||
try {
|
try {
|
||||||
result[val] = evalFn(val);
|
result[val] = context.evalFn(val);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const [key, val] of Object.entries(obj)) {
|
for (const [key, val] of Object.entries(obj)) {
|
||||||
try {
|
try {
|
||||||
result[key] = evalFn(val);
|
result[key] = context.evalFn(val);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
demos/python/Person.py
Normal file
7
demos/python/Person.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class Person:
|
||||||
|
def __init__(self, name, parents=None) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.parents = [] if parents is None else parents
|
||||||
|
|
||||||
|
def addParent(self, parent: "Person"):
|
||||||
|
self.parents.append(parent)
|
||||||
44
demos/python/debugvisualizer.py
Normal file
44
demos/python/debugvisualizer.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from Person import Person
|
||||||
|
import json
|
||||||
|
from vscodedebugvisualizer import globalVisualizationFactory
|
||||||
|
|
||||||
|
|
||||||
|
class PersonVisualizer:
|
||||||
|
def checkType(self, t):
|
||||||
|
return isinstance(t, Person)
|
||||||
|
|
||||||
|
def visualizePerson(self, person: Person, nodes=[], edges=[]):
|
||||||
|
if person.name in [n["id"] for n in nodes]:
|
||||||
|
return nodes, edges
|
||||||
|
|
||||||
|
nodes.append(
|
||||||
|
{
|
||||||
|
"id": person.name,
|
||||||
|
"label": person.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for p in person.parents:
|
||||||
|
nodes, edges = self.visualizePerson(p, nodes, edges)
|
||||||
|
edges.append(
|
||||||
|
{
|
||||||
|
"from": p.name,
|
||||||
|
"to": person.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return nodes, edges
|
||||||
|
|
||||||
|
def visualize(self, person: Person):
|
||||||
|
jsonDict = {
|
||||||
|
"kind": {"graph": True},
|
||||||
|
"nodes": [],
|
||||||
|
"edges": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
self.visualizePerson(person, jsonDict["nodes"], jsonDict["edges"])
|
||||||
|
|
||||||
|
return json.dumps(jsonDict)
|
||||||
|
|
||||||
|
|
||||||
|
globalVisualizationFactory.addVisualizer(PersonVisualizer())
|
||||||
123
demos/python/demo.py
Normal file
123
demos/python/demo.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
# put "x" in the Debug Visualizer and use step by step
|
||||||
|
# debugging to see the diffrent types of data visualization
|
||||||
|
|
||||||
|
|
||||||
|
x = 5
|
||||||
|
x = "asdf"
|
||||||
|
x = 5.5
|
||||||
|
x = [1, 2, 3, 4, 5, 6, "a", "b"]
|
||||||
|
x = ["b", "a", "c", "d", "e"]
|
||||||
|
x.sort()
|
||||||
|
x = {
|
||||||
|
"asdf1": "dasdf",
|
||||||
|
"asdf2": "dasdf",
|
||||||
|
"asdf3": {"foo": "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
x = [1, 2, 3, 4, 5, 6]
|
||||||
|
# histogram
|
||||||
|
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||||
|
x = x.reshape(2, -1)
|
||||||
|
|
||||||
|
|
||||||
|
# one dimension
|
||||||
|
x = np.arange(100)
|
||||||
|
x = np.linspace(-np.pi, np.pi, 100)
|
||||||
|
x = np.sin(x)
|
||||||
|
|
||||||
|
# 2 dimension
|
||||||
|
x = x.reshape(5, 20)
|
||||||
|
# 2 dimension transpose
|
||||||
|
x = x.transpose()
|
||||||
|
x = x.transpose()
|
||||||
|
|
||||||
|
# 3 dimensions
|
||||||
|
x = x.reshape(2, 5, 10)
|
||||||
|
|
||||||
|
# 4 dimensions
|
||||||
|
x = x.reshape(2, 5, 2, 5)
|
||||||
|
|
||||||
|
# big number
|
||||||
|
x = np.empty(2 ** 24)
|
||||||
|
x[0:1000000] = 1
|
||||||
|
x[1000000:10000000] = 5
|
||||||
|
|
||||||
|
# pyTorch
|
||||||
|
try:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||||
|
x = x.reshape(2, -1)
|
||||||
|
x = torch.Tensor(x)
|
||||||
|
pass
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# tensorflow
|
||||||
|
|
||||||
|
try:
|
||||||
|
import tensorflow as tf
|
||||||
|
|
||||||
|
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||||
|
x = x.reshape(2, -1)
|
||||||
|
x = tf.convert_to_tensor(x)
|
||||||
|
pass
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# pandas
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pandas as pd
|
||||||
|
import plotly.express as px
|
||||||
|
|
||||||
|
x = px.data.gapminder().query("year == 2007")
|
||||||
|
pass
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# custom visualizer defined in ./debugvisualizer.py (this file is included automatically)
|
||||||
|
|
||||||
|
from Person import Person
|
||||||
|
|
||||||
|
x = Person("Aria")
|
||||||
|
parent1 = Person("Eduart")
|
||||||
|
parent2 = Person("Catelyn")
|
||||||
|
x.addParent(parent1)
|
||||||
|
x.addParent(parent2)
|
||||||
|
parent1.addParent(Person("Benjen"))
|
||||||
|
|
||||||
|
# direct debug-visualizer json as dict with property "kind"
|
||||||
|
|
||||||
|
x = {
|
||||||
|
"kind": {"dotGraph": True},
|
||||||
|
"text": '\ndigraph G {\n subgraph cluster_0 {\n style=filled;\n color=lightgrey;\n node [style=filled,color=white];\n a0 -> a1 -> a2 -> a3;\n label = "process #1";\n }\n \n subgraph cluster_1 {\n node [style=filled];\n b0 -> b1 -> b2 -> b3;\n label = "process #2";\n color=blue\n }\n start -> a0;\n start -> b0;\n a1 -> b3;\n b2 -> a3;\n a3 -> a0;\n a3 -> end;\n b3 -> end;\n \n start [shape=Mdiamond];\n end [shape=Msquare];\n}\n',
|
||||||
|
}
|
||||||
|
|
||||||
|
x = {
|
||||||
|
"kind": {"plotly": True},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"mode": "lines",
|
||||||
|
"type": "scatter",
|
||||||
|
"x": ["A", "B", "C"],
|
||||||
|
"xaxis": "x",
|
||||||
|
"y": [4488916, 3918072, 3892124],
|
||||||
|
"yaxis": "y",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cells": {"values": [["A", "B", "C"], [341319, 281489, 294786], [4488916, 3918072, 3892124]]},
|
||||||
|
"domain": {"x": [0.0, 1.0], "y": [0.0, 0.60]},
|
||||||
|
"header": {"align": "left", "font": {"size": 10}, "values": ["Date", "Number", "Output"]},
|
||||||
|
"type": "table",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"layout": {"xaxis": {"anchor": "y", "domain": [0.0, 1.0]}, "yaxis": {"anchor": "x", "domain": [0.75, 1.0]}},
|
||||||
|
}
|
||||||
|
pass
|
||||||
@@ -23,10 +23,9 @@ for i in range(2,100):
|
|||||||
# connects the node to a random edge
|
# connects the node to a random edge
|
||||||
targetId = str(randint(1, i - 1))
|
targetId = str(randint(1, i - 1))
|
||||||
graph["edges"].append({"from": id, "to": targetId})
|
graph["edges"].append({"from": id, "to": targetId})
|
||||||
json_graph = dumps(graph)
|
|
||||||
print("i is " + str(i))
|
print("i is " + str(i))
|
||||||
# try setting a breakpoint right above
|
# 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
|
# when you step through the code each time you hit the breakpoint
|
||||||
# the graph should automatically refresh!
|
# the graph should automatically refresh!
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Python demo for sorting using VS Code Debug Visualizer."""
|
"""Python demo for sorting using VS Code Debug Visualizer."""
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def serialize(arr):
|
def serialize(arr):
|
||||||
@@ -14,7 +13,7 @@ def serialize(arr):
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
return json.dumps(formatted)
|
return formatted
|
||||||
|
|
||||||
|
|
||||||
arr = [6, 9, 3, 12, 1, 11, 5, 13, 8, 14, 2, 4, 10, 0, 7]
|
arr = [6, 9, 3, 12, 1, 11, 5, 13, 8, 14, 2, 4, 10, 0, 7]
|
||||||
|
|||||||
BIN
docs/custom-scripts.gif
Normal file
BIN
docs/custom-scripts.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
@@ -1,5 +1,9 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
- [Improves support for python](https://github.com/hediet/vscode-debug-visualizer/pull/122)
|
||||||
|
|
||||||
## 2.2.0
|
## 2.2.0
|
||||||
|
|
||||||
- Adds setting `debugVisualizer.js.customScriptPaths` to inject custom scripts when debugging JavaScript. Such scripts are reloaded automatically when changed.
|
- Adds setting `debugVisualizer.js.customScriptPaths` to inject custom scripts when debugging JavaScript. Such scripts are reloaded automatically when changed.
|
||||||
|
|||||||
@@ -112,68 +112,71 @@ Also, a global object of name `hedietDbgVis` with helper functions is injected.
|
|||||||
|
|
||||||
This extension provides these configuration options:
|
This extension provides these configuration options:
|
||||||
|
|
||||||
- `debugVisualizer.debugAdapterConfigurations`
|
### **debugVisualizer.debugAdapterConfigurations**
|
||||||
|
|
||||||
Allows to set expression templates for specific debug adapter types.
|
Allows to set expression templates for specific debug adapter types.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"debugVisualizer.debugAdapterConfigurations": {
|
"debugVisualizer.debugAdapterConfigurations": {
|
||||||
"lldb": {
|
"lldb": {
|
||||||
"expressionTemplate": "script to_json(\"${expr}\")",
|
"expressionTemplate": "script to_json(\"${expr}\")",
|
||||||
"context": "repl"
|
"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`
|
### **debugVisualizer.js.customScriptPaths**
|
||||||
|
|
||||||
Specifies a list of JavaScript files that are injected into the debugee when debugging JavaScript.
|
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`.
|
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}`.
|
Paths must be absolute and can use the variable `${workspaceFolder}`.
|
||||||
Scripts are automatically reloaded when they are changed.
|
Scripts are automatically reloaded when they are changed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// @ts-check
|
// @ts-check
|
||||||
/**
|
/**
|
||||||
* @type {import("@hediet/debug-visualizer-data-extraction").LoadDataExtractorsFn}
|
* @type {import("@hediet/debug-visualizer-data-extraction").LoadDataExtractorsFn}
|
||||||
*/
|
*/
|
||||||
module.exports = (register, helpers) => {
|
module.exports = (register, helpers) => {
|
||||||
register({
|
register({
|
||||||
id: "map",
|
id: "map",
|
||||||
getExtractions(data, collector, context) {
|
getExtractions(data, collector, context) {
|
||||||
if (!(data instanceof Map)) {
|
if (!(data instanceof Map)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collector.addExtraction({
|
collector.addExtraction({
|
||||||
priority: 1000,
|
priority: 1000,
|
||||||
id: "map",
|
id: "map",
|
||||||
name: "Map",
|
name: "Map",
|
||||||
extractData() {
|
extractData() {
|
||||||
return helpers.asData({
|
return helpers.asData({
|
||||||
kind: { table: true },
|
kind: { table: true },
|
||||||
rows: [...data].map(([k, v]) => ({
|
rows: [...data].map(([k, v]) => ({
|
||||||
key: k,
|
key: k,
|
||||||
value: v,
|
value: v,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Use the `helpers` object to access some magic functions.
|
||||||
|
`helpers.find(val => ...)` can be used to find an object that is reachable by any variable in scope.
|
||||||
|
|
||||||
# See Also
|
# See Also
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"displayName": "Debug Visualizer",
|
"displayName": "Debug Visualizer",
|
||||||
"description": "A visual watch window that lets you visualize your data structures while debugging.",
|
"description": "A visual watch window that lets you visualize your data structures while debugging.",
|
||||||
"icon": "docs/logo.drawio.png",
|
"icon": "docs/logo.drawio.png",
|
||||||
"version": "2.2.1",
|
"version": "2.3.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.46.0"
|
"vscode": "^1.46.0"
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
getExpressionForDataExtractorApi,
|
|
||||||
DataResult,
|
|
||||||
getExpressionToInitializeDataExtractorApi,
|
|
||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
getExpressionToDetectDataExtractorApiPresence,
|
DataResult,
|
||||||
|
getExpressionForDataExtractorApi,
|
||||||
|
getExpressionToInitializeDataExtractorApi,
|
||||||
|
ApiHasNotBeenInitializedCode,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||||
import {
|
import { existsSync, readFileSync, watch } from "fs";
|
||||||
DebugSessionVisualizationSupport,
|
import { observable, reaction } from "mobx";
|
||||||
VisualizationBackend,
|
import { window, workspace } from "vscode";
|
||||||
GetVisualizationDataArgs,
|
|
||||||
VisualizationBackendBase,
|
|
||||||
} from "./VisualizationBackend";
|
|
||||||
import { FormattedMessage } from "../webviewContract";
|
|
||||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
|
||||||
import { Config } from "../Config";
|
import { Config } from "../Config";
|
||||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||||
import { readFileSync, existsSync } from "fs";
|
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||||
import { reaction, observable } from "mobx";
|
import { FormattedMessage } from "../webviewContract";
|
||||||
import {
|
import {
|
||||||
workspace,
|
DebugSessionVisualizationSupport,
|
||||||
FileSystemWatcher,
|
GetVisualizationDataArgs,
|
||||||
CancellationTokenSource,
|
VisualizationBackend,
|
||||||
CancellationToken,
|
VisualizationBackendBase,
|
||||||
window,
|
} from "./VisualizationBackend";
|
||||||
} from "vscode";
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
import { ResettableTimeout } from "@hediet/std/timer";
|
|
||||||
|
|
||||||
registerUpdateReconciler(module);
|
registerUpdateReconciler(module);
|
||||||
|
|
||||||
@@ -64,8 +58,7 @@ export class JsEvaluationEngine implements DebugSessionVisualizationSupport {
|
|||||||
|
|
||||||
class JsVisualizationBackend extends VisualizationBackendBase {
|
class JsVisualizationBackend extends VisualizationBackendBase {
|
||||||
public readonly expressionLanguageId = "javascript";
|
public readonly expressionLanguageId = "javascript";
|
||||||
private apiInitialized = false;
|
private initializePromise: Promise<void> | undefined = undefined;
|
||||||
private initializedPromise: Promise<void> | undefined = undefined;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
debugSession: DebugSessionProxy,
|
debugSession: DebugSessionProxy,
|
||||||
@@ -82,16 +75,41 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
return "repl";
|
return "repl";
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getVisualizationData({
|
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,
|
expression,
|
||||||
preferredExtractorId,
|
preferredExtractorId,
|
||||||
}: GetVisualizationDataArgs): Promise<
|
}: GetVisualizationDataArgs): Promise<
|
||||||
| { kind: "data"; result: DataExtractionResult }
|
| { kind: "data"; result: DataExtractionResult }
|
||||||
| { kind: "error"; message: FormattedMessage }
|
| { kind: "error"; message: FormattedMessage }
|
||||||
|
| { kind: "not-initialized" }
|
||||||
> {
|
> {
|
||||||
try {
|
try {
|
||||||
await this.ensureRuntimeIsInjected();
|
|
||||||
|
|
||||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||||
this.debugSession
|
this.debugSession
|
||||||
);
|
);
|
||||||
@@ -101,8 +119,8 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
const scopes = await this.debugSession.getScopes({ frameId });
|
const scopes = await this.debugSession.getScopes({ frameId });
|
||||||
const scopeVariables = await Promise.all(
|
const scopeVariables = await Promise.all(
|
||||||
scopes
|
scopes
|
||||||
.filter(s => !s.expensive)
|
.filter((s) => !s.expensive && s.name !== "Global")
|
||||||
.map(s =>
|
.map((s) =>
|
||||||
this.debugSession.getVariables({
|
this.debugSession.getVariables({
|
||||||
variablesReference: s.variablesReference,
|
variablesReference: s.variablesReference,
|
||||||
})
|
})
|
||||||
@@ -111,13 +129,13 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
for (const variables of scopeVariables) {
|
for (const variables of scopeVariables) {
|
||||||
variableNames.push(
|
variableNames.push(
|
||||||
...variables
|
...variables
|
||||||
.filter(v => v.value !== "undefined")
|
.filter((v) => v.value !== "undefined")
|
||||||
.map(v => v.name)
|
.map((v) => v.name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variableNames.length > 100) {
|
if (variableNames.length > 50) {
|
||||||
variableNames.length = 100;
|
variableNames.length = 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +147,7 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
e => (${expression}),
|
e => (${expression}),
|
||||||
expr => eval(expr),
|
expr => eval(expr),
|
||||||
${preferredExtractorExpr},
|
${preferredExtractorExpr},
|
||||||
{${variableNames.map(n => `${n}: ${n}`).join(",")}},
|
{${variableNames.map((n) => `${n}: () => ${n}`).join(",")}},
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const wrappedExpr = `
|
const wrappedExpr = `
|
||||||
@@ -171,6 +189,15 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
throw new Error("Invalid Data");
|
throw new Error("Invalid Data");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
typeof error.message === "string" &&
|
||||||
|
error.message.indexOf(ApiHasNotBeenInitializedCode) !== -1
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
kind: "not-initialized",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
message: error.message,
|
message: error.message,
|
||||||
@@ -178,144 +205,148 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureRuntimeIsInjected(): Promise<void> | undefined {
|
private initializeApiSafe(): Promise<void> {
|
||||||
if (this.apiInitialized) {
|
if (!this.initializePromise) {
|
||||||
return;
|
this.initializePromise = this.initializeApi().finally(
|
||||||
}
|
() => (this.initializePromise = undefined)
|
||||||
|
);
|
||||||
if (this.initializedPromise) {
|
|
||||||
return this.initializedPromise;
|
|
||||||
} else {
|
|
||||||
this.initializedPromise = this._ensureRuntimeIsInjected();
|
|
||||||
}
|
}
|
||||||
|
return this.initializePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _ensureRuntimeIsInjected(): Promise<void> {
|
private async initializeApi(): Promise<void> {
|
||||||
const body = getExpressionToDetectDataExtractorApiPresence();
|
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten.
|
||||||
|
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`;
|
||||||
|
|
||||||
const reply = await this.debugSession.evaluate({
|
await this.debugSession.evaluate({
|
||||||
expression: body,
|
expression,
|
||||||
frameId: undefined,
|
frameId: undefined,
|
||||||
context: this.getContext(),
|
context: this.getContext(),
|
||||||
});
|
});
|
||||||
const isApiInitialized = reply.result === "true";
|
|
||||||
|
|
||||||
if (!isApiInitialized) {
|
|
||||||
await this.initializeApi();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.initializeCustomScripts();
|
await this.initializeCustomScripts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initializeApi(): Promise<boolean> {
|
private customScripts: undefined | CustomScripts;
|
||||||
try {
|
|
||||||
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten.
|
|
||||||
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`;
|
|
||||||
|
|
||||||
await this.debugSession.evaluate({
|
private async initializeCustomScripts(): Promise<void> {
|
||||||
expression,
|
this.dispose.untrack(this.customScripts);
|
||||||
frameId: undefined,
|
this.customScripts?.dispose();
|
||||||
context: this.getContext(),
|
this.customScripts = this.dispose.track(new CustomScripts(this.debugSession, this.config, () => this.onChangeEmitter.emit()));
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomScripts {
|
||||||
|
public readonly dispose = Disposable.fn();
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
private filesVersionId: number = 0;
|
private filesVersionId: number = 0;
|
||||||
|
|
||||||
private readonly fileChangeDebouncedScheduler = new DebouncedScheduler(100);
|
private readonly fileChangeDebouncedScheduler = new DebouncedScheduler(100);
|
||||||
|
|
||||||
private async initializeCustomScripts(): Promise<void> {
|
constructor(private readonly debugSession: DebugSessionProxy, private readonly config: Config, private readonly changeHandler: () => void) {
|
||||||
await this.debugSession.evaluate({
|
this.init();
|
||||||
expression: `global["${injectedScriptsKey}"] = {};`,
|
}
|
||||||
frameId: undefined,
|
|
||||||
context: "repl",
|
|
||||||
});
|
|
||||||
|
|
||||||
|
private init(): void {
|
||||||
const watchers = new Map<string, WatchedFile>();
|
const watchers = new Map<string, WatchedFile>();
|
||||||
this.dispose.track({
|
this.dispose.track({
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
for (const w of watchers.values()) {
|
for (const w of watchers.values()) {
|
||||||
w.watcher.dispose();
|
w.dispose();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
reaction(
|
this.dispose.track({
|
||||||
() => ({
|
dispose: reaction(
|
||||||
customScriptPaths: [...this.config.customScriptPaths],
|
() => ({
|
||||||
version: this.filesVersionId,
|
customScriptPaths: [...this.config.customScriptPaths],
|
||||||
}),
|
version: this.filesVersionId,
|
||||||
async ({ customScriptPaths }) => {
|
}),
|
||||||
const promises = new Array<Promise<[Result, WatchedFile]>>();
|
async ({ customScriptPaths }) => {
|
||||||
|
const promises = new Array<
|
||||||
|
Promise<[Result, WatchedFile]>
|
||||||
|
>();
|
||||||
|
|
||||||
const activePaths = new Set<string>();
|
const activePaths = new Set<string>();
|
||||||
for (const fsPath of customScriptPaths) {
|
for (const fsPath of customScriptPaths) {
|
||||||
activePaths.add(fsPath);
|
activePaths.add(fsPath);
|
||||||
let entry = watchers.get(fsPath);
|
let entry = watchers.get(fsPath);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
entry = new WatchedFile(fsPath, this.debugSession);
|
entry = new WatchedFile(
|
||||||
watchers.set(fsPath, entry);
|
fsPath,
|
||||||
entry.watcher.onDidChange(() => {
|
this.debugSession,
|
||||||
this.fileChangeDebouncedScheduler.run(() => {
|
() => {
|
||||||
this.filesVersionId++;
|
this.fileChangeDebouncedScheduler.run(
|
||||||
});
|
() => {
|
||||||
});
|
this.filesVersionId++;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
watchers.set(fsPath, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(entry.update().then((e) => [e, entry!]));
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(entry.update().then(e => [e, entry!]));
|
for (const [fsPath, entry] of watchers) {
|
||||||
}
|
if (!activePaths.has(fsPath)) {
|
||||||
|
promises.push(
|
||||||
for (const [fsPath, entry] of watchers) {
|
entry.remove().then((e) => [e, entry])
|
||||||
if (!activePaths.has(fsPath)) {
|
);
|
||||||
promises.push(entry.remove().then(e => [e, entry]));
|
entry.dispose();
|
||||||
entry.watcher.dispose();
|
watchers.delete(fsPath);
|
||||||
watchers.delete(fsPath);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const results = await Promise.all(promises);
|
const results = await Promise.all(promises);
|
||||||
const errors = results.filter(([r]) => r.error);
|
const errors = results.filter(([r]) => r.error);
|
||||||
for (const [e, watcher] of errors) {
|
for (const [e, watcher] of errors) {
|
||||||
window.showErrorMessage(
|
window.showErrorMessage(
|
||||||
`Error while evaluating "${watcher.fsPath}": ${
|
`Error while evaluating "${watcher.fsPath}": ${
|
||||||
e.error!.message
|
e.error!.message
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promises.length > 0) {
|
if (promises.length > 0) {
|
||||||
this.onChangeEmitter.emit();
|
this.changeHandler();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fireImmediately: true,
|
||||||
}
|
}
|
||||||
},
|
),
|
||||||
{
|
});
|
||||||
fireImmediately: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const injectedScriptsKey = `@hediet/debug-visualizer/injectedScripts`;
|
|
||||||
|
|
||||||
interface Result {
|
interface Result {
|
||||||
error?: { message: string };
|
error?: { message: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
class WatchedFile {
|
class WatchedFile {
|
||||||
public static nextId = 0;
|
public static nextId = 0;
|
||||||
public readonly watcher = workspace.createFileSystemWatcher(this.fsPath);
|
private readonly watcher = watch(
|
||||||
|
this.fsPath,
|
||||||
|
{ encoding: "utf-8" },
|
||||||
|
this.changeHandler
|
||||||
|
);
|
||||||
public readonly id = WatchedFile.nextId++;
|
public readonly id = WatchedFile.nextId++;
|
||||||
public lastContent: string | undefined = undefined;
|
public lastContent: string | undefined = undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly fsPath: string,
|
public readonly fsPath: string,
|
||||||
private readonly debugSession: DebugSessionProxy
|
private readonly debugSession: DebugSessionProxy,
|
||||||
|
private readonly changeHandler: () => void
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
this.watcher.close();
|
||||||
|
}
|
||||||
|
|
||||||
public async update(): Promise<Result> {
|
public async update(): Promise<Result> {
|
||||||
if (!existsSync(this.fsPath)) {
|
if (!existsSync(this.fsPath)) {
|
||||||
return {
|
return {
|
||||||
@@ -337,7 +368,9 @@ class WatchedFile {
|
|||||||
load => {
|
load => {
|
||||||
var module = {};
|
var module = {};
|
||||||
load(module);
|
load(module);
|
||||||
global["${injectedScriptsKey}"]["${this.id}"] = module.exports;
|
${getExpressionForDataExtractorApi()}.registerDataExtractorsSource("${
|
||||||
|
this.id
|
||||||
|
}", module.exports);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
@@ -357,7 +390,9 @@ class WatchedFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async remove(): Promise<Result> {
|
public async remove(): Promise<Result> {
|
||||||
const expression = `(() => { delete global["${injectedScriptsKey}"]["${this.id}"]; })()`;
|
const expression = `${getExpressionForDataExtractorApi()}.unregisterDataExtractorsSource("${
|
||||||
|
this.id
|
||||||
|
}")`;
|
||||||
try {
|
try {
|
||||||
await this.debugSession.evaluate({
|
await this.debugSession.evaluate({
|
||||||
expression,
|
expression,
|
||||||
|
|||||||
149
extension/src/VisualizationBackend/PyVisualizationSupport.ts
Normal file
149
extension/src/VisualizationBackend/PyVisualizationSupport.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import {
|
||||||
|
DataExtractionResult,
|
||||||
|
DataExtractorId,
|
||||||
|
GraphNode,
|
||||||
|
GraphVisualizationData,
|
||||||
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
|
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||||
|
import {
|
||||||
|
DebugSessionVisualizationSupport,
|
||||||
|
VisualizationBackend,
|
||||||
|
GetVisualizationDataArgs,
|
||||||
|
VisualizationBackendBase,
|
||||||
|
} from "./VisualizationBackend";
|
||||||
|
import { Config } from "../Config";
|
||||||
|
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||||
|
import { FormattedMessage } from "../webviewContract";
|
||||||
|
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||||
|
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||||
|
|
||||||
|
registerUpdateReconciler(module);
|
||||||
|
|
||||||
|
@hotClass(module)
|
||||||
|
export class PyEvaluationEngine
|
||||||
|
implements DebugSessionVisualizationSupport {
|
||||||
|
constructor(
|
||||||
|
private readonly debuggerView: DebuggerViewProxy,
|
||||||
|
private readonly config: Config
|
||||||
|
) { }
|
||||||
|
|
||||||
|
createBackend(
|
||||||
|
session: DebugSessionProxy
|
||||||
|
): VisualizationBackend | undefined {
|
||||||
|
|
||||||
|
const supportedDebugAdapters = [
|
||||||
|
"python",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||||
|
return new PyVisualizationBackend(
|
||||||
|
session,
|
||||||
|
this.debuggerView,
|
||||||
|
this.config
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||||
|
public readonly expressionLanguageId = "python";
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
debugSession: DebugSessionProxy,
|
||||||
|
debuggerView: DebuggerViewProxy,
|
||||||
|
private readonly config: Config
|
||||||
|
) {
|
||||||
|
super(debugSession, debuggerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getContext(): "watch" | "repl" {
|
||||||
|
// we will use "repl" as default so that results are not truncated.
|
||||||
|
return "repl";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getVisualizationData({
|
||||||
|
expression,
|
||||||
|
preferredExtractorId,
|
||||||
|
}: GetVisualizationDataArgs): Promise<
|
||||||
|
| { kind: "data"; result: DataExtractionResult }
|
||||||
|
| { kind: "error"; message: FormattedMessage }
|
||||||
|
> {
|
||||||
|
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||||
|
this.debugSession
|
||||||
|
);
|
||||||
|
|
||||||
|
const finalExpression = this.getFinalExpression({
|
||||||
|
expression,
|
||||||
|
preferredExtractorId,
|
||||||
|
});
|
||||||
|
let reply: { result: string; variablesReference: number };
|
||||||
|
try {
|
||||||
|
// inject vscodedebugvisualizer for python
|
||||||
|
await this.debugSession.evaluate({
|
||||||
|
expression: 'from vscodedebugvisualizer import visualize\ntry:\n import debugvisualizer\nexcept ImportError:\n pass',
|
||||||
|
frameId,
|
||||||
|
context: this.getContext(),
|
||||||
|
});
|
||||||
|
|
||||||
|
reply = await this.debugSession.evaluate({
|
||||||
|
expression: finalExpression,
|
||||||
|
frameId,
|
||||||
|
context: this.getContext(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = reply.result;
|
||||||
|
// remove the initial escape by the the debug session e.g. `''{"kind": {"text": true}, "text": "{"asdf1\'"}''`
|
||||||
|
result = result.replace(/\\'/g, "'");
|
||||||
|
result = result.replace(/\\\\/g, "\\");
|
||||||
|
|
||||||
|
return parseEvaluationResultFromGenericDebugAdapter(
|
||||||
|
result,
|
||||||
|
{
|
||||||
|
debugAdapterType: this.debugSession.session
|
||||||
|
.configuration.type,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
let errorTyped = error as Error;
|
||||||
|
if (errorTyped.message.includes("ModuleNotFoundError: No module named 'vscodedebugvisualizer'")) {
|
||||||
|
return {
|
||||||
|
kind: "error",
|
||||||
|
message: {
|
||||||
|
kind: "list",
|
||||||
|
items: ["Pleas make sure vscodedebugvisualizer is installed: `pip install vscodedebugvisualizer`"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind: "error",
|
||||||
|
message: {
|
||||||
|
kind: "list",
|
||||||
|
items: [
|
||||||
|
"An error occurred while evaluating the expression:",
|
||||||
|
errorTyped.message,
|
||||||
|
`Used debug adapter: ${this.debugSession.session.configuration.type}`,
|
||||||
|
{
|
||||||
|
kind: "inlineList",
|
||||||
|
items: [
|
||||||
|
"Evaluated expression is",
|
||||||
|
{ kind: "code", content: finalExpression },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getFinalExpression(args: {
|
||||||
|
expression: string;
|
||||||
|
preferredExtractorId: DataExtractorId | undefined;
|
||||||
|
}): string {
|
||||||
|
// wrap expression with visualize function
|
||||||
|
let pythonInject = "";
|
||||||
|
pythonInject += "visualize(" + args.expression + ")";
|
||||||
|
return pythonInject;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,5 +2,6 @@ export * from "./VisualizationBackend";
|
|||||||
|
|
||||||
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
|
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
|
||||||
export { JsEvaluationEngine } from "./JsVisualizationSupport";
|
export { JsEvaluationEngine } from "./JsVisualizationSupport";
|
||||||
|
export { PyEvaluationEngine } from "./PyVisualizationSupport";
|
||||||
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
|
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
|
||||||
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";
|
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { VisualizationWatchModelImpl } from "./VisualizationWatchModel";
|
|||||||
import {
|
import {
|
||||||
ComposedVisualizationSupport,
|
ComposedVisualizationSupport,
|
||||||
JsEvaluationEngine,
|
JsEvaluationEngine,
|
||||||
|
PyEvaluationEngine,
|
||||||
GenericVisualizationSupport,
|
GenericVisualizationSupport,
|
||||||
ConfigurableVisualizationSupport,
|
ConfigurableVisualizationSupport,
|
||||||
} from "./VisualizationBackend";
|
} from "./VisualizationBackend";
|
||||||
@@ -32,7 +33,7 @@ export function activate(context: ExtensionContext) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate() {}
|
export function deactivate() { }
|
||||||
|
|
||||||
export class Extension {
|
export class Extension {
|
||||||
public readonly dispose = Disposable.fn();
|
public readonly dispose = Disposable.fn();
|
||||||
@@ -51,6 +52,7 @@ export class Extension {
|
|||||||
this.debuggerView
|
this.debuggerView
|
||||||
),
|
),
|
||||||
new JsEvaluationEngine(this.debuggerView, this.config),
|
new JsEvaluationEngine(this.debuggerView, this.config),
|
||||||
|
new PyEvaluationEngine(this.debuggerView, this.config),
|
||||||
new GenericVisualizationSupport(this.debuggerView),
|
new GenericVisualizationSupport(this.debuggerView),
|
||||||
]),
|
]),
|
||||||
this.debuggerView
|
this.debuggerView
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"mobx": "^5.15.4",
|
"mobx": "^5.15.4",
|
||||||
"mobx-react": "^6.1.8",
|
"mobx-react": "^6.1.8",
|
||||||
"monaco-editor": "^0.20.0",
|
"monaco-editor": "^0.25.2",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"react-measure": "^2.3.0",
|
"react-measure": "^2.3.0",
|
||||||
"@hediet/debug-visualizer-data-extraction": "*",
|
"@hediet/debug-visualizer-data-extraction": "*",
|
||||||
"@hediet/visualization-bundle": "^0.2.1",
|
"@hediet/visualization-bundle": "^0.2.3",
|
||||||
"@hediet/visualization-core": "^0.2.0",
|
"@hediet/visualization-core": "^0.2.0",
|
||||||
"@knuddels/mobx-logger": "^1.1.1",
|
"@knuddels/mobx-logger": "^1.1.1",
|
||||||
"less-loader": "^6.2.0"
|
"less-loader": "^6.2.0"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"file-loader": "^5.1.0",
|
"file-loader": "^5.1.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^4.0.4",
|
"fork-ts-checker-webpack-plugin": "^4.0.4",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
"monaco-editor-webpack-plugin": "^4.0.0",
|
||||||
"sass": "^1.25.0",
|
"sass": "^1.25.0",
|
||||||
"raw-loader": "^4.0.0",
|
"raw-loader": "^4.0.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import * as React from "react";
|
|||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import { App } from "./components/App";
|
import { App } from "./components/App";
|
||||||
|
import * as monaco from "monaco-editor";
|
||||||
|
|
||||||
|
(globalThis as any).monaco = monaco;
|
||||||
|
|
||||||
const elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
elem.className = "react-root";
|
elem.className = "react-root";
|
||||||
|
|||||||
38
yarn.lock
38
yarn.lock
@@ -487,10 +487,10 @@
|
|||||||
stacktracey "^1.2.112"
|
stacktracey "^1.2.112"
|
||||||
ws "^7.0.0"
|
ws "^7.0.0"
|
||||||
|
|
||||||
"@hediet/monaco-editor-react@^0.1.1":
|
"@hediet/monaco-editor-react@^0.2.0":
|
||||||
version "0.1.1"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.1.1.tgz#041f805aeb188403c7876f0f364d2a429e706031"
|
resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.2.0.tgz#4a98102baf1942f210fc19fd406b6c82da3bf583"
|
||||||
integrity sha512-0DZZfLcFNuc3Wrn+2eztnRloCBe6GV8zIrGT3LPdZlWYEigJT+Qh7ANvRHF0tCS5YPAFGQNZJcfzKbBfqRGo4w==
|
integrity sha512-EA71+iv9sV25+YgBQDJ4Ou17sOJCXQat7r5AOD5STZHcUQdSuQufI2t9ue+wJIm4048P9sQ/ruW3pYmwxF9Amw==
|
||||||
|
|
||||||
"@hediet/node-reload@^0.7.3":
|
"@hediet/node-reload@^0.7.3":
|
||||||
version "0.7.3"
|
version "0.7.3"
|
||||||
@@ -550,22 +550,22 @@
|
|||||||
"@hediet/std" "^0.6.0"
|
"@hediet/std" "^0.6.0"
|
||||||
io-ts "^1.8.6"
|
io-ts "^1.8.6"
|
||||||
|
|
||||||
"@hediet/visualization-bundle@^0.2.1":
|
"@hediet/visualization-bundle@^0.2.3":
|
||||||
version "0.2.1"
|
version "0.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.1.tgz#397a6bff0ea12018a7a1cb1c182ef8c5de87328f"
|
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.3.tgz#8323706dcaaafc973fdc57da68e6527265e5bfcc"
|
||||||
integrity sha512-Qfyn8WbWGwuE5J1z8U7l8E5VKuG3DpS2JJOtKhlW1QW/rhqBWOLd5avifxqjWxiMIfqh3NTShp9gLHpmI2Bysw==
|
integrity sha512-RpzAST/K5O9FaEaCshHEY7VeGRVlwoTLLcWPkgxzeuZlRr5CNTw8d7Lznh+RaLD1rbz/UEezq2pI3qFYnq/z4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@finos/perspective-viewer" "^0.5.2"
|
"@finos/perspective-viewer" "^0.5.2"
|
||||||
"@finos/perspective-viewer-d3fc" "^0.5.2"
|
"@finos/perspective-viewer-d3fc" "^0.5.2"
|
||||||
"@finos/perspective-viewer-datagrid" "^0.5.2"
|
"@finos/perspective-viewer-datagrid" "^0.5.2"
|
||||||
"@hediet/monaco-editor-react" "^0.1.1"
|
"@hediet/monaco-editor-react" "^0.2.0"
|
||||||
"@hediet/semantic-json" "^0.3.15"
|
"@hediet/semantic-json" "^0.3.15"
|
||||||
"@hediet/std" "^0.6.0"
|
"@hediet/std" "^0.6.0"
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
line-column "^1.0.2"
|
line-column "^1.0.2"
|
||||||
mobx "^5.15.4"
|
mobx "^5.15.4"
|
||||||
mobx-react "^6.1.8"
|
mobx-react "^6.1.8"
|
||||||
monaco-editor "^0.20.0"
|
monaco-editor "^0.25.2"
|
||||||
plotly.js "^1.54.7"
|
plotly.js "^1.54.7"
|
||||||
react "^16.12.0"
|
react "^16.12.0"
|
||||||
react-dom "^16.12.0"
|
react-dom "^16.12.0"
|
||||||
@@ -6861,17 +6861,17 @@ moment@^2.19.1:
|
|||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||||
|
|
||||||
monaco-editor-webpack-plugin@^1.9.0:
|
monaco-editor-webpack-plugin@^4.0.0:
|
||||||
version "1.9.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.9.0.tgz#5b547281b9f404057dc5d8c5722390df9ac90be6"
|
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.0.0.tgz#95be3f48f4220999b909266a9997727f0deab947"
|
||||||
integrity sha512-tOiiToc94E1sb50BgZ8q8WK/bxus77SRrwCqIpAB5er3cpX78SULbEBY4YPOB8kDolOzKRt30WIHG/D6gz69Ww==
|
integrity sha512-4BT9XDRQXraMQjxEUjR+uuubRe3RIPkvVoGw8zwWG++s7wq6TAiXaSOMdkdS9TrjCREgSnygCOlVzY6MS8RPuA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loader-utils "^1.2.3"
|
loader-utils "^2.0.0"
|
||||||
|
|
||||||
monaco-editor@^0.20.0:
|
monaco-editor@^0.25.2:
|
||||||
version "0.20.0"
|
version "0.25.2"
|
||||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
|
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.25.2.tgz#119e2b15bbd968a1a99c03cac9c329316d7c37e9"
|
||||||
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
|
integrity sha512-5iylzSJevCnzJn9UVsW8yOZ3yHjmAs4TfvH3zsbftKiFKmHG0xirGN6DK9Kk04VSWxYCZZAIafYJoNJJMAU1KA==
|
||||||
|
|
||||||
monotone-convex-hull-2d@^1.0.1:
|
monotone-convex-hull-2d@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user