Update
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -86,3 +86,5 @@ out/
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
*.vsix
|
||||
@@ -2,5 +2,6 @@
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"useTabs": true
|
||||
"useTabs": true,
|
||||
"printWidth": 120
|
||||
}
|
||||
|
||||
14
.vscode/settings.json
vendored
14
.vscode/settings.json
vendored
@@ -7,11 +7,15 @@
|
||||
"plantuml.exportOutDir": "docs/exported",
|
||||
"typescript.updateImportsOnFileMove.enabled": "prompt",
|
||||
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
|
||||
"mochaExplorer.require": [
|
||||
"ts-node/register",
|
||||
"source-map-support/register"
|
||||
],
|
||||
"mochaExplorer.require": ["ts-node/register", "source-map-support/register"],
|
||||
"tasksStatusbar.taskLabelFilter": "dev",
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.prettierPath": "node_modules/prettier",
|
||||
"prettier.printWidth": 120,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"@types/copy-webpack-plugin": "^10.1.0",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/plotly.js": "1.44.28",
|
||||
@@ -36,10 +38,11 @@
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^15.0.0",
|
||||
"source-map-support": "^0.5.16",
|
||||
"ts-loader": "^6.2.1",
|
||||
"tslint": "^6.0.0",
|
||||
"typescript": "^3.8.2",
|
||||
"webpack": "^4.41.6",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"ts-loader": "^9.4.4",
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,5 @@ export function getGlobal(): any {
|
||||
} else if (typeof window === "object") {
|
||||
return window;
|
||||
}
|
||||
|
||||
throw new Error("No global available");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
VisualizationData,
|
||||
DataExtractionResult,
|
||||
} from "../../DataExtractionResult";
|
||||
import { DataExtractionResult, VisualizationData } from "../../DataExtractionResult";
|
||||
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||
|
||||
export interface DataExtractorApi {
|
||||
@@ -24,7 +21,8 @@ export interface DataExtractorApi {
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, unknown>
|
||||
variablesInScope: Record<string, unknown>,
|
||||
callFramesSnapshot: CallFramesSnapshot | null
|
||||
): JSONString<DataResult>;
|
||||
|
||||
/**
|
||||
@@ -42,12 +40,41 @@ export type DataResult =
|
||||
extractionResult: DataExtractionResult;
|
||||
}
|
||||
| { kind: "NoExtractors" }
|
||||
| { kind: "Error"; message: string };
|
||||
| { kind: "Error"; message: string }
|
||||
| {
|
||||
kind: "OutdatedCallFrameSnapshot";
|
||||
callFramesRequest: CallFramesRequest;
|
||||
};
|
||||
|
||||
export interface JSONString<T> extends String {
|
||||
__brand: { json: T };
|
||||
}
|
||||
|
||||
export interface CallFramesRequest {
|
||||
requestId: string;
|
||||
requestedCallFrames: CallFrameRequest[];
|
||||
}
|
||||
|
||||
export interface CallFrameRequest {
|
||||
methodName: string;
|
||||
pathRegExp: string | undefined;
|
||||
}
|
||||
|
||||
export interface CallFramesSnapshot {
|
||||
requestId: string;
|
||||
frames: (CallFrameInfo | SkippedCallFrames)[];
|
||||
}
|
||||
|
||||
export interface SkippedCallFrames {
|
||||
skippedFrames: number;
|
||||
}
|
||||
|
||||
export interface CallFrameInfo {
|
||||
methodName: string;
|
||||
source: { name: string; path: string };
|
||||
vars: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface DataExtractor {
|
||||
/**
|
||||
* Must be unique among all data extractors.
|
||||
@@ -58,11 +85,7 @@ export interface DataExtractor {
|
||||
* Filters the data to be extracted.
|
||||
*/
|
||||
dataCtor?: string;
|
||||
getExtractions(
|
||||
data: unknown,
|
||||
extractionCollector: ExtractionCollector,
|
||||
context: DataExtractorContext
|
||||
): void;
|
||||
getExtractions(data: unknown, extractionCollector: ExtractionCollector, context: DataExtractorContext): void;
|
||||
}
|
||||
|
||||
export interface ExtractionCollector {
|
||||
@@ -78,11 +101,15 @@ export interface DataExtractorContext {
|
||||
*/
|
||||
evalFn: <TEval>(expression: string) => TEval;
|
||||
|
||||
expression: string | undefined;
|
||||
readonly expression: string | undefined;
|
||||
|
||||
variablesInScope: Record<string, () => unknown>;
|
||||
readonly variablesInScope: Record<string, () => unknown>;
|
||||
|
||||
extract(value: unknown): VisualizationData | undefined;
|
||||
|
||||
readonly callFrameInfos: readonly (CallFrameInfo | SkippedCallFrames)[];
|
||||
|
||||
addCallFrameRequest(request: CallFrameRequest): void;
|
||||
}
|
||||
|
||||
export interface DataExtraction {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import {
|
||||
DataExtractorApi,
|
||||
DataResult,
|
||||
JSONString,
|
||||
DataExtractor,
|
||||
DataExtraction,
|
||||
ExtractionCollector,
|
||||
DataExtractorContext,
|
||||
} from "./DataExtractorApi";
|
||||
import {
|
||||
DataExtractorInfo,
|
||||
VisualizationData,
|
||||
} from "../../DataExtractionResult";
|
||||
import { registerDefaultExtractors } from "./default-extractors";
|
||||
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||
import * as helpers from "../helpers";
|
||||
import {
|
||||
CallFrameInfo,
|
||||
CallFrameRequest,
|
||||
CallFramesSnapshot,
|
||||
DataExtraction,
|
||||
DataExtractor,
|
||||
DataExtractorApi,
|
||||
DataExtractorContext,
|
||||
DataResult,
|
||||
JSONString,
|
||||
SkippedCallFrames,
|
||||
} from "./DataExtractorApi";
|
||||
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
|
||||
import { registerDefaultExtractors } from "./default-extractors";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -42,51 +45,18 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, () => unknown>
|
||||
variablesInScope: Record<string, () => unknown>,
|
||||
callFramesSnapshot: CallFramesSnapshot | null
|
||||
): JSONString<DataResult> {
|
||||
class ContextImpl implements DataExtractorContext {
|
||||
constructor(
|
||||
public readonly variablesInScope: Record<string, () => unknown>,
|
||||
public readonly expression: string | undefined,
|
||||
public readonly evalFn: <T>(expression: string) => T,
|
||||
private readonly _api: DataExtractorApiImpl,
|
||||
private readonly _parent: ContextImpl | undefined
|
||||
) {}
|
||||
|
||||
get _level(): number {
|
||||
return this._parent ? this._parent._level + 1 : 0;
|
||||
}
|
||||
|
||||
extract(value: any): VisualizationData | undefined {
|
||||
if (this._level > 10) {
|
||||
throw new Error(
|
||||
"extract() called too many times recursively"
|
||||
);
|
||||
}
|
||||
|
||||
const extractions = this._api.getExtractions(
|
||||
value,
|
||||
new ContextImpl(
|
||||
this.variablesInScope,
|
||||
undefined,
|
||||
this.evalFn,
|
||||
this._api,
|
||||
this
|
||||
)
|
||||
);
|
||||
if (extractions.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return extractions[0].extractData();
|
||||
}
|
||||
}
|
||||
|
||||
const callFrameRequests: CallFrameRequest[] = [];
|
||||
const rootContext = new ContextImpl(
|
||||
variablesInScope,
|
||||
removeEnd(removeStart(valueFn.toString(), "() => ("), ")").trim(),
|
||||
evalFn,
|
||||
this,
|
||||
undefined
|
||||
undefined,
|
||||
callFramesSnapshot?.frames ?? [],
|
||||
callFrameRequests
|
||||
);
|
||||
|
||||
DataExtractorApiImpl.lastContext = rootContext;
|
||||
@@ -94,6 +64,20 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
const extractions = this.getExtractions(value, rootContext);
|
||||
DataExtractorApiImpl.lastContext = undefined;
|
||||
|
||||
const requestId =
|
||||
callFrameRequests.length === 0
|
||||
? ""
|
||||
: "" + cyrb53(JSON.stringify(callFrameRequests));
|
||||
if ((callFramesSnapshot?.requestId ?? "") !== requestId) {
|
||||
return this.toJson({
|
||||
kind: "OutdatedCallFrameSnapshot",
|
||||
callFramesRequest: {
|
||||
requestId,
|
||||
requestedCallFrames: callFrameRequests,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let usedExtraction = extractions[0];
|
||||
if (!usedExtraction) {
|
||||
return this.toJson({ kind: "NoExtractors" } as DataResult);
|
||||
@@ -108,14 +92,6 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
}
|
||||
}
|
||||
|
||||
function mapExtractor(e: DataExtraction): DataExtractorInfo {
|
||||
return {
|
||||
id: e.id! as any,
|
||||
name: e.name!,
|
||||
priority: e.priority,
|
||||
};
|
||||
}
|
||||
|
||||
const data = usedExtraction.extractData();
|
||||
return this.toJson({
|
||||
kind: "Data",
|
||||
@@ -188,6 +164,60 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
}
|
||||
}
|
||||
|
||||
function mapExtractor(e: DataExtraction): DataExtractorInfo {
|
||||
return {
|
||||
id: e.id! as any,
|
||||
name: e.name!,
|
||||
priority: e.priority,
|
||||
};
|
||||
}
|
||||
|
||||
class ContextImpl implements DataExtractorContext {
|
||||
constructor(
|
||||
public readonly variablesInScope: Record<string, () => unknown>,
|
||||
public readonly expression: string | undefined,
|
||||
public readonly evalFn: <T>(expression: string) => T,
|
||||
private readonly _api: DataExtractorApiImpl,
|
||||
private readonly _parent: ContextImpl | undefined,
|
||||
public readonly callFrameInfos: readonly (
|
||||
| CallFrameInfo
|
||||
| SkippedCallFrames
|
||||
)[],
|
||||
private readonly _callFrameRequests: CallFrameRequest[]
|
||||
) {}
|
||||
|
||||
addCallFrameRequest(request: CallFrameRequest): void {
|
||||
this._callFrameRequests.push(request);
|
||||
}
|
||||
|
||||
get _level(): number {
|
||||
return this._parent ? this._parent._level + 1 : 0;
|
||||
}
|
||||
|
||||
extract(value: any): VisualizationData | undefined {
|
||||
if (this._level > 10) {
|
||||
throw new Error("extract() called too many times recursively");
|
||||
}
|
||||
|
||||
const extractions = this._api.getExtractions(
|
||||
value,
|
||||
new ContextImpl(
|
||||
this.variablesInScope,
|
||||
undefined,
|
||||
this.evalFn,
|
||||
this._api,
|
||||
this,
|
||||
this.callFrameInfos,
|
||||
this._callFrameRequests
|
||||
)
|
||||
);
|
||||
if (extractions.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return extractions[0].extractData();
|
||||
}
|
||||
}
|
||||
|
||||
function removeStart(str: string, start: string): string {
|
||||
if (str.startsWith(start)) {
|
||||
return str.substr(start.length);
|
||||
@@ -201,3 +231,20 @@ function removeEnd(str: string, end: string): string {
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
|
||||
function cyrb53(str: string, seed = 0) {
|
||||
let h1 = 0xdeadbeef ^ seed,
|
||||
h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
ch = str.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
||||
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as fs from "fs";
|
||||
import { getGlobal } from "../../getGlobal";
|
||||
import * as globalHelpers from "../global-helpers";
|
||||
import * as helpers from "../helpers";
|
||||
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.
|
||||
@@ -50,8 +50,18 @@ export function getDataExtractorApi(): DataExtractorApi {
|
||||
* @internal
|
||||
*/
|
||||
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
||||
const globalObj =
|
||||
typeof window === "object" ? (window as any) : (global as any);
|
||||
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");
|
||||
}
|
||||
|
||||
const globalObj = getGlobal();
|
||||
const key: typeof apiKey = "@hediet/data-extractor-api/v3";
|
||||
let api: DataExtractorApi | undefined = globalObj[key];
|
||||
if (!api) {
|
||||
|
||||
@@ -30,7 +30,12 @@ export function createGraphFromPointers<T>(
|
||||
return createGraph<T | Pointer>(
|
||||
items,
|
||||
(item) => {
|
||||
if ("marker" in item && item["marker"] === marker) {
|
||||
if (
|
||||
typeof item === "object" &&
|
||||
item &&
|
||||
"marker" in item &&
|
||||
item["marker"] === marker
|
||||
) {
|
||||
return {
|
||||
id: "label____" + item.name,
|
||||
color: "orange",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"target": "ES2018",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"outDir": "./dist",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
require("ts-node").register();
|
||||
module.exports = require("./webpack.config.ts");
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as webpack from "webpack";
|
||||
import path = require("path");
|
||||
import { CleanWebpackPlugin } from "clean-webpack-plugin";
|
||||
import * as path from "path";
|
||||
import * as webpack from "webpack";
|
||||
|
||||
const r = (file: string) => path.resolve(__dirname, file);
|
||||
|
||||
|
||||
2920
data-extraction/yarn.lock
Normal file
2920
data-extraction/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,130 +23,37 @@
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
register({
|
||||
id: "binaryViewer",
|
||||
getExtractions(data, collector, context) {
|
||||
if (typeof data !== "number") {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
register({
|
||||
id: "binaryViewer",
|
||||
getExtractions(data, collector, context) {
|
||||
if (typeof data !== "number") {
|
||||
return;
|
||||
}
|
||||
context.addCallFrameRequest({
|
||||
methodName: "Module._load",
|
||||
pathRegExp: ".*",
|
||||
});
|
||||
|
||||
collector.addExtraction({
|
||||
priority: 100,
|
||||
id: "binary",
|
||||
name: "Bits of Integer",
|
||||
extractData() {
|
||||
return helpers.asData({
|
||||
kind: { text: true },
|
||||
text: data.toString(2),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
context.addCallFrameRequest({
|
||||
methodName: "LinkedList.insertAt",
|
||||
pathRegExp: ".*",
|
||||
});*/
|
||||
|
||||
*/
|
||||
|
||||
(register, helpers) => {
|
||||
register({
|
||||
id: "positionOrRangeInTextModel",
|
||||
getExtractions(data, collector, context) {
|
||||
collector.addExtraction({
|
||||
priority: 100,
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
priority: 10000,
|
||||
id: "binary",
|
||||
name: "Bits of Integer",
|
||||
extractData() {
|
||||
return context.extract(JSON.stringify(context.callFrameInfos, undefined, 4));
|
||||
|
||||
return helpers.asData({
|
||||
kind: { text: true },
|
||||
text: Object.keys(context.variablesInScope).join(", "),
|
||||
text: data.toString(2),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -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.4.0",
|
||||
"version": "2.5.0",
|
||||
"license": "GPL-3.0",
|
||||
"engines": {
|
||||
"vscode": "^1.46.0"
|
||||
@@ -36,8 +36,6 @@
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onCommand:vscode-debug-visualizer.new-visualizer",
|
||||
"onCommand:vscode-debug-visualizer.visualizer-set-expression",
|
||||
"onDebug",
|
||||
"onWebviewPanel:debugVisualizer"
|
||||
],
|
||||
@@ -113,8 +111,8 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"pub": "vsce publish --packagePath extension.vsix",
|
||||
"package": "vsce package --no-dependencies -o extension.vsix --yarn --baseImagesUrl https://github.com/hediet/vscode-debug-visualizer/raw/master/extension --baseContentUrl https://github.com/hediet/vscode-debug-visualizer/raw/master/extension",
|
||||
"pub": "vsce publish --packagePath ./dist/extension.vsix",
|
||||
"package": "vsce package --no-dependencies -o ./dist/extension.vsix --yarn --baseImagesUrl https://github.com/hediet/vscode-debug-visualizer/raw/master/extension --baseContentUrl https://github.com/hediet/vscode-debug-visualizer/raw/master/extension",
|
||||
"vscode:prepublish": "",
|
||||
"build": "webpack --mode production",
|
||||
"dev": "shx rm -rf dist && tsc -watch -p ./"
|
||||
@@ -133,24 +131,18 @@
|
||||
"mobx-utils": "^5.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"@types/copy-webpack-plugin": "^5.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"@types/copy-webpack-plugin": "^10.1.0",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/serve-static": "^1.13.3",
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/vscode": "1.46.0",
|
||||
"tslint": "^6.0.0",
|
||||
"typescript": "^3.8.2",
|
||||
"webpack": "^4.41.6",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"ts-loader": "^6.2.1",
|
||||
"@types/vscode": "^1.79.1",
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"ts-loader": "^9.4.4",
|
||||
"shx": "^0.3.2",
|
||||
"vsce": "2.9.3"
|
||||
},
|
||||
"__metadata": {
|
||||
"id": "bce78737-8848-49f7-a27d-97004928fe41",
|
||||
"publisherDisplayName": "Henning Dieterichs",
|
||||
"publisherId": "4d8048f9-07cf-483a-b27b-1fdc29cea931",
|
||||
"isPreReleaseVersion": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,24 @@ import {
|
||||
GraphNode,
|
||||
GraphVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackend,
|
||||
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 {
|
||||
implements DebugSessionVisualizationSupport
|
||||
{
|
||||
constructor(private readonly debuggerView: DebuggerViewProxy) {}
|
||||
|
||||
createBackend(
|
||||
@@ -87,12 +88,12 @@ export class GenericVisualizationBackend extends VisualizationBackendBase {
|
||||
return parseEvaluationResultFromGenericDebugAdapter(
|
||||
reply.result,
|
||||
{
|
||||
debugAdapterType: this.debugSession.session
|
||||
.configuration.type,
|
||||
debugAdapterType:
|
||||
this.debugSession.session.configuration.type,
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import {
|
||||
ApiHasNotBeenInitializedCode,
|
||||
CallFrameRequest,
|
||||
CallFramesRequest,
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
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 { Disposable } from "@hediet/std/disposable";
|
||||
import { window } from "vscode";
|
||||
import { Config } from "../Config";
|
||||
import { DebugSessionProxy, StackFrame, StackTraceInfo } from "../proxies/DebugSessionProxy";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { FileWatcher } from "../webview/WebviewConnection";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
@@ -19,22 +22,14 @@ import {
|
||||
VisualizationBackend,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { DebouncedRunner } from "../utils/DebouncedRunner";
|
||||
import { FileWatcher } from "../webview/WebviewConnection";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class JsEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {}
|
||||
constructor(private readonly debuggerView: DebuggerViewProxy, private readonly config: Config) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
createBackend(session: DebugSessionProxy): VisualizationBackend | undefined {
|
||||
const supportedDebugAdapters = [
|
||||
"node",
|
||||
"node2",
|
||||
@@ -48,25 +43,21 @@ export class JsEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new JsVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
this.config
|
||||
);
|
||||
return new JsVisualizationBackend(session, this.debuggerView, this.config);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class CallFrameState {
|
||||
public request: CallFramesRequest | undefined = undefined;
|
||||
}
|
||||
|
||||
class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = "javascript";
|
||||
private initializePromise: Promise<void> | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
constructor(debugSession: DebugSessionProxy, debuggerView: DebuggerViewProxy, private readonly config: Config) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
@@ -79,20 +70,13 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
|
||||
public async getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const result = await this._getVisualizationData(args);
|
||||
): Promise<{ kind: "data"; result: DataExtractionResult } | { kind: "error"; message: FormattedMessage }> {
|
||||
let result = await this.getVisualizationDataIfInitialized(args);
|
||||
|
||||
if (result.kind === "not-initialized") {
|
||||
await this.initializeApiSafe();
|
||||
|
||||
const result2 = await this._getVisualizationData(args);
|
||||
|
||||
if (result2.kind !== "not-initialized") {
|
||||
return result2;
|
||||
} else {
|
||||
await this.initializeApiOrWait();
|
||||
result = await this.getVisualizationDataIfInitialized(args);
|
||||
if (result.kind === "not-initialized") {
|
||||
return {
|
||||
kind: "error",
|
||||
message: "Could not initialize API",
|
||||
@@ -103,103 +87,74 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
private async getVisualizationDataIfInitialized(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
| { kind: "not-initialized" }
|
||||
> {
|
||||
try {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
const { expression, preferredExtractorId } = args;
|
||||
let callFrameState: CallFrameState;
|
||||
if (args.sessionStore.data instanceof CallFrameState) {
|
||||
callFrameState = args.sessionStore.data;
|
||||
} else {
|
||||
callFrameState = new CallFrameState();
|
||||
args.sessionStore.data = callFrameState;
|
||||
}
|
||||
|
||||
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)
|
||||
try {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(this.debugSession);
|
||||
|
||||
const stackTrace = await this.debuggerView.activeDebugSession!.getStackTrace({
|
||||
threadId: 0,
|
||||
levels: 20,
|
||||
});
|
||||
|
||||
const variableNames = await getVariableNames(this.debugSession, frameId);
|
||||
|
||||
let tryCount = 0;
|
||||
while (true) {
|
||||
tryCount++;
|
||||
|
||||
let callFrameDataValue = "null";
|
||||
if (callFrameState.request) {
|
||||
callFrameDataValue = await getCallFramesSnapshotValue(
|
||||
this.debugSession,
|
||||
stackTrace,
|
||||
callFrameState.request
|
||||
);
|
||||
}
|
||||
const result = await this.evaluateVisualizationDataRequest(
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
variableNames,
|
||||
callFrameDataValue,
|
||||
frameId
|
||||
);
|
||||
|
||||
if (variableNames.length > 50) {
|
||||
variableNames.length = 50;
|
||||
if (result.kind === "NoExtractors") {
|
||||
throw new Error("No extractors");
|
||||
} else if (result.kind === "Error") {
|
||||
throw new Error(result.message + "\n" + (result as any).stack);
|
||||
} else if (result.kind === "Data") {
|
||||
return {
|
||||
kind: "data",
|
||||
result: result.extractionResult,
|
||||
};
|
||||
} else if (result.kind === "OutdatedCallFrameSnapshot") {
|
||||
if (tryCount >= 2) {
|
||||
throw new Error("OutdatedCallFrameSnapshot");
|
||||
}
|
||||
callFrameState.request = result.callFramesRequest;
|
||||
} else {
|
||||
throw new Error("Invalid Data");
|
||||
}
|
||||
}
|
||||
|
||||
const preferredExtractorExpr = preferredExtractorId
|
||||
? `"${preferredExtractorId}"`
|
||||
: "undefined";
|
||||
|
||||
const body = `${getExpressionForDataExtractorApi()}.getData(
|
||||
() => (${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 + "\n" + (result as any).stack);
|
||||
} else if (result.kind === "Data") {
|
||||
return {
|
||||
kind: "data",
|
||||
result: result.extractionResult,
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid Data");
|
||||
} catch (error: any) {
|
||||
if (typeof error.message === "string" && error.message.indexOf(ApiHasNotBeenInitializedCode) !== -1) {
|
||||
return { kind: "not-initialized" };
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
typeof error.message === "string" &&
|
||||
error.message.indexOf(ApiHasNotBeenInitializedCode) !== -1
|
||||
) {
|
||||
return {
|
||||
kind: "not-initialized",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "error",
|
||||
message: error.message,
|
||||
@@ -207,11 +162,42 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
}
|
||||
}
|
||||
|
||||
private initializeApiSafe(): Promise<void> {
|
||||
private async evaluateVisualizationDataRequest(
|
||||
expression: string,
|
||||
preferredExtractorId: DataExtractorId | undefined,
|
||||
variableNames: string[],
|
||||
callFrameDataValue: string,
|
||||
frameId: number | undefined
|
||||
): Promise<DataResult> {
|
||||
const preferredExtractorExpr = preferredExtractorId ? `"${preferredExtractorId}"` : "undefined";
|
||||
const body = `${getExpressionForDataExtractorApi()}.getData(
|
||||
() => (${expression}),
|
||||
expr => eval(expr),
|
||||
${preferredExtractorExpr},
|
||||
{${variableNames.map((n) => `${n}: () => ${n}`).join(",")}},
|
||||
${callFrameDataValue}
|
||||
)`;
|
||||
|
||||
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;
|
||||
return result;
|
||||
}
|
||||
|
||||
private initializeApiOrWait(): Promise<void> {
|
||||
if (!this.initializePromise) {
|
||||
this.initializePromise = this.initializeApi().finally(
|
||||
() => (this.initializePromise = undefined)
|
||||
);
|
||||
this.initializePromise = this.initializeApi().finally(() => (this.initializePromise = undefined));
|
||||
}
|
||||
return this.initializePromise;
|
||||
}
|
||||
@@ -219,10 +205,10 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
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({
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(this.debugSession);
|
||||
const result = await this.debugSession.evaluate({
|
||||
expression,
|
||||
frameId: undefined,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
@@ -235,18 +221,140 @@ class JsVisualizationBackend extends VisualizationBackendBase {
|
||||
this.dispose.untrack(this.customScripts);
|
||||
this.customScripts?.dispose();
|
||||
this.customScripts = this.dispose.track(
|
||||
new CustomScripts(this.debugSession, this.config, () =>
|
||||
this.onChangeEmitter.emit()
|
||||
)
|
||||
new CustomScripts(this.debugSession, this.debuggerView, this.config, () => this.onChangeEmitter.emit())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let globalCallFrameId = 0;
|
||||
|
||||
async function getCallFramesSnapshotValue(
|
||||
debugSession: DebugSessionProxy,
|
||||
stackTrace: StackTraceInfo,
|
||||
callFramesRequest: CallFramesRequest
|
||||
): Promise<string> {
|
||||
const globalFnDef = `
|
||||
function getGlobal() {
|
||||
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");
|
||||
}`;
|
||||
|
||||
const callFrameId = globalCallFrameId++;
|
||||
let idx = 0;
|
||||
let skippedCount = 0;
|
||||
const skippedCounts: Record<number, number> = {};
|
||||
|
||||
let frames = stackTrace.stackFrames;
|
||||
const firstAsyncFrameIdx = frames.findIndex((f) => f.id === 0);
|
||||
if (firstAsyncFrameIdx !== -1) {
|
||||
frames = frames.slice(0, firstAsyncFrameIdx);
|
||||
}
|
||||
|
||||
const result = await Promise.all(
|
||||
frames.map(async (frame) => {
|
||||
function matches(frame: StackFrame, callFrameRequest: CallFrameRequest): boolean {
|
||||
if (frame.name !== callFrameRequest.methodName) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!callFramesRequest.requestedCallFrames.some((r) => matches(frame, r))) {
|
||||
if (skippedCount === 0) {
|
||||
idx++;
|
||||
}
|
||||
skippedCount++;
|
||||
skippedCounts[idx - 1] = skippedCount;
|
||||
return undefined;
|
||||
}
|
||||
skippedCount = 0;
|
||||
const curFrameIdx = idx;
|
||||
idx++;
|
||||
|
||||
const scopes = await debugSession.getScopes({ frameId: frame.id });
|
||||
const scopeVariables = await Promise.all(
|
||||
scopes
|
||||
.filter((s) => !s.expensive && s.name.indexOf(":") !== -1)
|
||||
.map((s) => debugSession.getVariables({ variablesReference: s.variablesReference }))
|
||||
);
|
||||
const variables = new Set(scopeVariables.flatMap((v) => v.map((v) => v.name)));
|
||||
|
||||
await debugSession.evaluate({
|
||||
expression: `
|
||||
(() => {
|
||||
${globalFnDef}
|
||||
const g = getGlobal();
|
||||
(g.$$hedietDbgVslzr${callFrameId} = g.$$hedietDbgVslzr${callFrameId} || [])[${curFrameIdx}] = {
|
||||
vars: { ${[...variables].map((v) => `${v}: ${v}`).join(", ")} },
|
||||
...${JSON.stringify({
|
||||
methodName: frame.name,
|
||||
source: { path: frame.source.path, name: frame.source.name },
|
||||
})}
|
||||
};
|
||||
})();
|
||||
`,
|
||||
frameId: frame.id,
|
||||
context: "repl",
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
);
|
||||
|
||||
if (!result.some((r) => r)) {
|
||||
return `{ requestId: ${JSON.stringify(callFramesRequest.requestId)}, frames: [] }`;
|
||||
}
|
||||
|
||||
return `
|
||||
(() => {
|
||||
${globalFnDef}
|
||||
const g = getGlobal();
|
||||
const r = g.$$hedietDbgVslzr${callFrameId};
|
||||
delete g.$$hedietDbgVslzr${callFrameId};
|
||||
${Object.entries(skippedCounts)
|
||||
.map(([idx, val]) => `r[${idx}] = { skippedFrames: ${val} };\n`)
|
||||
.join("")}
|
||||
return { requestId: ${JSON.stringify(callFramesRequest.requestId)}, frames: r };
|
||||
})()
|
||||
`;
|
||||
}
|
||||
|
||||
async function getVariableNames(debugSession: DebugSessionProxy, frameId: number | undefined) {
|
||||
const variableNames = new Array<string>();
|
||||
if (frameId) {
|
||||
const scopes = await debugSession.getScopes({ frameId });
|
||||
const scopeVariables = await Promise.all(
|
||||
scopes
|
||||
.filter((s) => !s.expensive && s.name !== "Global")
|
||||
.map((s) =>
|
||||
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;
|
||||
}
|
||||
}
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
class CustomScripts {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
config: Config,
|
||||
changeHandler: () => void
|
||||
) {
|
||||
@@ -256,9 +364,7 @@ class CustomScripts {
|
||||
async (files) => {
|
||||
for (const file of files) {
|
||||
if (!file.fileExists) {
|
||||
window.showErrorMessage(
|
||||
`The file ${file.path} does not exist.`
|
||||
);
|
||||
window.showErrorMessage(`The file ${file.path} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -278,20 +384,16 @@ class CustomScripts {
|
||||
}
|
||||
)
|
||||
(
|
||||
${
|
||||
file.content === undefined
|
||||
? "undefined"
|
||||
: `function (module) { ${file.content} }`
|
||||
}
|
||||
${file.content === undefined ? "undefined" : `function (module) { ${file.content} }`}
|
||||
)`;
|
||||
|
||||
try {
|
||||
await debugSession.evaluate({
|
||||
expression,
|
||||
frameId: undefined,
|
||||
frameId: debuggerView.getActiveStackFrameId(debugSession),
|
||||
context: "repl",
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
window.showErrorMessage(
|
||||
'Error while running custom visualization extractor script "' +
|
||||
file.path +
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
GraphNode,
|
||||
GraphVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { Config } from "../Config";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackend,
|
||||
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 {
|
||||
export class PyEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) { }
|
||||
) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
|
||||
const supportedDebugAdapters = [
|
||||
"python",
|
||||
];
|
||||
const supportedDebugAdapters = ["python"];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new PyVisualizationBackend(
|
||||
@@ -81,7 +75,8 @@ export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||
try {
|
||||
// inject vscodedebugvisualizer for python
|
||||
await this.debugSession.evaluate({
|
||||
expression: 'from vscodedebugvisualizer import visualize\ntry:\n import debugvisualizer\nexcept ImportError:\n pass',
|
||||
expression:
|
||||
"from vscodedebugvisualizer import visualize\ntry:\n import debugvisualizer\nexcept ImportError:\n pass",
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
@@ -97,21 +92,23 @@ export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||
result = result.replace(/\\'/g, "'");
|
||||
result = result.replace(/\\\\/g, "\\");
|
||||
|
||||
return parseEvaluationResultFromGenericDebugAdapter(
|
||||
result,
|
||||
{
|
||||
debugAdapterType: this.debugSession.session
|
||||
.configuration.type,
|
||||
}
|
||||
);
|
||||
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'")) {
|
||||
if (
|
||||
errorTyped.message.includes(
|
||||
"ModuleNotFoundError: No module named 'vscodedebugvisualizer'"
|
||||
)
|
||||
) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: ["Please make sure vscodedebugvisualizer is installed: `pip install vscodedebugvisualizer`"],
|
||||
items: [
|
||||
"Please make sure vscodedebugvisualizer is installed: `pip install vscodedebugvisualizer`",
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -145,5 +142,4 @@ export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||
pythonInject += "visualize(" + args.expression + ")";
|
||||
return pythonInject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { DataExtractionResult, DataResult } from "@hediet/debug-visualizer-data-extraction";
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataResult,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { Config } from "../Config";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import { DebugSessionVisualizationSupport, GetVisualizationDataArgs, VisualizationBackend, VisualizationBackendBase } from "./VisualizationBackend";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackend,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@@ -13,12 +21,12 @@ export class RbEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) { }
|
||||
) {}
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
const supportedDebugAdapters = ['rdbg'];
|
||||
const supportedDebugAdapters = ["rdbg"];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new RbVisualizationBackend(
|
||||
@@ -32,13 +40,13 @@ export class RbEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
}
|
||||
|
||||
class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = 'ruby';
|
||||
public readonly expressionLanguageId = "ruby";
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
super(debugSession, debuggerView)
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
private readonly defaultContext = "repl";
|
||||
@@ -46,8 +54,8 @@ class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
public async getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult; }
|
||||
| { kind: "error"; message: FormattedMessage; }
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const result = await this._getVisualizationData(args);
|
||||
return result;
|
||||
@@ -55,7 +63,7 @@ class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
|
||||
private async _getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
@@ -69,9 +77,9 @@ class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
const initialReply = await this.debugSession.evaluate({
|
||||
expression: "require 'debugvisualizer'",
|
||||
frameId,
|
||||
context: this.defaultContext
|
||||
context: this.defaultContext,
|
||||
});
|
||||
if (initialReply.result.includes('LoadError')) {
|
||||
if (initialReply.result.includes("LoadError")) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
@@ -82,49 +90,56 @@ class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Install the gem by executing:",
|
||||
{ kind: "code", content: "$ bundle add debugvisualizer" },
|
||||
{
|
||||
kind: "code",
|
||||
content: "$ bundle add debugvisualizer",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"If bundler is not being used, install the gem by executing:",
|
||||
{ kind: "code", content: "$ gem install debugvisualizer" },
|
||||
{
|
||||
kind: "code",
|
||||
content:
|
||||
"$ gem install debugvisualizer",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
const preferredId = preferredExtractorId || '';
|
||||
const preferredId = preferredExtractorId || "";
|
||||
const wrappedExpr = `
|
||||
DebugVisualizer.to_debug_visualizer_protocol_json("${preferredId}", ${expression})
|
||||
`;
|
||||
const reply = await this.debugSession.evaluate({
|
||||
expression: wrappedExpr,
|
||||
frameId,
|
||||
context: this.defaultContext
|
||||
})
|
||||
context: this.defaultContext,
|
||||
});
|
||||
|
||||
let dataResult: DataResult;
|
||||
try {
|
||||
// Debuggee converts result to a JSON string twice.
|
||||
dataResult = JSON.parse(JSON.parse(reply.result)) as DataResult;
|
||||
} catch (error) {
|
||||
let message = error.message
|
||||
} catch (error: any) {
|
||||
let message = error.message;
|
||||
// The `reply.result` is as follows when error occurs in the evaluation of an expression and parsing will fail.
|
||||
// e.g. "#<ZeroDivisionError: divided by 0>"
|
||||
// In this case, it is more beneficial to display this error message.
|
||||
if (reply.result.includes('Error')) message = reply.result;
|
||||
throw new Error(message)
|
||||
if (reply.result.includes("Error")) message = reply.result;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
switch (dataResult.kind) {
|
||||
case 'NoExtractors':
|
||||
case "NoExtractors":
|
||||
throw new Error("No extractors");
|
||||
case 'Error':
|
||||
case "Error":
|
||||
throw new Error(dataResult.message);
|
||||
case 'Data':
|
||||
case "Data":
|
||||
return {
|
||||
kind: "data",
|
||||
result: dataResult.extractionResult,
|
||||
@@ -132,10 +147,10 @@ class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
default:
|
||||
throw new Error("Invalid Data");
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: error.message
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
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";
|
||||
import { reaction } from "mobx";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { CompletionItem, FormattedMessage } from "../webviewContract";
|
||||
|
||||
export interface DebugSessionVisualizationSupport {
|
||||
createBackend(session: DebugSessionProxy): VisualizationBackend | undefined;
|
||||
@@ -32,8 +32,11 @@ export interface VisualizationBackend extends Disposable {
|
||||
}
|
||||
|
||||
export interface GetVisualizationDataArgs {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
readonly expression: string;
|
||||
readonly preferredExtractorId: DataExtractorId | undefined;
|
||||
|
||||
// Can be used to attach data to the session.
|
||||
readonly sessionStore: { data: unknown };
|
||||
}
|
||||
|
||||
export abstract class VisualizationBackendBase implements VisualizationBackend {
|
||||
@@ -49,7 +52,7 @@ export abstract class VisualizationBackendBase implements VisualizationBackend {
|
||||
this.dispose.track({
|
||||
dispose: reaction(
|
||||
() => debuggerView.getActiveStackFrameId(debugSession),
|
||||
activeStackFrameId => {
|
||||
(activeStackFrameId) => {
|
||||
if (activeStackFrameId !== undefined) {
|
||||
this.onChangeEmitter.emit();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export function parseEvaluationResultFromGenericDebugAdapter(
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: e.message,
|
||||
@@ -93,7 +93,7 @@ function isEnclosedWith(str: string, char: string): boolean {
|
||||
function parseJson(str: string, context: ParseEvaluationResultContext) {
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
throw new FormattedError({
|
||||
kind: "list",
|
||||
items: [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
import { CompletionItem, DataExtractionState } from "../webviewContract";
|
||||
|
||||
export interface VisualizationWatchModel {
|
||||
createWatch(
|
||||
@@ -19,6 +19,7 @@ export interface VisualizationWatchModel {
|
||||
|
||||
export interface VisualizationWatchOptions {
|
||||
preferredDataExtractor?: DataExtractorId | undefined;
|
||||
sessionState?: unknown;
|
||||
}
|
||||
|
||||
export interface VisualizationWatch {
|
||||
@@ -31,6 +32,8 @@ export interface VisualizationWatch {
|
||||
/** This field is observable */
|
||||
readonly preferredDataExtractor: DataExtractorId | undefined;
|
||||
|
||||
readonly sessionState: unknown;
|
||||
|
||||
setPreferredDataExtractor(id: DataExtractorId | undefined): void;
|
||||
refresh(): void;
|
||||
dispose(): void;
|
||||
|
||||
@@ -7,11 +7,7 @@ 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";
|
||||
import { VisualizationWatch, VisualizationWatchModel, VisualizationWatchOptions } from "./VisualizationWatchModel";
|
||||
|
||||
@hotClass(module)
|
||||
export class VisualizationWatchModelImpl implements VisualizationWatchModel {
|
||||
@@ -22,23 +18,15 @@ export class VisualizationWatchModelImpl implements VisualizationWatchModel {
|
||||
this.dispose.track(
|
||||
visualizationBackend.onChange.sub(() => {
|
||||
if (visualizationBackend.expressionLanguageId !== undefined) {
|
||||
this.lastLanguageId =
|
||||
visualizationBackend.expressionLanguageId;
|
||||
this.lastLanguageId = visualizationBackend.expressionLanguageId;
|
||||
}
|
||||
})
|
||||
);
|
||||
this.lastLanguageId = visualizationBackend.expressionLanguageId;
|
||||
}
|
||||
|
||||
public createWatch(
|
||||
expression: string,
|
||||
options: VisualizationWatchOptions
|
||||
): VisualizationWatch {
|
||||
const w = new ObservableVisualizationWatch(
|
||||
expression,
|
||||
options,
|
||||
this.visualizationBackend
|
||||
);
|
||||
public createWatch(expression: string, options: VisualizationWatchOptions): VisualizationWatch {
|
||||
const w = new ObservableVisualizationWatch(expression, options, this.visualizationBackend);
|
||||
w.onDispose.sub(() => {
|
||||
this.watchers.delete(w);
|
||||
});
|
||||
@@ -53,10 +41,7 @@ export class VisualizationWatchModelImpl implements VisualizationWatchModel {
|
||||
return this.lastLanguageId;
|
||||
}
|
||||
|
||||
public getCompletions(
|
||||
text: string,
|
||||
column: number
|
||||
): Promise<CompletionItem[]> {
|
||||
public getCompletions(text: string, column: number): Promise<CompletionItem[]> {
|
||||
return this.visualizationBackend.getCompletions(text, column);
|
||||
}
|
||||
}
|
||||
@@ -115,31 +100,22 @@ class ObservableVisualizationWatch implements VisualizationWatch {
|
||||
this._refresh(tokenSource.token);
|
||||
}
|
||||
|
||||
public sessionState: unknown = undefined;
|
||||
|
||||
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 that = this;
|
||||
const result = await this.visualizationBackend.getVisualizationData({
|
||||
expression: this.expression,
|
||||
//frameId,
|
||||
preferredExtractorId: this.preferredDataExtractor,
|
||||
sessionStore: {
|
||||
get data(): unknown {
|
||||
return that.sessionState;
|
||||
},
|
||||
set data(value: unknown) {
|
||||
that.sessionState = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result.kind === "error") {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { observable } from "mobx";
|
||||
import { DebugSession } from "vscode";
|
||||
import { CompletionItem } from "../webviewContract";
|
||||
import { observable } from "mobx";
|
||||
|
||||
export class DebugSessionProxy {
|
||||
@observable private _activeStackFrameId: number | undefined;
|
||||
|
||||
constructor(public readonly session: DebugSession) {}
|
||||
|
||||
protected async getStackTrace(args: {
|
||||
public async getStackTrace(args: {
|
||||
threadId: number;
|
||||
startFrame?: number;
|
||||
levels?: number;
|
||||
}): Promise<{ totalFrames?: number; stackFrames: StackFrame[] }> {
|
||||
}): Promise<StackTraceInfo> {
|
||||
try {
|
||||
const reply = (await this.session.customRequest("stackTrace", {
|
||||
threadId: args.threadId,
|
||||
@@ -61,9 +61,7 @@ export class DebugSessionProxy {
|
||||
}
|
||||
}
|
||||
|
||||
public async getVariables(args: {
|
||||
variablesReference: number;
|
||||
}): Promise<Variable[]> {
|
||||
public async getVariables(args: { variablesReference: number }): Promise<Variable[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("variables", {
|
||||
variablesReference: args.variablesReference,
|
||||
@@ -100,6 +98,11 @@ export class DebugSessionProxy {
|
||||
}
|
||||
}
|
||||
|
||||
export interface StackTraceInfo {
|
||||
totalFrames?: number;
|
||||
stackFrames: StackFrame[];
|
||||
}
|
||||
|
||||
interface Scope {
|
||||
name: string;
|
||||
expensive: boolean;
|
||||
@@ -112,7 +115,8 @@ interface Variable {
|
||||
variablesReference: number;
|
||||
}
|
||||
|
||||
interface StackFrame {
|
||||
export interface StackFrame {
|
||||
id: number;
|
||||
name: string;
|
||||
source: { name: string; path: string };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
import { runInAction } from "mobx";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { DebugSessionProxy } from "./DebugSessionProxy";
|
||||
|
||||
/**
|
||||
@@ -14,7 +13,8 @@ export class DebuggerProxy {
|
||||
private readonly _onDidStartDebugSession = new EventEmitter<{
|
||||
session: DebugSessionProxy;
|
||||
}>();
|
||||
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
||||
public readonly onDidStartDebugSession =
|
||||
this._onDidStartDebugSession.asEvent();
|
||||
|
||||
public getDebugSessionProxy(session: DebugSession): DebugSessionProxy {
|
||||
let result = this.sessions.get(session);
|
||||
@@ -27,18 +27,18 @@ export class DebuggerProxy {
|
||||
|
||||
constructor() {
|
||||
this.dispose.track([
|
||||
debug.onDidStartDebugSession(session => {
|
||||
debug.onDidStartDebugSession((session) => {
|
||||
const e = this.sessions.get(session)!;
|
||||
this._onDidStartDebugSession.emit({ session: e });
|
||||
}),
|
||||
debug.onDidTerminateDebugSession(session => {
|
||||
debug.onDidTerminateDebugSession((session) => {
|
||||
this.sessions.delete(session);
|
||||
}),
|
||||
debug.registerDebugAdapterTrackerFactory("*", {
|
||||
createDebugAdapterTracker: session => {
|
||||
createDebugAdapterTracker: (session) => {
|
||||
const extendedSession = this.getDebugSessionProxy(session);
|
||||
return {
|
||||
onDidSendMessage: async msg => {
|
||||
onDidSendMessage: async (msg) => {
|
||||
type Message =
|
||||
| StoppedEvent
|
||||
| ThreadsResponse
|
||||
@@ -79,13 +79,12 @@ export class DebuggerProxy {
|
||||
if (m.type === "event") {
|
||||
if (m.event === "stopped") {
|
||||
const threadId = m.body.threadId;
|
||||
const r = await extendedSession[
|
||||
"getStackTrace"
|
||||
]({
|
||||
threadId,
|
||||
startFrame: 0,
|
||||
levels: 1,
|
||||
});
|
||||
const r =
|
||||
await extendedSession.getStackTrace({
|
||||
threadId,
|
||||
startFrame: 0,
|
||||
levels: 1,
|
||||
});
|
||||
extendedSession["_activeStackFrameId"] =
|
||||
r.stackFrames.length > 0
|
||||
? r.stackFrames[0].id
|
||||
@@ -98,9 +97,8 @@ export class DebuggerProxy {
|
||||
m.command === "stepIn" ||
|
||||
m.command === "stepOut"
|
||||
) {
|
||||
extendedSession[
|
||||
"_activeStackFrameId"
|
||||
] = undefined;
|
||||
extendedSession["_activeStackFrameId"] =
|
||||
undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { webviewContract } from "../webviewContract";
|
||||
import { ConsoleRpcLogger, RequestHandlingError } from "@hediet/typed-json-rpc";
|
||||
import {
|
||||
VisualizationWatch,
|
||||
VisualizationWatchModel,
|
||||
} from "../VisualizationWatchModel";
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import { observable, autorun } from "mobx";
|
||||
import { WebviewServer } from "./WebviewServer";
|
||||
import { existsSync, readFileSync, watch } from "fs";
|
||||
import { autorun, observable } from "mobx";
|
||||
import * as open from "open";
|
||||
import chromeLauncher = require("chrome-launcher");
|
||||
import { Config } from "../Config";
|
||||
import { IncrementalMap } from "../utils/IncrementalMap";
|
||||
import { watch, existsSync, readFileSync } from "fs";
|
||||
import { DebouncedRunner } from "../utils/DebouncedRunner";
|
||||
import { getExpressionForDataExtractorApi } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { window } from "vscode";
|
||||
import { Config } from "../Config";
|
||||
import { VisualizationWatch, VisualizationWatchModel } from "../VisualizationWatchModel";
|
||||
import { DebouncedRunner } from "../utils/DebouncedRunner";
|
||||
import { IncrementalMap } from "../utils/IncrementalMap";
|
||||
import { webviewContract } from "../webviewContract";
|
||||
import { WebviewServer } from "./WebviewServer";
|
||||
import chromeLauncher = require("chrome-launcher");
|
||||
|
||||
export class WebviewConnection {
|
||||
public readonly dispose = Disposable.fn();
|
||||
@@ -23,7 +19,7 @@ export class WebviewConnection {
|
||||
@observable
|
||||
private watcher: VisualizationWatch | undefined = undefined;
|
||||
|
||||
private readonly client: typeof webviewContract["TClientInterface"];
|
||||
private readonly client: (typeof webviewContract)["TClientInterface"];
|
||||
|
||||
constructor(
|
||||
evaluationWatchService: VisualizationWatchModel,
|
||||
@@ -40,78 +36,74 @@ export class WebviewConnection {
|
||||
}
|
||||
}
|
||||
|
||||
const { client, channel } = webviewContract.registerServerToStream(
|
||||
stream,
|
||||
new ConsoleRpcLogger(),
|
||||
{
|
||||
authenticate: async ({ secret }, { newErr }) => {
|
||||
if (secret !== serverSecret) {
|
||||
return newErr({ errorMessage: "Invalid Secret" });
|
||||
} else {
|
||||
authenticated = true;
|
||||
}
|
||||
},
|
||||
refresh: async () => {
|
||||
throwIfNotAuthenticated();
|
||||
const { client, channel } = webviewContract.registerServerToStream(stream, new ConsoleRpcLogger(), {
|
||||
authenticate: async ({ secret }, { newErr }) => {
|
||||
if (secret !== serverSecret) {
|
||||
return newErr({ errorMessage: "Invalid Secret" });
|
||||
} else {
|
||||
authenticated = true;
|
||||
}
|
||||
},
|
||||
refresh: async () => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
if (this.watcher) {
|
||||
this.watcher.refresh();
|
||||
}
|
||||
},
|
||||
setExpression: async ({ newExpression }) => {
|
||||
throwIfNotAuthenticated();
|
||||
if (this.watcher) {
|
||||
this.watcher.refresh();
|
||||
}
|
||||
},
|
||||
setExpression: async ({ newExpression }) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
let oldPreferredDataExtractor: VisualizationWatch["preferredDataExtractor"];
|
||||
if (this.watcher) {
|
||||
oldPreferredDataExtractor =
|
||||
this.watcher.preferredDataExtractor;
|
||||
this.dispose.untrack(this.watcher).dispose();
|
||||
}
|
||||
this.watcher = this.dispose.track(
|
||||
evaluationWatchService.createWatch(newExpression, {
|
||||
preferredDataExtractor: oldPreferredDataExtractor,
|
||||
})
|
||||
);
|
||||
},
|
||||
openInBrowser: async ({}) => {
|
||||
throwIfNotAuthenticated();
|
||||
if (this.watcher?.expression === newExpression) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = server.getWebviewPageUrl({
|
||||
mode: "standalone",
|
||||
expression: this.watcher
|
||||
? this.watcher.expression
|
||||
: undefined,
|
||||
});
|
||||
let oldPreferredDataExtractor: VisualizationWatch["preferredDataExtractor"];
|
||||
let oldSessionState: unknown;
|
||||
if (this.watcher) {
|
||||
oldPreferredDataExtractor = this.watcher.preferredDataExtractor;
|
||||
oldSessionState = this.watcher.sessionState;
|
||||
this.dispose.untrack(this.watcher).dispose();
|
||||
}
|
||||
this.watcher = this.dispose.track(
|
||||
evaluationWatchService.createWatch(newExpression, {
|
||||
preferredDataExtractor: oldPreferredDataExtractor,
|
||||
sessionState: oldSessionState,
|
||||
})
|
||||
);
|
||||
},
|
||||
openInBrowser: async ({}) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
let opened = false;
|
||||
if (config.useChromeKioskMode) {
|
||||
opened = await launchChrome(url);
|
||||
}
|
||||
if (!opened) {
|
||||
open(url);
|
||||
}
|
||||
},
|
||||
setPreferredDataExtractor: async ({ dataExtractorId }) => {
|
||||
throwIfNotAuthenticated();
|
||||
const url = server.getWebviewPageUrl({
|
||||
mode: "standalone",
|
||||
expression: this.watcher ? this.watcher.expression : undefined,
|
||||
});
|
||||
|
||||
if (this.watcher) {
|
||||
this.watcher.setPreferredDataExtractor(dataExtractorId);
|
||||
}
|
||||
},
|
||||
getCompletions: async ({ text, column }) => {
|
||||
throwIfNotAuthenticated();
|
||||
let opened = false;
|
||||
if (config.useChromeKioskMode) {
|
||||
opened = await launchChrome(url);
|
||||
}
|
||||
if (!opened) {
|
||||
open(url);
|
||||
}
|
||||
},
|
||||
setPreferredDataExtractor: async ({ dataExtractorId }) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
const completions =
|
||||
await evaluationWatchService.getCompletions(
|
||||
text,
|
||||
column
|
||||
);
|
||||
return {
|
||||
completions,
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
if (this.watcher) {
|
||||
this.watcher.setPreferredDataExtractor(dataExtractorId);
|
||||
}
|
||||
},
|
||||
getCompletions: async ({ text, column }) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
const completions = await evaluationWatchService.getCompletions(text, column);
|
||||
return {
|
||||
completions,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
@@ -121,9 +113,7 @@ export class WebviewConnection {
|
||||
async (files) => {
|
||||
for (const file of files) {
|
||||
if (!file.fileExists) {
|
||||
window.showErrorMessage(
|
||||
`The file ${file.path} does not exist.`
|
||||
);
|
||||
window.showErrorMessage(`The file ${file.path} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -132,7 +122,7 @@ export class WebviewConnection {
|
||||
id: file.path,
|
||||
jsSource: file.content || null,
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
window.showErrorMessage(
|
||||
'Error while running custom visualization extractor script "' +
|
||||
file.path +
|
||||
@@ -214,9 +204,7 @@ export class FileWatcher {
|
||||
new SingleFileWatcher(path, (w) => {
|
||||
scheduleRefresh.add(w);
|
||||
debouncer.run(() => {
|
||||
const changedWatchers = [...scheduleRefresh].filter(
|
||||
(w) => w.refresh()
|
||||
);
|
||||
const changedWatchers = [...scheduleRefresh].filter((w) => w.refresh());
|
||||
handleFileContents(
|
||||
changedWatchers.map((w) => ({
|
||||
path: w.path,
|
||||
@@ -242,18 +230,13 @@ export class FileWatcher {
|
||||
}
|
||||
|
||||
class SingleFileWatcher {
|
||||
private readonly watcher = watch(this.path, { encoding: "utf-8" }, () =>
|
||||
this.scheduleRefresh(this)
|
||||
);
|
||||
private readonly watcher = watch(this.path, { encoding: "utf-8" }, () => this.scheduleRefresh(this));
|
||||
private isDisposed = false;
|
||||
public content: string | undefined;
|
||||
public exists: boolean = true;
|
||||
|
||||
constructor(
|
||||
public readonly path: string,
|
||||
private readonly scheduleRefresh: (self: SingleFileWatcher) => void
|
||||
) {
|
||||
this.scheduleRefresh(this);
|
||||
constructor(public readonly path: string, private readonly scheduleRefresh: (self: SingleFileWatcher) => void) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"target": "ES2017",
|
||||
"outDir": "dist",
|
||||
"lib": ["es6"],
|
||||
"lib": ["ES2017"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../js-data-extractors/src/Extractors/TypeScriptDataExtractors.ts"
|
||||
]
|
||||
"include": ["src/**/*", "../js-data-extractors/src/Extractors/TypeScriptDataExtractors.ts"]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as webpack from "webpack";
|
||||
import path = require("path");
|
||||
import { CleanWebpackPlugin } from "clean-webpack-plugin";
|
||||
import CopyPlugin = require("copy-webpack-plugin");
|
||||
import * as CopyPlugin from "copy-webpack-plugin";
|
||||
import { readFileSync } from "fs";
|
||||
import * as path from "path";
|
||||
import * as webpack from "webpack";
|
||||
|
||||
const r = (file: string) => path.resolve(__dirname, file);
|
||||
|
||||
@@ -21,7 +21,7 @@ module.exports = {
|
||||
"@hediet/debug-visualizer-data-extraction":
|
||||
"@hediet/debug-visualizer-data-extraction",
|
||||
"debug-visualizer-webview": "debug-visualizer-webview",
|
||||
fsevents: "require('fsevents')"
|
||||
fsevents: "require('fsevents')",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
@@ -55,11 +55,15 @@ function includeDependency(location: string) {
|
||||
});
|
||||
const pkgName = JSON.parse(content).name;
|
||||
|
||||
return new CopyPlugin([
|
||||
{
|
||||
from: location,
|
||||
to: r(`./dist/node_modules/${pkgName}`),
|
||||
ignore: ["**/node_modules/**/*"],
|
||||
},
|
||||
]);
|
||||
return new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: location,
|
||||
to: r(`./dist/node_modules/${pkgName}`),
|
||||
globOptions: {
|
||||
ignore: ["**/node_modules/**/*"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"build-extension": "yarn workspace debug-visualizer build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^1.19.1"
|
||||
"prettier": "^2.8.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --hot",
|
||||
"build": "node --max_old_space_size=4096 ../node_modules/webpack/bin/webpack.js --mode production"
|
||||
"build": "node --max_old_space_size=4096 ../node_modules/webpack/bin/webpack.js --mode development"
|
||||
},
|
||||
"main": "./index.js",
|
||||
"dependencies": {
|
||||
@@ -28,25 +28,24 @@
|
||||
"devDependencies": {
|
||||
"@types/html-webpack-plugin": "^3.2.2",
|
||||
"@types/react-measure": "^2.0.6",
|
||||
"@types/webpack": "^4.41.6",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/react": "^16.9.22",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"file-loader": "^5.1.0",
|
||||
"fork-ts-checker-webpack-plugin": "^4.0.4",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^8.0.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"monaco-editor-webpack-plugin": "^4.0.0",
|
||||
"sass": "^1.25.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"style-loader": "^1.1.3",
|
||||
"ts-loader": "^6.2.1",
|
||||
"ts-node": "^8.6.2",
|
||||
"typescript": "^3.8.2",
|
||||
"webpack": "^4.41.6",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
"ts-loader": "^9.4.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as React from "react";
|
||||
import { Button, Spinner } from "@blueprintjs/core";
|
||||
import { observable, action } from "mobx";
|
||||
import { action, observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Model } from "../model/Model";
|
||||
import { ExpressionInput } from "./ExpressionInput";
|
||||
import { Visualizer } from "./Visualizer";
|
||||
import classnames = require("classnames");
|
||||
import { VisualizerHeaderDetails } from "./VisualizerHeaderDetails";
|
||||
import classnames = require("classnames");
|
||||
|
||||
@observer
|
||||
export class GUI extends React.Component<{ model: Model }> {
|
||||
@@ -25,10 +25,7 @@ export class GUI extends React.Component<{ model: Model }> {
|
||||
<div className="part-Header">
|
||||
<VisualizerHeader model={m} />
|
||||
</div>
|
||||
<div
|
||||
className="part-Visualizer"
|
||||
style={{ flex: 1, minHeight: 0 }}
|
||||
>
|
||||
<div className="part-Visualizer" style={{ flex: 1, minHeight: 0 }}>
|
||||
<Visualizer model={m} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,15 +40,9 @@ export class VisualizerHeader extends React.Component<{ model: Model }> {
|
||||
render() {
|
||||
const m = this.props.model;
|
||||
return (
|
||||
<div
|
||||
className="component-VisualizerHeader"
|
||||
style={{ display: "flex", flexDirection: "row" }}
|
||||
>
|
||||
<div className="component-VisualizerHeader" style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div
|
||||
className={classnames(
|
||||
"part-ExpandButton",
|
||||
this.expanded && "expanded"
|
||||
)}
|
||||
className={classnames("part-ExpandButton", this.expanded && "expanded")}
|
||||
onClick={this.toggleExpanded}
|
||||
/>
|
||||
<div
|
||||
@@ -96,10 +87,7 @@ export class VisualizerHeaderMain extends React.Component<{ model: Model }> {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="part-ExpressionInput "
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
>
|
||||
<div className="part-ExpressionInput " style={{ flex: 1, minWidth: 0 }}>
|
||||
<ExpressionInput model={m} />
|
||||
</div>
|
||||
<div style={{ width: 4 }} />
|
||||
@@ -109,21 +97,9 @@ export class VisualizerHeaderMain extends React.Component<{ model: Model }> {
|
||||
<Spinner size={Spinner.SIZE_SMALL} />
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
minimal
|
||||
small
|
||||
className="part-Icon"
|
||||
icon="refresh"
|
||||
onClick={() => m.refresh()}
|
||||
/>
|
||||
<Button minimal small className="part-Icon" icon="refresh" onClick={() => m.refresh()} />
|
||||
))}
|
||||
<Button
|
||||
minimal
|
||||
small
|
||||
className="part-Icon"
|
||||
icon="log-in"
|
||||
onClick={() => m.openBrowser()}
|
||||
/>
|
||||
<Button minimal small className="part-Icon" icon="log-in" onClick={() => m.openBrowser()} />
|
||||
{/*
|
||||
TODO
|
||||
<Button
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as monaco from "monaco-editor";
|
||||
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";
|
||||
import "./style.scss";
|
||||
|
||||
(globalThis as any).monaco = monaco;
|
||||
|
||||
|
||||
@@ -1,37 +1,27 @@
|
||||
import {
|
||||
webviewContract,
|
||||
DataExtractionState,
|
||||
WindowWithWebviewData,
|
||||
WebviewUrlParams,
|
||||
} from "debug-visualizer/src/webviewContract";
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import { DataExtractorId, VisualizationData } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { EventTimer } from "@hediet/std/timer";
|
||||
import { ConsoleRpcLogger } from "@hediet/typed-json-rpc";
|
||||
import {
|
||||
observable,
|
||||
action,
|
||||
computed,
|
||||
when,
|
||||
runInAction,
|
||||
ObservableMap,
|
||||
} from "mobx";
|
||||
import {
|
||||
DataExtractorId,
|
||||
VisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import "@hediet/visualization-bundle";
|
||||
import {
|
||||
globalVisualizationFactory,
|
||||
VisualizationId,
|
||||
Visualization,
|
||||
RegisterVisualizerFn,
|
||||
createVisualizer,
|
||||
libImplementation,
|
||||
Visualization,
|
||||
VisualizationFactory,
|
||||
VisualizationId,
|
||||
createVisualizer,
|
||||
globalVisualizationFactory,
|
||||
libImplementation,
|
||||
} from "@hediet/visualization-core";
|
||||
import { getApi as getVsCodeApi } from "./VsCodeApi";
|
||||
import {
|
||||
DataExtractionState,
|
||||
WebviewUrlParams,
|
||||
WindowWithWebviewData,
|
||||
webviewContract,
|
||||
} from "debug-visualizer/src/webviewContract";
|
||||
import { ObservableMap, action, computed, observable, runInAction, when } from "mobx";
|
||||
import { MonacoBridge } from "./MonacoBridge";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { startInterval, EventTimer } from "@hediet/std/timer";
|
||||
import { getApi as getVsCodeApi } from "./VsCodeApi";
|
||||
|
||||
declare const window: Window & WindowWithWebviewData;
|
||||
|
||||
@@ -39,8 +29,7 @@ declare let __webpack_public_path__: string;
|
||||
|
||||
export class Model {
|
||||
public readonly dispose = Disposable.fn();
|
||||
public readonly runningMode: "standalone" | "webview" | "webviewIFrame" =
|
||||
"webview";
|
||||
public readonly runningMode: "standalone" | "webview" | "webviewIFrame" = "webview";
|
||||
|
||||
@observable
|
||||
public theme: "dark" | "light" = "light";
|
||||
@@ -58,13 +47,10 @@ export class Model {
|
||||
|
||||
@observable languageId: string = "text";
|
||||
|
||||
@observable private preferredVisualizationId: VisualizationId | undefined =
|
||||
undefined;
|
||||
@observable private preferredVisualizationId: VisualizationId | undefined = undefined;
|
||||
|
||||
@observable isPolling = false;
|
||||
private readonly pollingTimer = this.dispose.track(
|
||||
new EventTimer(500, "stopped")
|
||||
);
|
||||
private readonly pollingTimer = this.dispose.track(new EventTimer(500, "stopped"));
|
||||
|
||||
@action
|
||||
public setPreferredVisualizationId(id: VisualizationId) {
|
||||
@@ -95,10 +81,7 @@ export class Model {
|
||||
if (this.state.kind === "data") {
|
||||
const vis = visualizerRegistry
|
||||
.get()
|
||||
.getVisualizations(
|
||||
this.state.result.data,
|
||||
this.preferredVisualizationId
|
||||
);
|
||||
.getVisualizations(this.state.result.data, this.preferredVisualizationId);
|
||||
return {
|
||||
visualization: vis.bestVisualization,
|
||||
allVisualizations: vis.allVisualizations,
|
||||
@@ -109,8 +92,7 @@ export class Model {
|
||||
}
|
||||
|
||||
@observable.ref
|
||||
public server: typeof webviewContract.TServerInterface | undefined =
|
||||
undefined;
|
||||
public server: typeof webviewContract.TServerInterface | undefined = undefined;
|
||||
|
||||
private readonly vsCodeApi = getVsCodeApi<{ expression: string }>();
|
||||
|
||||
@@ -137,9 +119,7 @@ export class Model {
|
||||
}
|
||||
} else {
|
||||
const url = new URL(window.location.href);
|
||||
const urlParams = Object.fromEntries(
|
||||
url.searchParams.entries()
|
||||
) as unknown as WebviewUrlParams;
|
||||
const urlParams = Object.fromEntries(url.searchParams.entries()) as unknown as WebviewUrlParams;
|
||||
const portStr = urlParams.serverPort;
|
||||
if (!portStr) {
|
||||
throw new Error("No port given.");
|
||||
@@ -220,29 +200,26 @@ export class Model {
|
||||
host: "localhost",
|
||||
port: this.port,
|
||||
});
|
||||
const { server } = webviewContract.getServerFromStream(
|
||||
stream,
|
||||
new ConsoleRpcLogger(),
|
||||
{
|
||||
updateState: async ({ newState }) => {
|
||||
runInAction(() => {
|
||||
this._loading = newState.kind === "loading";
|
||||
if (!this._loading) {
|
||||
this.state = newState;
|
||||
}
|
||||
});
|
||||
},
|
||||
setExpression: async ({ expression }) => {
|
||||
this.setExpression(expression);
|
||||
},
|
||||
updateLanguageId: async ({ languageId }) => {
|
||||
this.languageId = languageId || "text";
|
||||
},
|
||||
setTheme: async ({ theme }) => {
|
||||
this.theme = theme;
|
||||
},
|
||||
setCustomVisualizerScript: async ({ id, jsSource }) => {
|
||||
eval(`
|
||||
const { server } = webviewContract.getServerFromStream(stream, new ConsoleRpcLogger(), {
|
||||
updateState: async ({ newState }) => {
|
||||
runInAction(() => {
|
||||
this._loading = newState.kind === "loading";
|
||||
if (!this._loading) {
|
||||
this.state = newState;
|
||||
}
|
||||
});
|
||||
},
|
||||
setExpression: async ({ expression }) => {
|
||||
this.setExpression(expression);
|
||||
},
|
||||
updateLanguageId: async ({ languageId }) => {
|
||||
this.languageId = languageId || "text";
|
||||
},
|
||||
setTheme: async ({ theme }) => {
|
||||
this.theme = theme;
|
||||
},
|
||||
setCustomVisualizerScript: async ({ id, jsSource }) => {
|
||||
eval(`
|
||||
((load) => {
|
||||
let fn = undefined;
|
||||
if (load) {
|
||||
@@ -255,9 +232,8 @@ export class Model {
|
||||
${jsSource ? `function (module) { ${jsSource} }` : "undefined"}
|
||||
)
|
||||
`);
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
try {
|
||||
await server.authenticate({ secret: this.serverSecret });
|
||||
} catch (e) {
|
||||
@@ -291,18 +267,12 @@ const visualizerRegistry = computed(() => {
|
||||
result.addHiddenVisualizer(visualization);
|
||||
}
|
||||
fns.forEach((fn) => {
|
||||
fn(
|
||||
(options) => result.addVisualizer(createVisualizer(options)),
|
||||
libImplementation
|
||||
);
|
||||
fn((options) => result.addVisualizer(createVisualizer(options)), libImplementation);
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
function setVisualizationModule(
|
||||
id: string,
|
||||
moduleFn: RegisterVisualizerFn | undefined
|
||||
) {
|
||||
function setVisualizationModule(id: string, moduleFn: RegisterVisualizerFn | undefined) {
|
||||
if (moduleFn) {
|
||||
fns.set(id, moduleFn);
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"outDir": "dist",
|
||||
"skipLibCheck": true,
|
||||
"rootDir": "./src",
|
||||
"resolveJsonModule": true,
|
||||
"newLine": "LF",
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"outDir": "dist",
|
||||
"lib": ["DOM", "ES2023", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"rootDir": "./src",
|
||||
"resolveJsonModule": true,
|
||||
"newLine": "LF",
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as webpack from "webpack";
|
||||
import path = require("path");
|
||||
import HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
import MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
import ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
||||
import { CleanWebpackPlugin } from "clean-webpack-plugin";
|
||||
import * as HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import * as path from "path";
|
||||
import * as webpack from "webpack";
|
||||
import ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
||||
import MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
|
||||
const r = (file: string) => path.resolve(__dirname, file);
|
||||
|
||||
@@ -13,7 +13,7 @@ module.exports = {
|
||||
path: r("dist"),
|
||||
filename: "[name].js",
|
||||
chunkFilename: "[name]-[hash].js",
|
||||
devtoolModuleFilenameTemplate: info => {
|
||||
devtoolModuleFilenameTemplate: (info: any) => {
|
||||
let result = info.absoluteResourcePath.replace(/\\/g, "/");
|
||||
if (!result.startsWith("file:")) {
|
||||
// Some paths already start with the file scheme.
|
||||
@@ -30,10 +30,10 @@ module.exports = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.less$/,
|
||||
loaders: ["style-loader", "css-loader", "less-loader"],
|
||||
use: ["style-loader", "css-loader", "less-loader"],
|
||||
},
|
||||
{ test: /\.css$/, loader: "style-loader!css-loader" },
|
||||
{ test: /\.scss$/, loader: "style-loader!css-loader!sass-loader" },
|
||||
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
|
||||
{ test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] },
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|eot|ttf|svg|woff|woff2|md)$/i,
|
||||
loader: "file-loader",
|
||||
@@ -45,9 +45,9 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
node: {
|
||||
/*node: {
|
||||
fs: "empty",
|
||||
},
|
||||
},*/
|
||||
plugins: (() => {
|
||||
const plugins: any[] = [
|
||||
new HtmlWebpackPlugin({
|
||||
@@ -55,10 +55,13 @@ module.exports = {
|
||||
}),
|
||||
new ForkTsCheckerWebpackPlugin(),
|
||||
new CleanWebpackPlugin(),
|
||||
new MonacoWebpackPlugin({
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": JSON.stringify(process.env),
|
||||
}),
|
||||
/*new MonacoWebpackPlugin({
|
||||
// Add more languages here once webworker issues are solved.
|
||||
languages: ["typescript"],
|
||||
}),
|
||||
}),*/
|
||||
];
|
||||
|
||||
return plugins;
|
||||
|
||||
Reference in New Issue
Block a user