Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453e0ae5a8 | ||
|
|
08d97c2a82 | ||
|
|
e487dead95 | ||
|
|
8ce8e7dad8 | ||
|
|
41747c5dd0 | ||
|
|
1dd0349483 | ||
|
|
62415f1775 | ||
|
|
b23b90c8bf | ||
|
|
24aa6857e5 | ||
|
|
7ba2428819 | ||
|
|
f09e345908 | ||
|
|
c56f21d8ab | ||
|
|
5964c1c503 | ||
|
|
e5707a0f2e | ||
|
|
d0f1a0144e | ||
|
|
83b93b1369 | ||
|
|
39de71521b | ||
|
|
51a42eb497 | ||
|
|
ac90389aa1 | ||
|
|
80c5d34c81 | ||
|
|
787fc73b33 | ||
|
|
33b076cbd3 | ||
|
|
d73b3797cb | ||
|
|
04ea412322 | ||
|
|
b65ba5fdbe | ||
|
|
410e5d3048 | ||
|
|
e9803a0e98 | ||
|
|
0a65f6425e | ||
|
|
c9625d4939 | ||
|
|
59f94eda45 | ||
|
|
507e4fc695 | ||
|
|
52599482f3 | ||
|
|
415f061697 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: hediet
|
||||||
23
.github/workflows/ci.yml
vendored
Normal file
23
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10.x
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn build
|
||||||
15
.github/workflows/opened-issues-triage.yml
vendored
Normal file
15
.github/workflows/opened-issues-triage.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
name: Move new issues into Triage
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
automate-project-columns:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: alex-page/github-project-automation-plus@v0.2.3
|
||||||
|
with:
|
||||||
|
project: Backlog
|
||||||
|
column: Triage
|
||||||
|
repo-token: ${{ secrets.GH_TOKEN }}
|
||||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -15,7 +15,7 @@
|
|||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
||||||
"${workspaceFolder}\\demos\\php"
|
"${workspaceFolder}/demos/php"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"HOT_RELOAD": "true",
|
"HOT_RELOAD": "true",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
||||||
"${workspaceFolder}\\demos\\php"
|
"${workspaceFolder}/demos/php"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"HOT_RELOAD": "true",
|
"HOT_RELOAD": "true",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
||||||
"${workspaceFolder}\\demos\\js"
|
"${workspaceFolder}/demos/python"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"HOT_RELOAD": "",
|
"HOT_RELOAD": "",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
"--extensionDevelopmentPath=${workspaceFolder}/extension",
|
||||||
"${workspaceFolder}\\demos\\php"
|
"${workspaceFolder}/demos/js"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"HOT_RELOAD": "",
|
"HOT_RELOAD": "",
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -7,5 +7,6 @@
|
|||||||
"plantuml.exportOutDir": "docs/exported",
|
"plantuml.exportOutDir": "docs/exported",
|
||||||
"typescript.updateImportsOnFileMove.enabled": "prompt",
|
"typescript.updateImportsOnFileMove.enabled": "prompt",
|
||||||
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# VS Code Debug Visualizer
|
# VS Code Debug Visualizer
|
||||||
|
|
||||||
|
[](https://github.com/sponsors/hediet)
|
||||||
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZP5F38L4C88UY&source=url)
|
||||||
[](https://twitter.com/intent/follow?screen_name=hediet_dev)
|
[](https://twitter.com/intent/follow?screen_name=hediet_dev)
|
||||||
|
|
||||||
See [README.md](./extension/README.md) for the readme of the extension.
|
See [README.md](./extension/README.md) for the readme of the extension.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@hediet/debug-visualizer-data-extraction",
|
"name": "@hediet/debug-visualizer-data-extraction",
|
||||||
"description": "A library that helps implementing data extractors for the Debug Visualizer VS Code extension.",
|
"description": "A library that helps implementing data extractors for the Debug Visualizer VS Code extension.",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -1,122 +1,256 @@
|
|||||||
import { ExtractedData } from "./DataExtractionResult";
|
// This file was created automatically. Do not edit it manually!
|
||||||
|
|
||||||
export type CommonDataType =
|
export type KnownVisualizationData =
|
||||||
| CommonDataTypes.Text
|
| TreeVisualizationData
|
||||||
| CommonDataTypes.Svg
|
| AstTreeVisualizationData
|
||||||
| CommonDataTypes.Html
|
| GraphvizDotVisualizationData
|
||||||
| CommonDataTypes.DotGraph
|
| GraphVisualizationData
|
||||||
| CommonDataTypes.Tree
|
| GraphVisualizationData
|
||||||
| CommonDataTypes.Ast
|
| GridVisualizationData
|
||||||
| CommonDataTypes.Graph
|
| ImageVisualizationData
|
||||||
| CommonDataTypes.Plotly
|
| MonacoTextVisualizationData
|
||||||
| CommonDataTypes.Grid;
|
| TableVisualizationData
|
||||||
|
| PlotlyVisualizationData
|
||||||
|
| SimpleTextVisualizationData
|
||||||
|
| SvgVisualizationData;
|
||||||
|
|
||||||
export function isCommonDataType<T>(
|
export type TreeVisualizationData = {
|
||||||
data: ExtractedData,
|
kind: {
|
||||||
kind: T
|
tree: true;
|
||||||
): data is Narrow<CommonDataType, { kind: T }> {
|
};
|
||||||
for (const key of Object.keys(kind)) {
|
root: TreeNode;
|
||||||
if (!(key in data.kind)) {
|
};
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Narrow<T, TKind> = T extends TKind ? T : never;
|
export type TreeNode = {
|
||||||
|
/**
|
||||||
export module CommonDataTypes {
|
* The children of this tree-node
|
||||||
export interface Text {
|
*/
|
||||||
kind: { text: true };
|
children: TreeNode[];
|
||||||
text: string;
|
/**
|
||||||
mimeType?: string;
|
* The parts that make up the text of this item
|
||||||
fileName?: string;
|
*/
|
||||||
}
|
items: TreeNodeItem[];
|
||||||
|
/**
|
||||||
export interface Graph {
|
* If a node is selected, the concatenation of all segment values from root to the selected node is shown to the user.
|
||||||
kind: { graph: true };
|
*/
|
||||||
nodes: NodeGraphData[];
|
segment?: string;
|
||||||
edges: EdgeGraphData[];
|
/**
|
||||||
}
|
* Marked nodes are highlighted and scrolled into view on every visualization update.
|
||||||
|
*/
|
||||||
export interface Svg extends Text {
|
|
||||||
kind: { text: true; svg: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Html extends Text {
|
|
||||||
kind: { text: true; html: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DotGraph extends Text {
|
|
||||||
kind: { text: true; dotGraph: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tree<TData = unknown> {
|
|
||||||
kind: { tree: true };
|
|
||||||
root: TreeNode<TData>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Grid {
|
|
||||||
kind: { array: true };
|
|
||||||
columnLabels?: { label?: string }[];
|
|
||||||
rows: {
|
|
||||||
label?: string;
|
|
||||||
columns: {
|
|
||||||
content?: string;
|
|
||||||
tag?: string;
|
|
||||||
color?: string;
|
|
||||||
}[];
|
|
||||||
}[];
|
|
||||||
markers?: {
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
row: number;
|
|
||||||
column: number;
|
|
||||||
rows?: number;
|
|
||||||
columns?: number;
|
|
||||||
|
|
||||||
label?: string;
|
|
||||||
color?: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Ast
|
|
||||||
extends Tree<{
|
|
||||||
position: number;
|
|
||||||
length: number;
|
|
||||||
}>,
|
|
||||||
Text {
|
|
||||||
kind: { text: true; tree: true; ast: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Plotly {
|
|
||||||
kind: { plotly: true };
|
|
||||||
data: Partial<Plotly.Data>[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TreeNode<TExtraData> {
|
|
||||||
name: string;
|
|
||||||
children: TreeNode<TExtraData>[];
|
|
||||||
data: TExtraData;
|
|
||||||
id?: string;
|
|
||||||
value?: string;
|
|
||||||
emphasizedValue?: string;
|
|
||||||
isMarked?: boolean;
|
isMarked?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface NodeGraphData {
|
export type TreeNodeItem = {
|
||||||
|
/**
|
||||||
|
* The text to show
|
||||||
|
*/
|
||||||
|
text: string;
|
||||||
|
/**
|
||||||
|
* The style of the text
|
||||||
|
*/
|
||||||
|
emphasis?: "style1" | "style2" | "style3" | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AstTreeVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
ast: true;
|
||||||
|
tree: true;
|
||||||
|
text: true;
|
||||||
|
};
|
||||||
|
root: AstTreeNode;
|
||||||
|
text: string;
|
||||||
|
fileName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AstTreeNode = {
|
||||||
|
children: AstTreeNode[];
|
||||||
|
items: AstTreeNodeItem[];
|
||||||
|
segment?: string;
|
||||||
|
isMarked?: boolean;
|
||||||
|
span: {
|
||||||
|
start: number;
|
||||||
|
length: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AstTreeNodeItem = {
|
||||||
|
text: string;
|
||||||
|
emphasis?: "style1" | "style2" | "style3" | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphvizDotVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
dotGraph: true;
|
||||||
|
};
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
graph: true;
|
||||||
|
};
|
||||||
|
nodes: GraphNode[];
|
||||||
|
edges: GraphEdge[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphNode = {
|
||||||
id: string;
|
id: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
shape?: "ellipse" | "box";
|
shape?: "ellipse" | "box";
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EdgeGraphData {
|
export type GraphEdge = {
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
dashes?: boolean;
|
dashes?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type GridVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
grid: true;
|
||||||
|
};
|
||||||
|
columnLabels?: {
|
||||||
|
label?: string;
|
||||||
|
}[];
|
||||||
|
rows: {
|
||||||
|
label?: string;
|
||||||
|
columns: {
|
||||||
|
content?: string;
|
||||||
|
/**
|
||||||
|
* A value to identify this cell. Should be unique.
|
||||||
|
*/
|
||||||
|
tag?: string;
|
||||||
|
color?: string;
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
markers?: {
|
||||||
|
id: string;
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
rows?: number;
|
||||||
|
columns?: number;
|
||||||
|
label?: string;
|
||||||
|
color?: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ImageVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
imagePng: true;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The base 64 encoded PNG representation of the image
|
||||||
|
*/
|
||||||
|
base64Data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MonacoTextVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
text: true;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The text to show
|
||||||
|
*/
|
||||||
|
text: string;
|
||||||
|
/**
|
||||||
|
* An optional filename that might be used for chosing a syntax highlighter
|
||||||
|
*/
|
||||||
|
fileName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
table: true;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* An array of objects. The properties of the objects are used as columns.
|
||||||
|
*/
|
||||||
|
rows: {}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlotlyVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
plotly: true;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Expecting Plotly.Data[] (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/795ce172038dbafcb9cba030d637d733a7eea19c/types/plotly.js/index.d.ts#L1036)
|
||||||
|
*/
|
||||||
|
data: {
|
||||||
|
text?: string | string[];
|
||||||
|
xaxis?: string;
|
||||||
|
yaxis?: string;
|
||||||
|
x?: (string | number | null)[] | (string | number | null)[][];
|
||||||
|
y?: (string | number | null)[] | (string | number | null)[][];
|
||||||
|
z?: (string | number | null)[] | (string | number | null)[][];
|
||||||
|
type?:
|
||||||
|
| "bar"
|
||||||
|
| "box"
|
||||||
|
| "candlestick"
|
||||||
|
| "choropleth"
|
||||||
|
| "contour"
|
||||||
|
| "heatmap"
|
||||||
|
| "histogram"
|
||||||
|
| "indicator"
|
||||||
|
| "mesh3d"
|
||||||
|
| "ohlc"
|
||||||
|
| "parcoords"
|
||||||
|
| "pie"
|
||||||
|
| "pointcloud"
|
||||||
|
| "scatter"
|
||||||
|
| "scatter3d"
|
||||||
|
| "scattergeo"
|
||||||
|
| "scattergl"
|
||||||
|
| "scatterpolar"
|
||||||
|
| "scatterternary"
|
||||||
|
| "sunburst"
|
||||||
|
| "surface"
|
||||||
|
| "treemap"
|
||||||
|
| "waterfall"
|
||||||
|
| "funnel"
|
||||||
|
| "funnelarea"
|
||||||
|
| "scattermapbox";
|
||||||
|
mode?:
|
||||||
|
| "lines"
|
||||||
|
| "markers"
|
||||||
|
| "text"
|
||||||
|
| "lines+markers"
|
||||||
|
| "text+markers"
|
||||||
|
| "text+lines"
|
||||||
|
| "text+lines+markers"
|
||||||
|
| "none"
|
||||||
|
| "gauge"
|
||||||
|
| "number"
|
||||||
|
| "delta"
|
||||||
|
| "number+delta"
|
||||||
|
| "gauge+number"
|
||||||
|
| "gauge+number+delta"
|
||||||
|
| "gauge+delta";
|
||||||
|
}[];
|
||||||
|
/**
|
||||||
|
* Expecting Partial<Plotly.Layout> (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/795ce172038dbafcb9cba030d637d733a7eea19c/types/plotly.js/index.d.ts#L329)
|
||||||
|
*/
|
||||||
|
layout?: {
|
||||||
|
title?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SimpleTextVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
text: true;
|
||||||
|
};
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SvgVisualizationData = {
|
||||||
|
kind: {
|
||||||
|
svg: true;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The svg content
|
||||||
|
*/
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
export type DataExtractionResult = {
|
export interface DataExtractionResult {
|
||||||
data: ExtractedData;
|
data: VisualizationData;
|
||||||
usedExtractor: DataExtractorInfo;
|
usedExtractor: DataExtractorInfo;
|
||||||
availableExtractors: DataExtractorInfo[];
|
availableExtractors: DataExtractorInfo[];
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances must be valid json values.
|
* Instances must be valid json values.
|
||||||
*/
|
*/
|
||||||
export type ExtractedData = {
|
export interface VisualizationData {
|
||||||
kind: Record<string, true>;
|
kind: Record<string, true>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type DataExtractorInfo = {
|
export interface DataExtractorInfo {
|
||||||
id: DataExtractorId;
|
id: DataExtractorId;
|
||||||
name: string;
|
name: string;
|
||||||
priority: number;
|
priority: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type DataExtractorId = {
|
export type DataExtractorId = {
|
||||||
__brand: "DataExtractorId";
|
__brand: "DataExtractorId";
|
||||||
} & string;
|
} & string;
|
||||||
|
|
||||||
export function isExtractedData(val: unknown): val is ExtractedData {
|
export function isVisualizationData(val: unknown): val is VisualizationData {
|
||||||
if (typeof val !== "object" || !val || !("kind" in val)) {
|
if (typeof val !== "object" || !val || !("kind" in val)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
ExtractedData,
|
VisualizationData,
|
||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
} from "../../DataExtractionResult";
|
} from "../../DataExtractionResult";
|
||||||
|
|
||||||
@@ -7,14 +7,12 @@ export interface DataExtractorApi {
|
|||||||
/**
|
/**
|
||||||
* Registers a single extractor.
|
* Registers a single extractor.
|
||||||
*/
|
*/
|
||||||
registerExtractor<TExtractedData extends ExtractedData>(
|
registerExtractor(extractor: DataExtractor): void;
|
||||||
extractor: DataExtractor<TExtractedData>
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers multiple extractors.
|
* Registers multiple extractors.
|
||||||
*/
|
*/
|
||||||
registerExtractors(extractors: DataExtractor<ExtractedData>[]): void;
|
registerExtractors(extractors: DataExtractor[]): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts data from the result of `valueFn`.
|
* Extracts data from the result of `valueFn`.
|
||||||
@@ -46,23 +44,23 @@ export interface JSONString<T> extends String {
|
|||||||
__brand: { json: T };
|
__brand: { json: T };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataExtractor<T extends ExtractedData> {
|
export interface DataExtractor {
|
||||||
/**
|
/**
|
||||||
* Must be unique among all data extractors.
|
* Must be unique among all data extractors.
|
||||||
*/
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
extractionCollector: ExtractionCollector<T>,
|
extractionCollector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtractionCollector<T extends ExtractedData> {
|
export interface ExtractionCollector {
|
||||||
/**
|
/**
|
||||||
* Suggests a possible extraction.
|
* Suggests a possible extraction.
|
||||||
*/
|
*/
|
||||||
addExtraction(extraction: DataExtraction<T>): void;
|
addExtraction(extraction: DataExtraction): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataExtractorContext {
|
export interface DataExtractorContext {
|
||||||
@@ -72,7 +70,7 @@ export interface DataExtractorContext {
|
|||||||
evalFn: <TEval>(expression: string) => TEval;
|
evalFn: <TEval>(expression: string) => TEval;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataExtraction<TData extends ExtractedData> {
|
export interface DataExtraction {
|
||||||
/**
|
/**
|
||||||
* Higher priorities are preferred.
|
* Higher priorities are preferred.
|
||||||
*/
|
*/
|
||||||
@@ -87,5 +85,5 @@ export interface DataExtraction<TData extends ExtractedData> {
|
|||||||
* A user friendly name of this extraction.
|
* A user friendly name of this extraction.
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
extractData(): TData;
|
extractData(): VisualizationData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
DataExtraction,
|
DataExtraction,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
} from "./DataExtractorApi";
|
} from "./DataExtractorApi";
|
||||||
import { ExtractedData, DataExtractorInfo } from "../../DataExtractionResult";
|
import { DataExtractorInfo } from "../../DataExtractionResult";
|
||||||
import { registerDefaultExtractors } from "./default-extractors";
|
import { registerDefaultExtractors } from "./default-extractors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,24 +15,17 @@ import { registerDefaultExtractors } from "./default-extractors";
|
|||||||
export class DataExtractorApiImpl implements DataExtractorApi {
|
export class DataExtractorApiImpl implements DataExtractorApi {
|
||||||
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
|
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
|
||||||
|
|
||||||
private readonly extractors = new Map<
|
private readonly extractors = new Map<string, DataExtractor>();
|
||||||
string,
|
|
||||||
DataExtractor<ExtractedData>
|
|
||||||
>();
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerExtractor<TExtractedData extends ExtractedData>(
|
public registerExtractor(extractor: DataExtractor): void {
|
||||||
extractor: DataExtractor<TExtractedData>
|
|
||||||
): void {
|
|
||||||
this.extractors.set(extractor.id, extractor);
|
this.extractors.set(extractor.id, extractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerExtractors(
|
public registerExtractors(extractors: DataExtractor[]): void {
|
||||||
extractors: DataExtractor<ExtractedData>[]
|
|
||||||
): void {
|
|
||||||
for (const e of extractors) {
|
for (const e of extractors) {
|
||||||
this.registerExtractor(e);
|
this.registerExtractor(e);
|
||||||
}
|
}
|
||||||
@@ -43,8 +36,8 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
evalFn: <T>(expression: string) => T,
|
evalFn: <T>(expression: string) => T,
|
||||||
preferredDataExtractorId: string | undefined
|
preferredDataExtractorId: string | undefined
|
||||||
): JSONString<DataResult> {
|
): JSONString<DataResult> {
|
||||||
const extractions = new Array<DataExtraction<ExtractedData>>();
|
const extractions = new Array<DataExtraction>();
|
||||||
const extractionCollector: ExtractionCollector<ExtractedData> = {
|
const extractionCollector: ExtractionCollector = {
|
||||||
addExtraction(extraction) {
|
addExtraction(extraction) {
|
||||||
extractions.push(extraction);
|
extractions.push(extraction);
|
||||||
},
|
},
|
||||||
@@ -72,9 +65,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapExtractor(
|
function mapExtractor(e: DataExtraction): DataExtractorInfo {
|
||||||
e: DataExtraction<ExtractedData>
|
|
||||||
): DataExtractorInfo {
|
|
||||||
return {
|
return {
|
||||||
id: e.id as any,
|
id: e.id as any,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { ExtractedData, isExtractedData } from "../../../DataExtractionResult";
|
import { isVisualizationData } from "../../../DataExtractionResult";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../..";
|
} from "../..";
|
||||||
|
|
||||||
export class AsIsDataExtractor implements DataExtractor<ExtractedData> {
|
export class AsIsDataExtractor implements DataExtractor {
|
||||||
readonly id = "as-is";
|
readonly id = "as-is";
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
extractionCollector: ExtractionCollector<ExtractedData>,
|
extractionCollector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
if (!isExtractedData(data)) {
|
if (!isVisualizationData(data)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
import { ExtractedData } from "../../../DataExtractionResult";
|
import { VisualizationData } from "../../../DataExtractionResult";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../DataExtractorApi";
|
} from "../DataExtractorApi";
|
||||||
|
|
||||||
export class GetDebugVisualizationDataExtractor
|
export class GetVisualizationDataExtractor implements DataExtractor {
|
||||||
implements DataExtractor<ExtractedData> {
|
readonly id = "get-visualization-data";
|
||||||
readonly id = "get-debug-visualization";
|
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
collector: ExtractionCollector<ExtractedData>,
|
collector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
if (
|
if (typeof data !== "object" || !data) {
|
||||||
typeof data !== "object" ||
|
return;
|
||||||
!data ||
|
}
|
||||||
!("getDebugVisualization" in data)
|
|
||||||
) {
|
const getVisualizationData = (data as any)
|
||||||
|
.getVisualizationData as Function;
|
||||||
|
if (typeof getVisualizationData !== "function") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collector.addExtraction({
|
collector.addExtraction({
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: "Use Method 'getDebugVisualization'",
|
name: "Use Method 'getVisualizationData'",
|
||||||
priority: 600,
|
priority: 600,
|
||||||
extractData() {
|
extractData() {
|
||||||
return (data as any).getDebugVisualization();
|
return getVisualizationData.apply(data);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { ExtractedData } from "../../../DataExtractionResult";
|
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../..";
|
} from "../..";
|
||||||
import { CommonDataTypes } from "../../../CommonDataTypes";
|
import { GridVisualizationData } from "../../../CommonDataTypes";
|
||||||
|
import { expect } from "../../../util";
|
||||||
|
|
||||||
export class GridExtractor implements DataExtractor<ExtractedData> {
|
export class GridExtractor implements DataExtractor {
|
||||||
readonly id = "grid";
|
readonly id = "grid";
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
extractionCollector: ExtractionCollector<ExtractedData>,
|
extractionCollector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
@@ -21,12 +21,11 @@ export class GridExtractor implements DataExtractor<ExtractedData> {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
name: "Array As Grid",
|
name: "Array As Grid",
|
||||||
priority: 500,
|
priority: 500,
|
||||||
extractData(): CommonDataTypes.Grid {
|
extractData: () =>
|
||||||
return {
|
expect<GridVisualizationData>({
|
||||||
kind: { array: true },
|
kind: { grid: true },
|
||||||
rows: [{ columns: data.map(d => ({ tag: d })) }],
|
rows: [{ columns: data.map(d => ({ tag: d })) }],
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExtractedData } from "../../../DataExtractionResult";
|
import { VisualizationData } from "../../../DataExtractionResult";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
} from "../DataExtractorApi";
|
} from "../DataExtractorApi";
|
||||||
import { createGraph, CreateGraphEdge } from "../../helpers";
|
import { createGraph, CreateGraphEdge } from "../../helpers";
|
||||||
|
|
||||||
export class ObjectGraphExtractor implements DataExtractor<ExtractedData> {
|
export class ObjectGraphExtractor implements DataExtractor {
|
||||||
readonly id = "object-graph";
|
readonly id = "object-graph";
|
||||||
|
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
collector: ExtractionCollector<ExtractedData>,
|
collector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
function isObject(val: unknown): val is object {
|
function isObject(val: unknown): val is object {
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { ExtractedData } from "../../../DataExtractionResult";
|
import { PlotlyVisualizationData } from "../../../CommonDataTypes";
|
||||||
|
import { expect } from "../../../util";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../DataExtractorApi";
|
} from "../DataExtractorApi";
|
||||||
import { CommonDataTypes } from "../../../CommonDataTypes";
|
|
||||||
|
|
||||||
export class PlotDataExtractor implements DataExtractor<ExtractedData> {
|
export class PlotlyDataExtractor implements DataExtractor {
|
||||||
readonly id = "plot";
|
readonly id = "plot";
|
||||||
|
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
collector: ExtractionCollector<ExtractedData>,
|
collector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
@@ -25,14 +25,13 @@ export class PlotDataExtractor implements DataExtractor<ExtractedData> {
|
|||||||
id: "plot-y",
|
id: "plot-y",
|
||||||
name: "Plot as y-Values",
|
name: "Plot as y-Values",
|
||||||
priority: 1001,
|
priority: 1001,
|
||||||
extractData() {
|
extractData: () =>
|
||||||
return {
|
expect<PlotlyVisualizationData>({
|
||||||
kind: {
|
kind: {
|
||||||
plotly: true,
|
plotly: true,
|
||||||
},
|
},
|
||||||
data: [{ y: data }],
|
data: [{ y: data }],
|
||||||
} as CommonDataTypes.Plotly;
|
}),
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { ExtractedData } from "../../../DataExtractionResult";
|
import { MonacoTextVisualizationData } from "../../../CommonDataTypes";
|
||||||
|
import { expect } from "../../../util";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../DataExtractorApi";
|
} from "../DataExtractorApi";
|
||||||
|
|
||||||
export class ToStringDataExtractor implements DataExtractor<ExtractedData> {
|
export class ToStringDataExtractor implements DataExtractor {
|
||||||
readonly id = "to-string";
|
readonly id = "to-string";
|
||||||
|
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
collector: ExtractionCollector<ExtractedData>,
|
collector: ExtractionCollector,
|
||||||
context: DataExtractorContext
|
context: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
collector.addExtraction({
|
collector.addExtraction({
|
||||||
@@ -18,12 +19,12 @@ export class ToStringDataExtractor implements DataExtractor<ExtractedData> {
|
|||||||
name: "To String",
|
name: "To String",
|
||||||
priority: 100,
|
priority: 100,
|
||||||
extractData() {
|
extractData() {
|
||||||
return {
|
return expect<MonacoTextVisualizationData>({
|
||||||
kind: {
|
kind: {
|
||||||
text: true,
|
text: true,
|
||||||
},
|
},
|
||||||
text: "" + data,
|
text: "" + data,
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import * as ts from "typescript"; // Only compile-time import!
|
import * as ts from "typescript"; // Only compile-time import!
|
||||||
|
import { AstTreeNode } from "../../..";
|
||||||
|
import { AstTreeVisualizationData } from "../../../CommonDataTypes";
|
||||||
|
import { expect } from "../../../util";
|
||||||
import {
|
import {
|
||||||
DataExtractor,
|
DataExtractor,
|
||||||
ExtractionCollector,
|
ExtractionCollector,
|
||||||
DataExtractorContext,
|
DataExtractorContext,
|
||||||
} from "../DataExtractorApi";
|
} from "../DataExtractorApi";
|
||||||
import { CommonDataTypes } from "../../../CommonDataTypes";
|
|
||||||
|
|
||||||
// This class is self contained and can be injected into both nodejs and browser environments.
|
export class TypeScriptAstDataExtractor implements DataExtractor {
|
||||||
export class TypeScriptAstDataExtractor
|
|
||||||
implements DataExtractor<CommonDataTypes.Ast> {
|
|
||||||
readonly id = "typescript-ast";
|
readonly id = "typescript-ast";
|
||||||
|
|
||||||
getExtractions(
|
getExtractions(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
collector: ExtractionCollector<CommonDataTypes.Ast>,
|
collector: ExtractionCollector,
|
||||||
{ evalFn }: DataExtractorContext
|
{ evalFn }: DataExtractorContext
|
||||||
): void {
|
): void {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -40,114 +40,21 @@ export class TypeScriptAstDataExtractor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findKey(value: any, object: any): string | null {
|
const helper = new Helper(tsApi);
|
||||||
for (var key in object) {
|
|
||||||
if (key.startsWith("_")) continue;
|
|
||||||
|
|
||||||
var member = object[key];
|
|
||||||
if (member === value) return key;
|
|
||||||
|
|
||||||
if (Array.isArray(member) && member.indexOf(value) !== -1) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getChildren(node: ts.Node): ts.Node[] {
|
|
||||||
const result = new Array<ts.Node>();
|
|
||||||
tsApi.forEachChild(node, n => {
|
|
||||||
result.push(n);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toTreeNode(
|
|
||||||
node: ts.Node,
|
|
||||||
memberName: string,
|
|
||||||
marked: Set<ts.Node>,
|
|
||||||
emphasizedValueFn: (node: ts.Node) => string | undefined
|
|
||||||
): CommonDataTypes.Ast["root"] {
|
|
||||||
const name = tsApi.SyntaxKind[node.kind];
|
|
||||||
const children = getChildren(node)
|
|
||||||
.map((childNode, idx) => {
|
|
||||||
let parentPropertyName = findKey(childNode, node) || "";
|
|
||||||
if (childNode.kind == tsApi.SyntaxKind.SyntaxList) {
|
|
||||||
const children = getChildren(childNode);
|
|
||||||
children.some(c => {
|
|
||||||
parentPropertyName = findKey(c, node) || "";
|
|
||||||
return !!parentPropertyName;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (children.length === 0) return null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.kind == tsApi.SyntaxKind.SyntaxList) {
|
|
||||||
parentPropertyName = "" + idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
return toTreeNode(
|
|
||||||
childNode,
|
|
||||||
parentPropertyName,
|
|
||||||
marked,
|
|
||||||
emphasizedValueFn
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.filter(c => c !== null);
|
|
||||||
|
|
||||||
let value: string | undefined = undefined;
|
|
||||||
|
|
||||||
if (tsApi.isIdentifier(node)) {
|
|
||||||
value = node.text || (node.escapedText as string);
|
|
||||||
} else if (tsApi.isLiteralExpression(node)) {
|
|
||||||
value = node.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: name,
|
|
||||||
id: memberName,
|
|
||||||
children: children,
|
|
||||||
data: {
|
|
||||||
length: node.end - node.pos,
|
|
||||||
position: node.pos,
|
|
||||||
},
|
|
||||||
emphasizedValue: emphasizedValueFn(node),
|
|
||||||
isMarked: marked.has(node),
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNode(node: unknown): node is ts.Node {
|
|
||||||
return (
|
|
||||||
typeof node === "object" &&
|
|
||||||
node !== null &&
|
|
||||||
(tsApi.isToken(node as any) || (tsApi as any).isNode(node))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSourceFile(node: ts.Node | any): ts.SourceFile {
|
|
||||||
if (!node) {
|
|
||||||
throw new Error("Detached node");
|
|
||||||
}
|
|
||||||
if (tsApi.isSourceFile(node)) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
if (!("getSourceFile" in node)) {
|
|
||||||
return getSourceFile(node.parent);
|
|
||||||
}
|
|
||||||
return node.getSourceFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
let rootSourceFile: ts.SourceFile | undefined = undefined;
|
let rootSourceFile: ts.SourceFile | undefined = undefined;
|
||||||
let rootNode: ts.Node | undefined = undefined;
|
let rootNode: ts.Node | undefined = undefined;
|
||||||
let marked: Set<ts.Node>;
|
let marked: Set<ts.Node>;
|
||||||
let fn: (n: ts.Node) => string | undefined = (n: ts.Node) => undefined;
|
let fn: (n: ts.Node) => string | undefined = (n: ts.Node) => undefined;
|
||||||
if (Array.isArray(data) && data.every(isNode) && data.length > 0) {
|
if (
|
||||||
rootSourceFile = getSourceFile(data[0] as ts.Node);
|
Array.isArray(data) &&
|
||||||
|
data.every(helper.isNode) &&
|
||||||
|
data.length > 0
|
||||||
|
) {
|
||||||
|
rootSourceFile = helper.getSourceFile(data[0] as ts.Node);
|
||||||
marked = new Set(data);
|
marked = new Set(data);
|
||||||
} else if (isNode(data)) {
|
} else if (helper.isNode(data)) {
|
||||||
rootSourceFile = getSourceFile(data);
|
rootSourceFile = helper.getSourceFile(data);
|
||||||
marked = new Set([data]);
|
marked = new Set([data]);
|
||||||
} else if (typeof data === "object" && data) {
|
} else if (typeof data === "object" && data) {
|
||||||
marked = new Set();
|
marked = new Set();
|
||||||
@@ -162,15 +69,18 @@ export class TypeScriptAstDataExtractor
|
|||||||
rootNode = item;
|
rootNode = item;
|
||||||
}
|
}
|
||||||
let nodes: Array<ts.Node>;
|
let nodes: Array<ts.Node>;
|
||||||
if (isNode(item)) {
|
if (helper.isNode(item)) {
|
||||||
nodes = [item];
|
nodes = [item];
|
||||||
} else if (Array.isArray(item) && item.every(isNode)) {
|
} else if (
|
||||||
|
Array.isArray(item) &&
|
||||||
|
item.every(helper.isNode)
|
||||||
|
) {
|
||||||
nodes = item;
|
nodes = item;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nodes.length > 0 && !rootSourceFile) {
|
if (nodes.length > 0 && !rootSourceFile) {
|
||||||
rootSourceFile = getSourceFile(nodes[0]);
|
rootSourceFile = helper.getSourceFile(nodes[0]);
|
||||||
}
|
}
|
||||||
for (const n of nodes) {
|
for (const n of nodes) {
|
||||||
marked.add(n);
|
marked.add(n);
|
||||||
@@ -192,9 +102,9 @@ export class TypeScriptAstDataExtractor
|
|||||||
name: "TypeScript AST",
|
name: "TypeScript AST",
|
||||||
priority: 1000,
|
priority: 1000,
|
||||||
extractData() {
|
extractData() {
|
||||||
return {
|
return expect<AstTreeVisualizationData>({
|
||||||
kind: { text: true, tree: true, ast: true },
|
kind: { text: true, tree: true, ast: true },
|
||||||
root: toTreeNode(
|
root: helper.toTreeNode(
|
||||||
rootNode || finalRootSourceFile,
|
rootNode || finalRootSourceFile,
|
||||||
"root",
|
"root",
|
||||||
marked,
|
marked,
|
||||||
@@ -202,8 +112,122 @@ export class TypeScriptAstDataExtractor
|
|||||||
),
|
),
|
||||||
text: finalRootSourceFile.text,
|
text: finalRootSourceFile.text,
|
||||||
fileName: "index.ts",
|
fileName: "index.ts",
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Helper {
|
||||||
|
constructor(private readonly tsApi: typeof ts) {}
|
||||||
|
|
||||||
|
findKey(value: any, object: any): string | null {
|
||||||
|
for (var key in object) {
|
||||||
|
if (key.startsWith("_")) continue;
|
||||||
|
|
||||||
|
var member = object[key];
|
||||||
|
if (member === value) return key;
|
||||||
|
|
||||||
|
if (Array.isArray(member) && member.indexOf(value) !== -1) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren(node: ts.Node): ts.Node[] {
|
||||||
|
const result = new Array<ts.Node>();
|
||||||
|
this.tsApi.forEachChild(node, n => {
|
||||||
|
result.push(n);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
toTreeNode(
|
||||||
|
node: ts.Node,
|
||||||
|
memberName: string,
|
||||||
|
marked: Set<ts.Node>,
|
||||||
|
emphasizedValueFn: (node: ts.Node) => string | undefined
|
||||||
|
): AstTreeNode {
|
||||||
|
const name = this.tsApi.SyntaxKind[node.kind];
|
||||||
|
const children = this.getChildren(node)
|
||||||
|
.map((childNode, idx) => {
|
||||||
|
let parentPropertyName = this.findKey(childNode, node) || "";
|
||||||
|
if (childNode.kind == this.tsApi.SyntaxKind.SyntaxList) {
|
||||||
|
const children = this.getChildren(childNode);
|
||||||
|
children.some(c => {
|
||||||
|
parentPropertyName = this.findKey(c, node) || "";
|
||||||
|
return !!parentPropertyName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (children.length === 0) return null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.kind == this.tsApi.SyntaxKind.SyntaxList) {
|
||||||
|
parentPropertyName = "" + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.toTreeNode(
|
||||||
|
childNode,
|
||||||
|
parentPropertyName,
|
||||||
|
marked,
|
||||||
|
emphasizedValueFn
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter(c => c !== null);
|
||||||
|
|
||||||
|
let value: string | undefined = undefined;
|
||||||
|
|
||||||
|
if (this.tsApi.isIdentifier(node)) {
|
||||||
|
value = node.text || (node.escapedText as string);
|
||||||
|
} else if (this.tsApi.isLiteralExpression(node)) {
|
||||||
|
value = node.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: AstTreeNode["items"] = [
|
||||||
|
{ text: `${memberName}: `, emphasis: "style1" },
|
||||||
|
{ text: name },
|
||||||
|
];
|
||||||
|
|
||||||
|
const emphasizedVal = emphasizedValueFn(node);
|
||||||
|
if (value) {
|
||||||
|
items.push({ text: value, emphasis: "style2" });
|
||||||
|
}
|
||||||
|
if (emphasizedVal) {
|
||||||
|
items.push({ text: emphasizedVal, emphasis: "style3" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
children: children,
|
||||||
|
span: {
|
||||||
|
length: node.end - node.pos,
|
||||||
|
start: node.pos,
|
||||||
|
},
|
||||||
|
isMarked: marked.has(node),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
isNode = (node: unknown): node is ts.Node => {
|
||||||
|
return (
|
||||||
|
typeof node === "object" &&
|
||||||
|
node !== null &&
|
||||||
|
(this.tsApi.isToken(node as any) ||
|
||||||
|
(this.tsApi as any).isNode(node))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
getSourceFile(node: ts.Node | any): ts.SourceFile {
|
||||||
|
if (!node) {
|
||||||
|
throw new Error("Detached node");
|
||||||
|
}
|
||||||
|
if (this.tsApi.isSourceFile(node)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (!("getSourceFile" in node)) {
|
||||||
|
return this.getSourceFile(node.parent);
|
||||||
|
}
|
||||||
|
return node.getSourceFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { DataExtractorApi } from "../DataExtractorApi";
|
import { DataExtractorApi } from "../DataExtractorApi";
|
||||||
import { TypeScriptAstDataExtractor } from "./TypeScriptDataExtractors";
|
import { TypeScriptAstDataExtractor } from "./TypeScriptDataExtractors";
|
||||||
import { AsIsDataExtractor } from "./AsIsDataExtractor";
|
import { AsIsDataExtractor } from "./AsIsDataExtractor";
|
||||||
import { GetDebugVisualizationDataExtractor } from "./GetDebugVisualizationDataExtractor";
|
import { GetVisualizationDataExtractor } from "./GetDebugVisualizationDataExtractor";
|
||||||
import { ToStringDataExtractor } from "./ToStringExtractor";
|
import { ToStringDataExtractor } from "./ToStringExtractor";
|
||||||
import { PlotDataExtractor } from "./PlotlyDataExtractor";
|
import { PlotlyDataExtractor } from "./PlotlyDataExtractor";
|
||||||
import { ObjectGraphExtractor } from "./ObjectGraphExtractor";
|
import { ObjectGraphExtractor } from "./ObjectGraphExtractor";
|
||||||
import { getDataExtractorApi } from "../injection";
|
import { getDataExtractorApi } from "../injection";
|
||||||
import { GridExtractor } from "./GridExtractor";
|
import { GridExtractor } from "./GridExtractor";
|
||||||
@@ -18,9 +18,9 @@ export function registerDefaultExtractors(
|
|||||||
for (const item of [
|
for (const item of [
|
||||||
new TypeScriptAstDataExtractor(),
|
new TypeScriptAstDataExtractor(),
|
||||||
new AsIsDataExtractor(),
|
new AsIsDataExtractor(),
|
||||||
new GetDebugVisualizationDataExtractor(),
|
new GetVisualizationDataExtractor(),
|
||||||
new ToStringDataExtractor(),
|
new ToStringDataExtractor(),
|
||||||
new PlotDataExtractor(),
|
new PlotlyDataExtractor(),
|
||||||
new ObjectGraphExtractor(),
|
new ObjectGraphExtractor(),
|
||||||
new GridExtractor(),
|
new GridExtractor(),
|
||||||
]) {
|
]) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
CommonDataTypes,
|
GraphEdge,
|
||||||
EdgeGraphData,
|
GraphNode,
|
||||||
NodeGraphData,
|
GraphVisualizationData,
|
||||||
} from "../../CommonDataTypes";
|
} from "../../CommonDataTypes";
|
||||||
|
|
||||||
export type CreateGraphEdge<T> = { to: T } & Omit<EdgeGraphData, "from" | "to">;
|
export type CreateGraphEdge<T> = { to: T } & Omit<GraphEdge, "from" | "to">;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of roots, it creates a graph by following their edges recursively.
|
* Given a list of roots, it creates a graph by following their edges recursively.
|
||||||
@@ -17,10 +17,10 @@ export function createGraph<T>(
|
|||||||
) => {
|
) => {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
edges: CreateGraphEdge<T>[];
|
edges: CreateGraphEdge<T>[];
|
||||||
} & Omit<NodeGraphData, "id">,
|
} & Omit<GraphNode, "id">,
|
||||||
options: { maxSize?: number } = {}
|
options: { maxSize?: number } = {}
|
||||||
): CommonDataTypes.Graph {
|
): GraphVisualizationData {
|
||||||
const r: CommonDataTypes.Graph = {
|
const r: GraphVisualizationData = {
|
||||||
kind: {
|
kind: {
|
||||||
graph: true,
|
graph: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NodeGraphData, CommonDataTypes } from "../../CommonDataTypes";
|
import { GraphNode, GraphVisualizationData } from "../../CommonDataTypes";
|
||||||
import { CreateGraphEdge, createGraph } from "./createGraph";
|
import { CreateGraphEdge, createGraph } from "./createGraph";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,8 +12,8 @@ export function createGraphFromPointers<T>(
|
|||||||
) => {
|
) => {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
edges: CreateGraphEdge<T>[];
|
edges: CreateGraphEdge<T>[];
|
||||||
} & Omit<NodeGraphData, "id">
|
} & Omit<GraphNode, "id">
|
||||||
): CommonDataTypes.Graph {
|
): GraphVisualizationData {
|
||||||
const marker = {};
|
const marker = {};
|
||||||
|
|
||||||
interface Pointer {
|
interface Pointer {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { CommonDataTypes } from "../../CommonDataTypes";
|
import { GridVisualizationData } from "../../CommonDataTypes";
|
||||||
|
|
||||||
export function markedGrid(
|
export function markedGrid(
|
||||||
arr: any[],
|
arr: any[],
|
||||||
marked: Record<string, number>
|
marked: Record<string, number>
|
||||||
): CommonDataTypes.Grid {
|
): GridVisualizationData {
|
||||||
return {
|
return {
|
||||||
kind: { array: true },
|
kind: { grid: true },
|
||||||
rows: [{ columns: arr.map(d => ({ tag: d })) }],
|
rows: [{ columns: arr.map(d => ({ tag: d })) }],
|
||||||
markers: Object.entries(marked).map(([key, val]) => ({
|
markers: Object.entries(marked).map(([key, val]) => ({
|
||||||
id: key,
|
id: key,
|
||||||
|
|||||||
3
data-extraction/src/util.ts
Normal file
3
data-extraction/src/util.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function expect<T>(data: T): T {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@hediet/live-debug@^0.5.1", "@hediet/live-debug@^0.5.2":
|
"@hediet/live-debug@^0.5.1":
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/live-debug/-/live-debug-0.5.2.tgz#f2ee0034f014836bfd68e26c35a168ab42b15486"
|
resolved "https://registry.yarnpkg.com/@hediet/live-debug/-/live-debug-0.5.2.tgz#f2ee0034f014836bfd68e26c35a168ab42b15486"
|
||||||
integrity sha512-HBqVbyFez1oQlIPO9dzkFi7/9Gvt6ZpssgURzYaVFREl9Oq03nPd6V7Ay4bW03xFHPo3YT2Yff9mFmGQUX7JEg==
|
integrity sha512-HBqVbyFez1oQlIPO9dzkFi7/9Gvt6ZpssgURzYaVFREl9Oq03nPd6V7Ay4bW03xFHPo3YT2Yff9mFmGQUX7JEg==
|
||||||
@@ -13,30 +13,21 @@
|
|||||||
stacktracey "^1.2.112"
|
stacktracey "^1.2.112"
|
||||||
ws "^7.0.0"
|
ws "^7.0.0"
|
||||||
|
|
||||||
"@hediet/node-reload@0.7.0":
|
"@hediet/node-reload@0.7.3":
|
||||||
version "0.7.0"
|
version "0.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/node-reload/-/node-reload-0.7.0.tgz#ec50160ce26203079f18e7024cd58025fa79652a"
|
resolved "https://registry.yarnpkg.com/@hediet/node-reload/-/node-reload-0.7.3.tgz#305d68f54185200b018b14f257037ab8d8ca9857"
|
||||||
integrity sha512-gWFbq2tOThSV9GT+pFx1JJzFuOV7l40IsseOFHMJGyvLmmAdgbJPV3OZuV9gcsXIGocOtbfEyky9S4tQELLLxA==
|
integrity sha512-icOyfDpArfvqmw0VLf2CoGR/jCSuWk3fScJGFuXQHpb0c9yNoGTiuG3Q8L2iJIuDzCjRu47DR2T+yCvZsY/f+w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hediet/live-debug" "^0.5.1"
|
"@hediet/live-debug" "^0.5.1"
|
||||||
"@hediet/std" "^0.6.0"
|
"@hediet/std" "^0.6.0"
|
||||||
"@hediet/typed-json-rpc" "^0.7.6"
|
"@hediet/typed-json-rpc" "^0.7.6"
|
||||||
"@types/node" "^12.0.7"
|
"@types/node" "^12.0.7"
|
||||||
"@types/stack-trace" "^0.0.29"
|
"@types/stack-trace" "^0.0.29"
|
||||||
|
chalk "^2.4.2"
|
||||||
chokidar "^3.0.1"
|
chokidar "^3.0.1"
|
||||||
stack-trace "^0.0.10"
|
stack-trace "^0.0.10"
|
||||||
ws "^7.0.0"
|
ws "^7.0.0"
|
||||||
|
|
||||||
"@hediet/std@^0.4.0":
|
|
||||||
version "0.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/std/-/std-0.4.0.tgz#95dfd6e9d62f8bf9d91f3925d37b7b359b2af047"
|
|
||||||
integrity sha512-2kw4vSOprLchTjhcg8qQo6fdbBxJicSUrzZXgL0KzgMv18hjVmpm57NPLCPfI6115kAaMIWvMnlHFLYbx7poQQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/chai" "^4.1.7"
|
|
||||||
"@types/sinon" "^7.0.10"
|
|
||||||
chai "^4.2.0"
|
|
||||||
sinon "^7.2.7"
|
|
||||||
|
|
||||||
"@hediet/std@^0.6.0":
|
"@hediet/std@^0.6.0":
|
||||||
version "0.6.0"
|
version "0.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hediet/std/-/std-0.6.0.tgz#f7d2ffc1180ecef6ff272d0d6780f0c0d9337e4e"
|
resolved "https://registry.yarnpkg.com/@hediet/std/-/std-0.6.0.tgz#f7d2ffc1180ecef6ff272d0d6780f0c0d9337e4e"
|
||||||
@@ -109,6 +100,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c"
|
||||||
integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==
|
integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==
|
||||||
|
|
||||||
|
"@types/node@^13.7.4":
|
||||||
|
version "13.13.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766"
|
||||||
|
integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw==
|
||||||
|
|
||||||
"@types/node@^8.0.7":
|
"@types/node@^8.0.7":
|
||||||
version "8.10.51"
|
version "8.10.51"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.51.tgz#80600857c0a47a8e8bafc2dae6daed6db58e3627"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.51.tgz#80600857c0a47a8e8bafc2dae6daed6db58e3627"
|
||||||
@@ -132,6 +128,13 @@
|
|||||||
"@types/events" "*"
|
"@types/events" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
ansi-styles@^3.2.1:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||||
|
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||||
|
dependencies:
|
||||||
|
color-convert "^1.9.0"
|
||||||
|
|
||||||
anymatch@^3.0.1:
|
anymatch@^3.0.1:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09"
|
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09"
|
||||||
@@ -186,6 +189,15 @@ chai@^4.2.0:
|
|||||||
pathval "^1.1.0"
|
pathval "^1.1.0"
|
||||||
type-detect "^4.0.5"
|
type-detect "^4.0.5"
|
||||||
|
|
||||||
|
chalk@^2.4.2:
|
||||||
|
version "2.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||||
|
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^3.2.1"
|
||||||
|
escape-string-regexp "^1.0.5"
|
||||||
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
check-error@^1.0.2:
|
check-error@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||||
@@ -206,6 +218,18 @@ chokidar@^3.0.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "^2.0.6"
|
fsevents "^2.0.6"
|
||||||
|
|
||||||
|
color-convert@^1.9.0:
|
||||||
|
version "1.9.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
|
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||||
|
dependencies:
|
||||||
|
color-name "1.1.3"
|
||||||
|
|
||||||
|
color-name@1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||||
|
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||||
|
|
||||||
data-uri-to-buffer@^2.0.0:
|
data-uri-to-buffer@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz#ca8f56fe38b1fd329473e9d1b4a9afcd8ce1c045"
|
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz#ca8f56fe38b1fd329473e9d1b4a9afcd8ce1c045"
|
||||||
@@ -225,6 +249,11 @@ diff@^3.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||||
|
|
||||||
|
escape-string-regexp@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
|
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||||
|
|
||||||
fill-range@^7.0.1:
|
fill-range@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||||
@@ -404,7 +433,7 @@ stacktracey@^1.2.112:
|
|||||||
as-table "^1.0.36"
|
as-table "^1.0.36"
|
||||||
get-source "^1.0.34"
|
get-source "^1.0.34"
|
||||||
|
|
||||||
supports-color@^5.5.0:
|
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||||
@@ -423,10 +452,10 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||||
|
|
||||||
typescript@^3.5.3:
|
typescript@^3.8.2:
|
||||||
version "3.5.3"
|
version "3.9.7"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||||
|
|
||||||
ws@^7.0.0:
|
ws@^7.0.0:
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
|
|||||||
18
demos/nim/.vscode/launch.json
vendored
Normal file
18
demos/nim/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Run your Executable",
|
||||||
|
"program": "${workspaceFolder}/main",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"initCommands": [
|
||||||
|
// To let LLDB know that NCSTRING is just like a C string
|
||||||
|
"type format add --format c-string NCSTRING"
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
10
demos/nim/.vscode/tasks.json
vendored
Normal file
10
demos/nim/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "nim c main.nim"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
21
demos/nim/LICENSE
Executable file
21
demos/nim/LICENSE
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Danil Yarantsev
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
82
demos/nim/main.nim
Normal file
82
demos/nim/main.nim
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import random, json, options, sugar
|
||||||
|
|
||||||
|
type
|
||||||
|
Kind = object
|
||||||
|
array: bool
|
||||||
|
|
||||||
|
Label = object
|
||||||
|
label: Option[string]
|
||||||
|
|
||||||
|
Column = object
|
||||||
|
content: Option[string]
|
||||||
|
tag: Option[string]
|
||||||
|
color: Option[string]
|
||||||
|
|
||||||
|
Row = object
|
||||||
|
label: Option[string]
|
||||||
|
columns: seq[Column]
|
||||||
|
|
||||||
|
Marker = object
|
||||||
|
id: string
|
||||||
|
row: int
|
||||||
|
column: int
|
||||||
|
rows: Option[int]
|
||||||
|
columns: Option[int]
|
||||||
|
label: Option[string]
|
||||||
|
color: Option[string]
|
||||||
|
|
||||||
|
Grid = object
|
||||||
|
kind: Kind
|
||||||
|
columnLabels: Option[seq[Label]]
|
||||||
|
rows: seq[Row]
|
||||||
|
markers: Option[seq[Marker]]
|
||||||
|
|
||||||
|
proc showSeq[T](data: seq[T], markers: seq[(string, int)] = @[]): cstring =
|
||||||
|
# Need to use cstring string type for C-compatible strings
|
||||||
|
let labels = collect(newSeq):
|
||||||
|
# Labels are indexes of the seq
|
||||||
|
for x in 0 ..< data.len():
|
||||||
|
Label(label: some($x))
|
||||||
|
|
||||||
|
let columns = collect(newSeq):
|
||||||
|
# Columns contain values of the seq
|
||||||
|
for x in data:
|
||||||
|
Column(content: some($x), tag: some($x))
|
||||||
|
|
||||||
|
let marks = collect(newSeq):
|
||||||
|
# If there are any markers (id - string, index - int)
|
||||||
|
for marker in markers:
|
||||||
|
Marker(id: marker[0], row: 0, column: marker[1])
|
||||||
|
|
||||||
|
let grid = Grid(
|
||||||
|
kind: Kind(array: true),
|
||||||
|
rows: @[Row(columns: columns)],
|
||||||
|
columnLabels: some(labels),
|
||||||
|
markers: some(marks)
|
||||||
|
)
|
||||||
|
# Serialize to JSON
|
||||||
|
result = $ %grid
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
# Random seed
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
# Fill seq with 10 random values from 1 to 999
|
||||||
|
var a = newSeq[int]()
|
||||||
|
for x in 0..10:
|
||||||
|
a.add rand(1..999)
|
||||||
|
|
||||||
|
# Simple bubble sort with some markers
|
||||||
|
# Open the visualizer and type in "ress"
|
||||||
|
let n = a.len() - 1
|
||||||
|
var ress = showSeq(a, @[("i", 0), ("j", 0)])
|
||||||
|
for i in 0 .. n:
|
||||||
|
for j in 0 .. n - i - 1:
|
||||||
|
if a[j] > a[j+1]:
|
||||||
|
ress = showSeq(a, @[("i", i), ("j", j)]) # breakpoint
|
||||||
|
let temp = a[j]
|
||||||
|
a[j] = a[j + 1]
|
||||||
|
a[j + 1] = temp
|
||||||
|
ress = showSeq(a, @[("i", i), ("j", j)]) # breakpoint
|
||||||
|
|
||||||
|
main()
|
||||||
2
demos/nim/nim.cfg
Normal file
2
demos/nim/nim.cfg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# For Nim source code lines
|
||||||
|
debugger:native
|
||||||
@@ -1,5 +1,22 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
- Support for the new JS debug adapters
|
||||||
|
- Moved visualizations to [github.com/hediet/visualization](https://github.com/hediet/visualization).
|
||||||
|
- New Visualizations / Improved design / Improved dark theme
|
||||||
|
- New Logo
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
- Fixes a bug that occurs when the debug visualizer is opened after the debug session has started.
|
||||||
|
- Fixes a bug that is caused by VS Code invoking the debug tracker multiple times for the same session.
|
||||||
|
- Adds support for the new `pwa-chrome` debug adapter.
|
||||||
|
- Uses `repl` mode as default evaluation context so that responses don't get truncated.
|
||||||
|
- Improves error messages when evaluating an expression or parsing the result goes wrong.
|
||||||
|
- Sets `webview.retainContextWhenHidden` to true, so that visualizer views load faster when they were already opened.
|
||||||
|
- Configures `webview.portMapping` so that this extension should work with remote development.
|
||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- "Debug Visualizer: Use Selection as Expression" will open a new view if no Debug Visualizer view was opened.
|
- "Debug Visualizer: Use Selection as Expression" will open a new view if no Debug Visualizer view was opened.
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
[](https://twitter.com/intent/follow?screen_name=hediet_dev)
|
[](https://twitter.com/intent/follow?screen_name=hediet_dev)
|
||||||
|
|
||||||
A VS Code extension for visualizing data structures while debugging.
|
A VS Code extension for visualizing data structures while debugging.
|
||||||
|
Like the VS Code's watch view, but with custom visualizations.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
See [demos](./demos/) for demos. These languages and debuggers are verified to work with this extension:
|
See [demos](../demos/) for demos. These languages and debuggers are verified to work with this extension:
|
||||||
|
|
||||||
- JavaScript/TypeScript/... using `node`/`node2`/`extensionHost` or `chrome` debug adapter: [⭐ Full Support](../demos/js)
|
- JavaScript/TypeScript/... using `node`/`node2`/`extensionHost` or `chrome` debug adapter: [⭐ Full Support](../demos/js)
|
||||||
- Go using `go` (Delve) debug adapter: [✅ Rudimentary Support](../demos/golang)
|
- Go using `go` (Delve) debug adapter: [✅ Rudimentary Support](../demos/golang)
|
||||||
@@ -28,8 +29,9 @@ Fully supported languages offer _Data Extractors_ which convert some well known
|
|||||||
|
|
||||||
After installing this extension, use the command `Debug Visualizer: New View` to open a new visualizer view.
|
After installing this extension, use the command `Debug Visualizer: New View` to open a new visualizer view.
|
||||||
In this view you can enter an expression that is evaluated and visualized while stepping through your application.
|
In this view you can enter an expression that is evaluated and visualized while stepping through your application.
|
||||||
|
This view works the same as the watch view of VS Code, except that the resulting value is presented visually rather than textually.
|
||||||
|
|
||||||
You can refresh the evaluation and pop out the current visualizer view into a new browser window by using the top right buttons.
|
You can refresh the evaluation and open the current visualizer view into a new browser window by using the top right buttons.
|
||||||
You can also unfold the details pane to select a _Data Extractor_ and a _Visualizer_.
|
You can also unfold the details pane to select a _Data Extractor_ and a _Visualizer_.
|
||||||
|
|
||||||
Use the command `Debug Visualizer: Use Selection as Expression` (Shift + F1) to use the currently selected text as expression
|
Use the command `Debug Visualizer: Use Selection as Expression` (Shift + F1) to use the currently selected text as expression
|
||||||
@@ -39,7 +41,7 @@ in the most recently opened debug visualizer.
|
|||||||
|
|
||||||
Visualizers consume specific JSON data. See [Integrated Visualizers](#Integrated%20Visualizers) for the schema of supported JSON data.
|
Visualizers consume specific JSON data. See [Integrated Visualizers](#Integrated%20Visualizers) for the schema of supported JSON data.
|
||||||
|
|
||||||
The currently visualized expression should evaluate to a JSON Object string,
|
The currently watched expression should evaluate to a JSON Object string,
|
||||||
matching the schema of one of the supported visualizers. This JSON string may be surrounded by single or double quotation marks (or none at all) and must not be escaped.
|
matching the schema of one of the supported visualizers. This JSON string may be surrounded by single or double quotation marks (or none at all) and must not be escaped.
|
||||||
A valid example is `"{ "kind": { "text": true }, "text": "some text\nmore text" }"`.
|
A valid example is `"{ "kind": { "text": true }, "text": "some text\nmore text" }"`.
|
||||||
|
|
||||||
@@ -217,7 +219,7 @@ interface DotGraph extends Text {
|
|||||||
## JavaScript/TypeScript Integrated Data Extractors
|
## JavaScript/TypeScript Integrated Data Extractors
|
||||||
|
|
||||||
Data extractors convert arbitrary values into data consumable by visualizers.
|
Data extractors convert arbitrary values into data consumable by visualizers.
|
||||||
They live in the debugee. The following data extractors are injected automatically into the debugee by this extension when using the `node`, `node2`, `extensionHost` or `chrome` debug adapter.
|
They live in the debugee. The following data extractors are injected automatically into the debugee by this extension when using the `node`, `node2`, `extensionHost`, `chrome` or `pwa-chrome` debug adapter.
|
||||||
Custom data extractors can be registered too.
|
Custom data extractors can be registered too.
|
||||||
See the package `@hediet/debug-visualizer-data-extraction` and its [README](../data-extraction/README.md) for the implementation and its API.
|
See the package `@hediet/debug-visualizer-data-extraction` and its [README](../data-extraction/README.md) for the implementation and its API.
|
||||||
Also, a global object of name `hedietDbgVis` with helper functions is injected.
|
Also, a global object of name `hedietDbgVis` with helper functions is injected.
|
||||||
|
|||||||
BIN
extension/docs/logo.drawio.png
Normal file
BIN
extension/docs/logo.drawio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -2,14 +2,27 @@
|
|||||||
"name": "debug-visualizer",
|
"name": "debug-visualizer",
|
||||||
"private": true,
|
"private": true,
|
||||||
"displayName": "Debug Visualizer",
|
"displayName": "Debug Visualizer",
|
||||||
"description": "An extension that visualizes values while debugging.",
|
"description": "A visual watch window that lets you visualize your data structures while debugging.",
|
||||||
"icon": "docs/logo.png",
|
"icon": "docs/logo.drawio.png",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.35.0"
|
"vscode": "^1.46.0"
|
||||||
},
|
},
|
||||||
"publisher": "hediet",
|
"publisher": "hediet",
|
||||||
|
"keywords": [
|
||||||
|
"debugger",
|
||||||
|
"debugging",
|
||||||
|
"debug",
|
||||||
|
"visual",
|
||||||
|
"javascript",
|
||||||
|
"graph",
|
||||||
|
"ast",
|
||||||
|
"visualization"
|
||||||
|
],
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/hediet/vscode-debug-visualizer/issues"
|
||||||
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"email": "henning.dieterichs@live.de",
|
"email": "henning.dieterichs@live.de",
|
||||||
"name": "Henning Dieterichs"
|
"name": "Henning Dieterichs"
|
||||||
@@ -95,7 +108,8 @@
|
|||||||
"open": "^7.0.2",
|
"open": "^7.0.2",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"ws": "^7.2.1",
|
"ws": "^7.2.1",
|
||||||
"crypto-random-string": "^3.1.0"
|
"crypto-random-string": "^3.1.0",
|
||||||
|
"mobx-utils": "^5.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"copy-webpack-plugin": "^5.1.1",
|
"copy-webpack-plugin": "^5.1.1",
|
||||||
@@ -103,7 +117,7 @@
|
|||||||
"@types/express": "^4.17.2",
|
"@types/express": "^4.17.2",
|
||||||
"@types/serve-static": "^1.13.3",
|
"@types/serve-static": "^1.13.3",
|
||||||
"@types/node": "^13.7.4",
|
"@types/node": "^13.7.4",
|
||||||
"@types/vscode": "1.35.0",
|
"@types/vscode": "1.46.0",
|
||||||
"tslint": "^6.0.0",
|
"tslint": "^6.0.0",
|
||||||
"typescript": "^3.8.2",
|
"typescript": "^3.8.2",
|
||||||
"webpack": "^4.41.6",
|
"webpack": "^4.41.6",
|
||||||
|
|||||||
@@ -1,55 +1,50 @@
|
|||||||
import { workspace } from "vscode";
|
import { workspace, window, ColorThemeKind } from "vscode";
|
||||||
import { Disposable } from "@hediet/std/disposable";
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||||
|
import { VsCodeSetting, serializerWithDefault } from "./utils/VsCodeSettings";
|
||||||
const useChromeKioskModeKey = "debugVisualizer.useChromeKioskMode";
|
|
||||||
const debugAdapterConfigsKey = "debugVisualizer.debugAdapterConfigurations";
|
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
public dispose = Disposable.fn();
|
public dispose = Disposable.fn();
|
||||||
|
|
||||||
@observable
|
private readonly _useChromeKioskMode = new VsCodeSetting(
|
||||||
private _useChromeKioskMode!: boolean;
|
"debugVisualizer.useChromeKioskMode",
|
||||||
|
{ serializer: serializerWithDefault<boolean>(true) }
|
||||||
@observable
|
);
|
||||||
private _debugAdapterConfigs!: DebugAdapterConfigs;
|
|
||||||
|
|
||||||
public get useChromeKioskMode(): boolean {
|
public get useChromeKioskMode(): boolean {
|
||||||
return this._useChromeKioskMode;
|
return this._useChromeKioskMode.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get debugAdapterConfigs(): DebugAdapterConfigs {
|
private readonly _debugAdapterConfigs = new VsCodeSetting(
|
||||||
return this._debugAdapterConfigs;
|
"debugVisualizer.debugAdapterConfigurations",
|
||||||
|
{ serializer: serializerWithDefault<DebugAdapterConfigs>({}) }
|
||||||
|
);
|
||||||
|
|
||||||
|
@observable
|
||||||
|
private _vsCodeTheme = window.activeColorTheme;
|
||||||
|
|
||||||
|
public get theme(): "light" | "dark" {
|
||||||
|
if (this._vsCodeTheme.kind === ColorThemeKind.Light) {
|
||||||
|
return "light";
|
||||||
|
} else if (this._vsCodeTheme.kind === ColorThemeKind.Dark) {
|
||||||
|
return "dark";
|
||||||
|
}
|
||||||
|
return "light";
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.updateConfig();
|
|
||||||
this.dispose.track(
|
this.dispose.track(
|
||||||
workspace.onDidChangeConfiguration(() => {
|
window.onDidChangeActiveColorTheme(() => {
|
||||||
this.updateConfig();
|
this._vsCodeTheme = window.activeColorTheme;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateConfig(): void {
|
|
||||||
const c = workspace.getConfiguration();
|
|
||||||
|
|
||||||
this._useChromeKioskMode = mapUndefined(
|
|
||||||
c.get<boolean>(useChromeKioskModeKey),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
this._debugAdapterConfigs = mapUndefined(
|
|
||||||
c.get<DebugAdapterConfigs>(debugAdapterConfigsKey),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDebugAdapterConfig(
|
public getDebugAdapterConfig(
|
||||||
debugAdapterType: string
|
debugAdapterType: string
|
||||||
): DebugAdapterConfig | undefined {
|
): DebugAdapterConfig | undefined {
|
||||||
const c = this.debugAdapterConfigs[debugAdapterType];
|
const c = this._debugAdapterConfigs.get()[debugAdapterType];
|
||||||
if (!c) {
|
if (!c) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -65,13 +60,6 @@ export class Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapUndefined<T>(val: T | undefined, defaultVal: T) {
|
|
||||||
if (val === undefined) {
|
|
||||||
return defaultVal;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
type DebugAdapterConfigs = {
|
type DebugAdapterConfigs = {
|
||||||
[debugAdapter: string]: {
|
[debugAdapter: string]: {
|
||||||
context?: "watch" | "repl";
|
context?: "watch" | "repl";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||||
|
|
||||||
export class ComposedEvaluationEngine implements EvaluationEngine {
|
export class ComposedEvaluationEngine implements EvaluationEngine {
|
||||||
constructor(public readonly engines: EvaluationEngine[]) {}
|
constructor(public readonly engines: EvaluationEngine[]) {}
|
||||||
|
|
||||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||||
for (const f of this.engines) {
|
for (const f of this.engines) {
|
||||||
const evaluator = f.createEvaluator(session);
|
const evaluator = f.createEvaluator(session);
|
||||||
if (evaluator) {
|
if (evaluator) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||||
import { Config, DebugAdapterConfig } from "../../Config";
|
import { Config, DebugAdapterConfig } from "../../Config";
|
||||||
import { GenericEvaluator } from "./GenericEvaluationEngine";
|
import { GenericEvaluator } from "./GenericEvaluationEngine";
|
||||||
@@ -11,7 +11,7 @@ registerUpdateReconciler(module);
|
|||||||
export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
||||||
constructor(private readonly config: Config) {}
|
constructor(private readonly config: Config) {}
|
||||||
|
|
||||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||||
const config = this.config.getDebugAdapterConfig(session.session.type);
|
const config = this.config.getDebugAdapterConfig(session.session.type);
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -22,7 +22,7 @@ export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
|||||||
|
|
||||||
class ConfiguredEvaluator extends GenericEvaluator {
|
class ConfiguredEvaluator extends GenericEvaluator {
|
||||||
constructor(
|
constructor(
|
||||||
session: VsCodeDebugSession,
|
session: EnhancedDebugSession,
|
||||||
private readonly config: DebugAdapterConfig
|
private readonly config: DebugAdapterConfig
|
||||||
) {
|
) {
|
||||||
super(session);
|
super(session);
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import {
|
|||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
DataExtractorId,
|
DataExtractorId,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||||
import { FormattedMessage } from "../../contract";
|
import { FormattedMessage } from "../../webviewContract";
|
||||||
|
|
||||||
export interface EvaluationEngine {
|
export interface EvaluationEngine {
|
||||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined;
|
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Evaluator {
|
export interface Evaluator {
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ import {
|
|||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
DataExtractorId,
|
DataExtractorId,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||||
import {
|
import {
|
||||||
EvaluationEngine,
|
EvaluationEngine,
|
||||||
Evaluator,
|
Evaluator,
|
||||||
EvaluationArgs,
|
EvaluationArgs,
|
||||||
} from "./EvaluationEngine";
|
} from "./EvaluationEngine";
|
||||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||||
import { FormattedMessage } from "../../contract";
|
import { FormattedMessage } from "../../webviewContract";
|
||||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||||
|
|
||||||
registerUpdateReconciler(module);
|
registerUpdateReconciler(module);
|
||||||
|
|
||||||
@hotClass(module)
|
@hotClass(module)
|
||||||
export class GenericEvaluationEngine implements EvaluationEngine {
|
export class GenericEvaluationEngine implements EvaluationEngine {
|
||||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||||
return new GenericEvaluator(session);
|
return new GenericEvaluator(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ export class GenericEvaluationEngine implements EvaluationEngine {
|
|||||||
export class GenericEvaluator implements Evaluator {
|
export class GenericEvaluator implements Evaluator {
|
||||||
public readonly languageId = "text";
|
public readonly languageId = "text";
|
||||||
|
|
||||||
constructor(private readonly session: VsCodeDebugSession) {}
|
constructor(private readonly session: EnhancedDebugSession) {}
|
||||||
|
|
||||||
public async evaluate({
|
public async evaluate({
|
||||||
expression,
|
expression,
|
||||||
@@ -51,6 +51,7 @@ export class GenericEvaluator implements Evaluator {
|
|||||||
message: {
|
message: {
|
||||||
kind: "list",
|
kind: "list",
|
||||||
items: [
|
items: [
|
||||||
|
"An error occurred while evaluating the expression:",
|
||||||
error.message,
|
error.message,
|
||||||
`Used debug adapter: ${this.session.session.configuration.type}`,
|
`Used debug adapter: ${this.session.session.configuration.type}`,
|
||||||
{
|
{
|
||||||
@@ -65,7 +66,9 @@ export class GenericEvaluator implements Evaluator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseEvaluationResultFromGenericDebugAdapter(reply.result);
|
return parseEvaluationResultFromGenericDebugAdapter(reply.result, {
|
||||||
|
debugAdapterType: this.session.session.configuration.type,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getFinalExpression(args: {
|
protected getFinalExpression(args: {
|
||||||
@@ -76,6 +79,7 @@ export class GenericEvaluator implements Evaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getContext(): "watch" | "repl" {
|
protected getContext(): "watch" | "repl" {
|
||||||
return "watch";
|
// we will use "repl" as default so that results are not truncated.
|
||||||
|
return "repl";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,25 +5,27 @@ import {
|
|||||||
getExpressionToInitializeDataExtractorApi,
|
getExpressionToInitializeDataExtractorApi,
|
||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||||
import {
|
import {
|
||||||
EvaluationEngine,
|
EvaluationEngine,
|
||||||
Evaluator,
|
Evaluator,
|
||||||
EvaluationArgs,
|
EvaluationArgs,
|
||||||
} from "./EvaluationEngine";
|
} from "./EvaluationEngine";
|
||||||
import { FormattedMessage } from "../../contract";
|
import { FormattedMessage } from "../../webviewContract";
|
||||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
||||||
|
|
||||||
registerUpdateReconciler(module);
|
registerUpdateReconciler(module);
|
||||||
|
|
||||||
@hotClass(module)
|
@hotClass(module)
|
||||||
export class JsEvaluationEngine implements EvaluationEngine {
|
export class JsEvaluationEngine implements EvaluationEngine {
|
||||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||||
const supportedDebugAdapters = [
|
const supportedDebugAdapters = [
|
||||||
"node",
|
"node",
|
||||||
"node2",
|
"node2",
|
||||||
"extensionHost",
|
"extensionHost",
|
||||||
"chrome",
|
"chrome",
|
||||||
|
"pwa-chrome",
|
||||||
|
"pwa-node",
|
||||||
];
|
];
|
||||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||||
return new JsEvaluator(session);
|
return new JsEvaluator(session);
|
||||||
@@ -35,7 +37,14 @@ export class JsEvaluationEngine implements EvaluationEngine {
|
|||||||
class JsEvaluator implements Evaluator {
|
class JsEvaluator implements Evaluator {
|
||||||
public readonly languageId = "javascript";
|
public readonly languageId = "javascript";
|
||||||
|
|
||||||
constructor(private readonly session: VsCodeDebugSession) {}
|
constructor(private readonly session: EnhancedDebugSession) {}
|
||||||
|
|
||||||
|
private getContext(): "copy" | "repl" {
|
||||||
|
if (this.session.session.type.startsWith("pwa-")) {
|
||||||
|
return "copy";
|
||||||
|
}
|
||||||
|
return "repl";
|
||||||
|
}
|
||||||
|
|
||||||
public async evaluate({
|
public async evaluate({
|
||||||
expression,
|
expression,
|
||||||
@@ -74,9 +83,13 @@ class JsEvaluator implements Evaluator {
|
|||||||
const reply = await this.session.evaluate({
|
const reply = await this.session.evaluate({
|
||||||
expression: wrappedExpr,
|
expression: wrappedExpr,
|
||||||
frameId,
|
frameId,
|
||||||
|
context: this.getContext(),
|
||||||
});
|
});
|
||||||
const resultStr = reply.result;
|
const resultStr = reply.result;
|
||||||
const jsonData = resultStr.substr(1, resultStr.length - 2);
|
const jsonData =
|
||||||
|
this.getContext() === "copy"
|
||||||
|
? resultStr
|
||||||
|
: resultStr.substr(1, resultStr.length - 2);
|
||||||
const result = JSON.parse(jsonData) as DataResult;
|
const result = JSON.parse(jsonData) as DataResult;
|
||||||
|
|
||||||
if (result.kind === "NoExtractors") {
|
if (result.kind === "NoExtractors") {
|
||||||
@@ -115,6 +128,7 @@ class JsEvaluator implements Evaluator {
|
|||||||
await this.session.evaluate({
|
await this.session.evaluate({
|
||||||
expression,
|
expression,
|
||||||
frameId,
|
frameId,
|
||||||
|
context: this.getContext(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
DataExtractionResult,
|
DataExtractionResult,
|
||||||
isExtractedData,
|
isVisualizationData,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { FormattedMessage } from "../../contract";
|
import { FormattedMessage } from "../../webviewContract";
|
||||||
|
|
||||||
|
export interface ParseEvaluationResultContext {
|
||||||
|
debugAdapterType: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseEvaluationResultFromGenericDebugAdapter(
|
export function parseEvaluationResultFromGenericDebugAdapter(
|
||||||
resultText: string
|
resultText: string,
|
||||||
|
context: ParseEvaluationResultContext
|
||||||
):
|
):
|
||||||
| { kind: "data"; result: DataExtractionResult }
|
| { kind: "data"; result: DataExtractionResult }
|
||||||
| { kind: "error"; message: FormattedMessage } {
|
| { kind: "error"; message: FormattedMessage } {
|
||||||
@@ -26,16 +31,16 @@ export function parseEvaluationResultFromGenericDebugAdapter(
|
|||||||
// Just in case no quoting is done.
|
// Just in case no quoting is done.
|
||||||
jsonData2 = jsonData;
|
jsonData2 = jsonData;
|
||||||
}
|
}
|
||||||
resultObj = parseJson(jsonData2);
|
resultObj = parseJson(jsonData2, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// in case of C++: `"{ \"kind\": { ... }, \"text\": \"some\\ntext\" }"`
|
// in case of C++: `"{ \"kind\": { ... }, \"text\": \"some\\ntext\" }"`
|
||||||
const str = parseJson(jsonData);
|
const str = parseJson(jsonData, context);
|
||||||
// str is now `{ "kind": { ... }, "text": "some\ntext" }"`
|
// str is now `{ "kind": { ... }, "text": "some\ntext" }"`
|
||||||
resultObj = parseJson(str);
|
resultObj = parseJson(str, context);
|
||||||
// result is now { kind: { ... }, text: "some\ntext" }
|
// result is now { kind: { ... }, text: "some\ntext" }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isExtractedData(resultObj)) {
|
if (!isVisualizationData(resultObj)) {
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
message: {
|
message: {
|
||||||
@@ -85,7 +90,7 @@ function isEnclosedWith(str: string, char: string): boolean {
|
|||||||
return str.startsWith(char) && str.endsWith(char);
|
return str.startsWith(char) && str.endsWith(char);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseJson(str: string) {
|
function parseJson(str: string, context: ParseEvaluationResultContext) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(str);
|
return JSON.parse(str);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -104,6 +109,7 @@ function parseJson(str: string) {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
`Used debug adapter: ${context.debugAdapterType}`,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { DataExtractionState, CompletionItem } from "../contract";
|
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||||
|
|
||||||
export interface EvaluationWatchService {
|
export interface EvaluationWatchService {
|
||||||
createEvaluationWatcher(
|
createEvaluationWatcher(
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import {
|
|||||||
import { observable, autorun, action } from "mobx";
|
import { observable, autorun, action } from "mobx";
|
||||||
import { Disposable } from "@hediet/std/disposable";
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||||
import { DataExtractionState, CompletionItem } from "../contract";
|
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||||
import { hotClass } from "@hediet/node-reload";
|
import { hotClass } from "@hediet/node-reload";
|
||||||
import { VsCodeDebuggerView } from "../VsCodeDebugger";
|
import { VsCodeDebuggerView } from "../debugger/VsCodeDebuggerView";
|
||||||
import { EvaluationEngine } from "./EvaluationEngine/EvaluationEngine";
|
import { EvaluationEngine } from "./EvaluationEngine/EvaluationEngine";
|
||||||
|
|
||||||
@hotClass(module)
|
@hotClass(module)
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
import { Disposable } from "@hediet/std/disposable";
|
|
||||||
import { debug, DebugSession } from "vscode";
|
|
||||||
import { EventEmitter } from "@hediet/std/events";
|
|
||||||
import { CompletionItem } from "./contract";
|
|
||||||
import { observable, runInAction } from "mobx";
|
|
||||||
|
|
||||||
export class VsCodeDebugger {
|
|
||||||
public readonly dispose = Disposable.fn();
|
|
||||||
private readonly sessions = new Map<DebugSession, VsCodeDebugSession>();
|
|
||||||
|
|
||||||
private readonly _onDidStartDebugSession = new EventEmitter<{
|
|
||||||
session: VsCodeDebugSession;
|
|
||||||
}>();
|
|
||||||
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
|
||||||
|
|
||||||
public getDebugSession(session: DebugSession): VsCodeDebugSession {
|
|
||||||
return this.sessions.get(session)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.dispose.track([
|
|
||||||
debug.onDidStartDebugSession(session => {
|
|
||||||
const e = this.sessions.get(session)!;
|
|
||||||
this._onDidStartDebugSession.emit({ session: e });
|
|
||||||
}),
|
|
||||||
debug.onDidTerminateDebugSession(session => {
|
|
||||||
const e = this.sessions.get(session)!;
|
|
||||||
// TODO add proper event
|
|
||||||
this.sessions.delete(session);
|
|
||||||
}),
|
|
||||||
debug.registerDebugAdapterTrackerFactory("*", {
|
|
||||||
createDebugAdapterTracker: session => {
|
|
||||||
const extendedSession = new VsCodeDebugSession(session);
|
|
||||||
this.sessions.set(session, extendedSession);
|
|
||||||
|
|
||||||
return {
|
|
||||||
onWillReceiveMessage: msg => {
|
|
||||||
console.log(msg.type, msg.event, msg);
|
|
||||||
},
|
|
||||||
onDidSendMessage: async msg => {
|
|
||||||
type Message =
|
|
||||||
| StoppedEvent
|
|
||||||
| ThreadsResponse
|
|
||||||
| ContinueLikeResponse;
|
|
||||||
|
|
||||||
interface ContinueLikeResponse {
|
|
||||||
type: "response";
|
|
||||||
command:
|
|
||||||
| "continue"
|
|
||||||
| "stepIn"
|
|
||||||
| "stepOut"
|
|
||||||
| "next";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StoppedEvent {
|
|
||||||
type: "event";
|
|
||||||
event: "stopped";
|
|
||||||
body: {
|
|
||||||
threadId: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ThreadsResponse {
|
|
||||||
type: "response";
|
|
||||||
command: "threads";
|
|
||||||
success: boolean;
|
|
||||||
body: {
|
|
||||||
threads: ThreadInfo[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ThreadInfo {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const m = msg as Message;
|
|
||||||
if (m.type === "event") {
|
|
||||||
if (m.event === "stopped") {
|
|
||||||
const threadId = m.body.threadId;
|
|
||||||
const r = await extendedSession[
|
|
||||||
"getStackTrace"
|
|
||||||
]({
|
|
||||||
threadId,
|
|
||||||
startFrame: 0,
|
|
||||||
levels: 1,
|
|
||||||
});
|
|
||||||
extendedSession["activeStackFrame"] =
|
|
||||||
r.stackFrames.length > 0
|
|
||||||
? r.stackFrames[0].id
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
} else if (m.type === "response") {
|
|
||||||
if (
|
|
||||||
m.command === "continue" ||
|
|
||||||
m.command === "next" ||
|
|
||||||
m.command === "stepIn" ||
|
|
||||||
m.command === "stepOut"
|
|
||||||
) {
|
|
||||||
extendedSession[
|
|
||||||
"activeStackFrame"
|
|
||||||
] = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StackFrame {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VsCodeDebugSession {
|
|
||||||
@observable protected activeStackFrame: number | undefined;
|
|
||||||
|
|
||||||
constructor(public readonly session: DebugSession) {}
|
|
||||||
|
|
||||||
protected async getStackTrace(args: {
|
|
||||||
threadId: number;
|
|
||||||
startFrame?: number;
|
|
||||||
levels?: number;
|
|
||||||
}): Promise<{ totalFrames?: number; stackFrames: StackFrame[] }> {
|
|
||||||
try {
|
|
||||||
const reply = (await this.session.customRequest("stackTrace", {
|
|
||||||
threadId: args.threadId,
|
|
||||||
levels: args.levels,
|
|
||||||
startFrame: args.startFrame || 0,
|
|
||||||
})) as { totalFrames?: number; stackFrames: StackFrame[] };
|
|
||||||
return reply;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getCompletions(args: {
|
|
||||||
text: string;
|
|
||||||
column: number;
|
|
||||||
frameId: number | undefined;
|
|
||||||
}): Promise<CompletionItem[]> {
|
|
||||||
try {
|
|
||||||
const reply = await this.session.customRequest("completions", {
|
|
||||||
text: args.text,
|
|
||||||
frameId: args.frameId,
|
|
||||||
column: args.column,
|
|
||||||
});
|
|
||||||
if (!reply) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return reply.targets;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async evaluate(args: {
|
|
||||||
expression: string;
|
|
||||||
frameId: number | undefined;
|
|
||||||
context?: "watch" | "repl";
|
|
||||||
}): Promise<{ result: string }> {
|
|
||||||
const reply = await this.session.customRequest("evaluate", {
|
|
||||||
expression: args.expression,
|
|
||||||
frameId: args.frameId,
|
|
||||||
context: args.context || "watch",
|
|
||||||
});
|
|
||||||
return { result: reply.result };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VsCodeDebuggerView {
|
|
||||||
public readonly dispose = Disposable.fn();
|
|
||||||
|
|
||||||
@observable private _activeDebugSession: VsCodeDebugSession | undefined;
|
|
||||||
|
|
||||||
public get activeDebugSession(): VsCodeDebugSession | undefined {
|
|
||||||
return this._activeDebugSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get activeFrameId(): number | undefined {
|
|
||||||
if (!this._activeDebugSession) {
|
|
||||||
return undefined;
|
|
||||||
} else {
|
|
||||||
return this._activeDebugSession["activeStackFrame"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private vsCodeDebugger: VsCodeDebugger) {
|
|
||||||
this.dispose.track(
|
|
||||||
debug.onDidChangeActiveDebugSession(activeSession => {
|
|
||||||
runInAction("Update active debug session", () => {
|
|
||||||
if (!activeSession) {
|
|
||||||
this._activeDebugSession = undefined;
|
|
||||||
} else {
|
|
||||||
const s = this.vsCodeDebugger.getDebugSession(
|
|
||||||
activeSession
|
|
||||||
);
|
|
||||||
this._activeDebugSession = s;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
extension/src/debugger/EnhancedDebugSession.ts
Normal file
70
extension/src/debugger/EnhancedDebugSession.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { DebugSession } from "vscode";
|
||||||
|
import { CompletionItem } from "../webviewContract";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
export class EnhancedDebugSession {
|
||||||
|
@observable protected activeStackFrame: number | undefined;
|
||||||
|
|
||||||
|
constructor(public readonly session: DebugSession) {}
|
||||||
|
|
||||||
|
protected async getStackTrace(args: {
|
||||||
|
threadId: number;
|
||||||
|
startFrame?: number;
|
||||||
|
levels?: number;
|
||||||
|
}): Promise<{ totalFrames?: number; stackFrames: StackFrame[] }> {
|
||||||
|
try {
|
||||||
|
const reply = (await this.session.customRequest("stackTrace", {
|
||||||
|
threadId: args.threadId,
|
||||||
|
levels: args.levels,
|
||||||
|
startFrame: args.startFrame || 0,
|
||||||
|
})) as { totalFrames?: number; stackFrames: StackFrame[] };
|
||||||
|
return reply;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getCompletions(args: {
|
||||||
|
text: string;
|
||||||
|
column: number;
|
||||||
|
frameId: number | undefined;
|
||||||
|
}): Promise<CompletionItem[]> {
|
||||||
|
try {
|
||||||
|
const reply = await this.session.customRequest("completions", {
|
||||||
|
text: args.text,
|
||||||
|
frameId: args.frameId,
|
||||||
|
column: args.column,
|
||||||
|
});
|
||||||
|
if (!reply) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return reply.targets;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the given expression.
|
||||||
|
* If context is "watch", long results are usually shortened.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public async evaluate(args: {
|
||||||
|
expression: string;
|
||||||
|
frameId: number | undefined;
|
||||||
|
context: "watch" | "repl" | "copy";
|
||||||
|
}): Promise<{ result: string }> {
|
||||||
|
const reply = await this.session.customRequest("evaluate", {
|
||||||
|
expression: args.expression,
|
||||||
|
frameId: args.frameId,
|
||||||
|
context: args.context,
|
||||||
|
});
|
||||||
|
return { result: reply.result };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface StackFrame {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
116
extension/src/debugger/VsCodeDebugger.ts
Normal file
116
extension/src/debugger/VsCodeDebugger.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
|
import { debug, DebugSession } from "vscode";
|
||||||
|
import { EventEmitter } from "@hediet/std/events";
|
||||||
|
import { runInAction } from "mobx";
|
||||||
|
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates DebugSession instances and emits an event for new debug sessions.
|
||||||
|
*/
|
||||||
|
export class VsCodeDebugger {
|
||||||
|
public readonly dispose = Disposable.fn();
|
||||||
|
private readonly sessions = new Map<DebugSession, EnhancedDebugSession>();
|
||||||
|
|
||||||
|
private readonly _onDidStartDebugSession = new EventEmitter<{
|
||||||
|
session: EnhancedDebugSession;
|
||||||
|
}>();
|
||||||
|
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
||||||
|
|
||||||
|
public getEnhancedDebugSession(
|
||||||
|
session: DebugSession
|
||||||
|
): EnhancedDebugSession {
|
||||||
|
let result = this.sessions.get(session);
|
||||||
|
if (!result) {
|
||||||
|
result = new EnhancedDebugSession(session);
|
||||||
|
this.sessions.set(session, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.dispose.track([
|
||||||
|
debug.onDidStartDebugSession(session => {
|
||||||
|
const e = this.sessions.get(session)!;
|
||||||
|
this._onDidStartDebugSession.emit({ session: e });
|
||||||
|
}),
|
||||||
|
debug.onDidTerminateDebugSession(session => {
|
||||||
|
this.sessions.delete(session);
|
||||||
|
}),
|
||||||
|
debug.registerDebugAdapterTrackerFactory("*", {
|
||||||
|
createDebugAdapterTracker: session => {
|
||||||
|
const extendedSession = this.getEnhancedDebugSession(
|
||||||
|
session
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
onDidSendMessage: async msg => {
|
||||||
|
type Message =
|
||||||
|
| StoppedEvent
|
||||||
|
| ThreadsResponse
|
||||||
|
| ContinueLikeResponse;
|
||||||
|
|
||||||
|
interface ContinueLikeResponse {
|
||||||
|
type: "response";
|
||||||
|
command:
|
||||||
|
| "continue"
|
||||||
|
| "stepIn"
|
||||||
|
| "stepOut"
|
||||||
|
| "next";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoppedEvent {
|
||||||
|
type: "event";
|
||||||
|
event: "stopped";
|
||||||
|
body: {
|
||||||
|
threadId: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ThreadsResponse {
|
||||||
|
type: "response";
|
||||||
|
command: "threads";
|
||||||
|
success: boolean;
|
||||||
|
body: {
|
||||||
|
threads: ThreadInfo[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ThreadInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = msg as Message;
|
||||||
|
if (m.type === "event") {
|
||||||
|
if (m.event === "stopped") {
|
||||||
|
const threadId = m.body.threadId;
|
||||||
|
const r = await extendedSession[
|
||||||
|
"getStackTrace"
|
||||||
|
]({
|
||||||
|
threadId,
|
||||||
|
startFrame: 0,
|
||||||
|
levels: 1,
|
||||||
|
});
|
||||||
|
extendedSession["activeStackFrame"] =
|
||||||
|
r.stackFrames.length > 0
|
||||||
|
? r.stackFrames[0].id
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
} else if (m.type === "response") {
|
||||||
|
if (
|
||||||
|
m.command === "continue" ||
|
||||||
|
m.command === "next" ||
|
||||||
|
m.command === "stepIn" ||
|
||||||
|
m.command === "stepOut"
|
||||||
|
) {
|
||||||
|
extendedSession[
|
||||||
|
"activeStackFrame"
|
||||||
|
] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
48
extension/src/debugger/VsCodeDebuggerView.ts
Normal file
48
extension/src/debugger/VsCodeDebuggerView.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
|
import { debug, DebugSession } from "vscode";
|
||||||
|
import { observable, action } from "mobx";
|
||||||
|
import { VsCodeDebugger } from "./VsCodeDebugger";
|
||||||
|
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks the currently active debug session and its currently active stack frame.
|
||||||
|
* This is currently all faked. A proper API would be nice.
|
||||||
|
*/
|
||||||
|
export class VsCodeDebuggerView {
|
||||||
|
public readonly dispose = Disposable.fn();
|
||||||
|
|
||||||
|
@observable private _activeDebugSession: EnhancedDebugSession | undefined;
|
||||||
|
|
||||||
|
public get activeDebugSession(): EnhancedDebugSession | undefined {
|
||||||
|
return this._activeDebugSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get activeFrameId(): number | undefined {
|
||||||
|
if (!this._activeDebugSession) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return this._activeDebugSession["activeStackFrame"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private vsCodeDebugger: VsCodeDebugger) {
|
||||||
|
this.dispose.track(
|
||||||
|
debug.onDidChangeActiveDebugSession(activeSession => {
|
||||||
|
this.updateActiveDebugSession(activeSession);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.updateActiveDebugSession(debug.activeDebugSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
|
||||||
|
if (!activeSession) {
|
||||||
|
this._activeDebugSession = undefined;
|
||||||
|
} else {
|
||||||
|
const s = this.vsCodeDebugger.getEnhancedDebugSession(
|
||||||
|
activeSession
|
||||||
|
);
|
||||||
|
this._activeDebugSession = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,10 +12,11 @@ if (process.env.HOT_RELOAD) {
|
|||||||
}
|
}
|
||||||
registerUpdateReconciler(module);
|
registerUpdateReconciler(module);
|
||||||
|
|
||||||
import { WebViews } from "./WebViews";
|
import { InternalWebviewManager } from "./webview/InternalWebviewManager";
|
||||||
import { Server } from "./Server";
|
import { WebviewServer } from "./webview/WebviewServer";
|
||||||
import { Config } from "./Config";
|
import { Config } from "./Config";
|
||||||
import { VsCodeDebugger, VsCodeDebuggerView } from "./VsCodeDebugger";
|
import { VsCodeDebugger } from "./debugger/VsCodeDebugger";
|
||||||
|
import { VsCodeDebuggerView } from "./debugger/VsCodeDebuggerView";
|
||||||
import { EvaluationWatchServiceImpl } from "./EvaluationWatchService";
|
import { EvaluationWatchServiceImpl } from "./EvaluationWatchService";
|
||||||
import {
|
import {
|
||||||
ComposedEvaluationEngine,
|
ComposedEvaluationEngine,
|
||||||
@@ -24,11 +25,18 @@ import {
|
|||||||
ConfiguredEvaluationEngine,
|
ConfiguredEvaluationEngine,
|
||||||
} from "./EvaluationWatchService/EvaluationEngine";
|
} from "./EvaluationWatchService/EvaluationEngine";
|
||||||
|
|
||||||
|
export function activate(context: ExtensionContext) {
|
||||||
|
context.subscriptions.push(
|
||||||
|
hotRequireExportedFn(module, Extension, Extension => new Extension())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deactivate() {}
|
||||||
|
|
||||||
export class Extension {
|
export class Extension {
|
||||||
public readonly dispose = Disposable.fn();
|
public readonly dispose = Disposable.fn();
|
||||||
|
|
||||||
private readonly config = new Config();
|
private readonly config = new Config();
|
||||||
|
|
||||||
private readonly debugger = this.dispose.track(new VsCodeDebugger());
|
private readonly debugger = this.dispose.track(new VsCodeDebugger());
|
||||||
private readonly debuggerView = this.dispose.track(
|
private readonly debuggerView = this.dispose.track(
|
||||||
new VsCodeDebuggerView(this.debugger)
|
new VsCodeDebuggerView(this.debugger)
|
||||||
@@ -43,8 +51,10 @@ export class Extension {
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
private readonly server = new Server(this.dataSource, this.config);
|
private readonly server = new WebviewServer(this.dataSource, this.config);
|
||||||
private readonly views = this.dispose.track(new WebViews(this.server));
|
private readonly views = this.dispose.track(
|
||||||
|
new InternalWebviewManager(this.server, this.config)
|
||||||
|
);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (getReloadCount(module) > 0) {
|
if (getReloadCount(module) > 0) {
|
||||||
@@ -79,10 +89,10 @@ export class Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connections = [...this.server.connections.values()];
|
const connections = [...this.server.connections.values()];
|
||||||
if (connections.length > 0) {
|
const latestConnection =
|
||||||
const latestConnection =
|
connections[connections.length - 1];
|
||||||
connections[connections.length - 1];
|
|
||||||
|
|
||||||
|
if (latestConnection) {
|
||||||
latestConnection.setExpression(selectedText);
|
latestConnection.setExpression(selectedText);
|
||||||
} else {
|
} else {
|
||||||
this.views.createNew(selectedText);
|
this.views.createNew(selectedText);
|
||||||
@@ -92,11 +102,3 @@ export class Extension {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function activate(context: ExtensionContext) {
|
|
||||||
context.subscriptions.push(
|
|
||||||
hotRequireExportedFn(module, Extension, Extension => new Extension())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deactivate() {}
|
|
||||||
|
|||||||
120
extension/src/utils/VsCodeSettings.ts
Normal file
120
extension/src/utils/VsCodeSettings.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { Uri, workspace, ConfigurationTarget, Disposable } from "vscode";
|
||||||
|
import { fromResource } from "mobx-utils";
|
||||||
|
import { computed, runInAction } from "mobx";
|
||||||
|
import { EventEmitter } from "@hediet/std/events";
|
||||||
|
|
||||||
|
export interface Serializer<T> {
|
||||||
|
deserialize: (val: any) => T;
|
||||||
|
serializer: (val: T) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializerWithDefault<T>(defaultValue: T): Serializer<T> {
|
||||||
|
return {
|
||||||
|
deserialize: val => (val === undefined ? defaultValue : val),
|
||||||
|
serializer: val => val,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VsCodeSetting<T> {
|
||||||
|
public get T(): T {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly serializer: Serializer<T>;
|
||||||
|
public readonly scope: Uri | undefined;
|
||||||
|
private readonly settingResource: VsCodeSettingResource;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
options: {
|
||||||
|
serializer?: Serializer<T>;
|
||||||
|
scope?: Uri;
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
|
this.scope = options.scope;
|
||||||
|
this.serializer = options.serializer || {
|
||||||
|
deserialize: val => val,
|
||||||
|
serializer: val => val,
|
||||||
|
};
|
||||||
|
this.settingResource = new VsCodeSettingResource(this.id, this.scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(): T {
|
||||||
|
const result = this.settingResource.value;
|
||||||
|
return this.serializer.deserialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async set(value: T): Promise<void> {
|
||||||
|
const value2 = this.serializer.serializer(value);
|
||||||
|
const c = workspace.getConfiguration(undefined, this.scope);
|
||||||
|
const result = c.inspect(this.id);
|
||||||
|
let target: ConfigurationTarget;
|
||||||
|
|
||||||
|
if (
|
||||||
|
result &&
|
||||||
|
[result.workspaceFolderValue].some(i => i !== undefined)
|
||||||
|
) {
|
||||||
|
target = ConfigurationTarget.WorkspaceFolder;
|
||||||
|
}
|
||||||
|
if (result && [result.workspaceValue].some(i => i !== undefined)) {
|
||||||
|
target = ConfigurationTarget.Workspace;
|
||||||
|
} else {
|
||||||
|
target = ConfigurationTarget.Global;
|
||||||
|
}
|
||||||
|
|
||||||
|
await c.update(this.id, value2, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VsCodeSettingResource {
|
||||||
|
public static onConfigChange = new EventEmitter();
|
||||||
|
|
||||||
|
private subscription: Disposable | undefined;
|
||||||
|
private readonly resource = fromResource<any>(
|
||||||
|
update => {
|
||||||
|
update(this.readValue());
|
||||||
|
this.subscription = VsCodeSettingResource.onConfigChange.sub(() => {
|
||||||
|
update(this.readValue());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
() => this.subscription!.dispose(),
|
||||||
|
this.readValue()
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly id: string,
|
||||||
|
private readonly scope: Uri | undefined
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private readValue(): any {
|
||||||
|
const val = workspace
|
||||||
|
.getConfiguration(undefined, this.scope)
|
||||||
|
.get(this.id);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This improves change detection.
|
||||||
|
*/
|
||||||
|
private readonly stringifiedSettingValue = computed(
|
||||||
|
() => JSON.stringify(this.resource.current()),
|
||||||
|
{
|
||||||
|
name: `VsCodeSettingResource[${this.id}].value`,
|
||||||
|
context: this,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public get value() {
|
||||||
|
const v = this.stringifiedSettingValue.get();
|
||||||
|
if (v === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return JSON.parse(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace.onDidChangeConfiguration(() => {
|
||||||
|
runInAction("Update Configuration", () => {
|
||||||
|
VsCodeSettingResource.onConfigChange.emit();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
import { window, ViewColumn, WebviewPanel } from "vscode";
|
import { window, ViewColumn, WebviewPanel } from "vscode";
|
||||||
import { Server } from "./Server";
|
import { WebviewServer } from "./WebviewServer";
|
||||||
import { Disposable } from "@hediet/std/disposable";
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
|
import { WindowWithWebviewData } from "../webviewContract";
|
||||||
|
import { Config } from "../Config";
|
||||||
|
|
||||||
export const debugVisualizer = "debugVisualizer";
|
export const debugVisualizer = "debugVisualizer";
|
||||||
|
|
||||||
export class WebViews {
|
export class InternalWebviewManager {
|
||||||
private readonly debugVisualizations = new Map<WebviewPanel, WebView>();
|
private readonly openedWebviews = new Map<
|
||||||
|
WebviewPanel,
|
||||||
|
DebugVisualizerWebview
|
||||||
|
>();
|
||||||
|
|
||||||
public readonly dispose = Disposable.fn();
|
public readonly dispose = Disposable.fn();
|
||||||
|
|
||||||
constructor(private readonly server: Server) {
|
constructor(
|
||||||
|
private readonly server: WebviewServer,
|
||||||
|
private readonly config: Config
|
||||||
|
) {
|
||||||
this.dispose.track(
|
this.dispose.track(
|
||||||
window.registerWebviewPanelSerializer(debugVisualizer, {
|
window.registerWebviewPanelSerializer(debugVisualizer, {
|
||||||
deserializeWebviewPanel: async (panel, state) => {
|
deserializeWebviewPanel: async (panel, state) => {
|
||||||
@@ -20,7 +28,7 @@ export class WebViews {
|
|||||||
|
|
||||||
this.dispose.track({
|
this.dispose.track({
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
for (const panel of this.debugVisualizations.keys()) {
|
for (const panel of this.openedWebviews.keys()) {
|
||||||
panel.dispose();
|
panel.dispose();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -28,40 +36,54 @@ export class WebViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createNew(expression: string | undefined = undefined) {
|
public createNew(expression: string | undefined = undefined) {
|
||||||
const panel = window.createWebviewPanel(
|
const webviewPanel = window.createWebviewPanel(
|
||||||
debugVisualizer,
|
debugVisualizer,
|
||||||
"Debug Visualizer",
|
"Debug Visualizer",
|
||||||
ViewColumn.Two,
|
ViewColumn.Two,
|
||||||
{ enableScripts: true }
|
{
|
||||||
|
enableScripts: true,
|
||||||
|
retainContextWhenHidden: true,
|
||||||
|
portMapping: [
|
||||||
|
{
|
||||||
|
webviewPort: this.server.port,
|
||||||
|
extensionHostPort: this.server.port,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setupView(panel, expression);
|
this.initializeView(webviewPanel, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public restore(webviewPanel: WebviewPanel) {
|
private restore(webviewPanel: WebviewPanel) {
|
||||||
this.setupView(webviewPanel);
|
this.initializeView(webviewPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupView(
|
private initializeView(
|
||||||
webviewPanel: WebviewPanel,
|
webviewPanel: WebviewPanel,
|
||||||
expression: string | undefined = undefined
|
expression: string | undefined = undefined
|
||||||
) {
|
) {
|
||||||
webviewPanel.webview.html = getHtml(this.server, expression);
|
webviewPanel.webview.html = getDebugVisualizerWebviewHtml(
|
||||||
const view = new WebView(webviewPanel);
|
this.server,
|
||||||
this.debugVisualizations.set(webviewPanel, view);
|
expression,
|
||||||
|
this.config
|
||||||
|
);
|
||||||
|
const view = new DebugVisualizerWebview(webviewPanel);
|
||||||
|
this.openedWebviews.set(webviewPanel, view);
|
||||||
webviewPanel.onDidDispose(() => {
|
webviewPanel.onDidDispose(() => {
|
||||||
this.debugVisualizations.delete(webviewPanel);
|
this.openedWebviews.delete(webviewPanel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WebView {
|
export class DebugVisualizerWebview {
|
||||||
constructor(private readonly webviewPanel: WebviewPanel) {}
|
constructor(private readonly webviewPanel: WebviewPanel) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHtml(
|
function getDebugVisualizerWebviewHtml(
|
||||||
server: Server,
|
server: WebviewServer,
|
||||||
expression: string | undefined = undefined
|
initialExpression: string | undefined = undefined,
|
||||||
|
config: Config
|
||||||
) {
|
) {
|
||||||
const isDev = !!process.env.USE_DEV_UI;
|
const isDev = !!process.env.USE_DEV_UI;
|
||||||
return `
|
return `
|
||||||
@@ -77,12 +99,15 @@ export function getHtml(
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script>
|
<script>
|
||||||
window.webViewData = ${JSON.stringify({
|
Object.assign(window, ${JSON.stringify({
|
||||||
serverSecret: server.secret,
|
webviewData: {
|
||||||
serverPort: server.port,
|
serverSecret: server.secret,
|
||||||
publicPath: server.publicPath,
|
serverPort: server.port,
|
||||||
expression,
|
publicPath: server.publicPath,
|
||||||
})};
|
expression: initialExpression,
|
||||||
|
theme: config.theme,
|
||||||
|
},
|
||||||
|
} as WindowWithWebviewData)});
|
||||||
const api = window.VsCodeApi = acquireVsCodeApi();
|
const api = window.VsCodeApi = acquireVsCodeApi();
|
||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
if (event.source === window.frames[0]) {
|
if (event.source === window.frames[0]) {
|
||||||
@@ -100,10 +125,13 @@ export function getHtml(
|
|||||||
|
|
||||||
${
|
${
|
||||||
isDev
|
isDev
|
||||||
? `<iframe sandbox="allow-top-navigation allow-scripts allow-same-origin allow-popups allow-pointer-lock allow-forms" src="${server.getIndexUrl(
|
? `<iframe sandbox="allow-top-navigation allow-scripts allow-same-origin allow-popups allow-pointer-lock allow-forms" src="${server.getWebviewPageUrl(
|
||||||
{ mode: "webViewIFrame", expression }
|
{
|
||||||
|
mode: "webviewIFrame",
|
||||||
|
expression: initialExpression,
|
||||||
|
}
|
||||||
)}"></iframe>`
|
)}"></iframe>`
|
||||||
: `<script type="text/javascript" src="${server.mainBundleUrl}"></script>`
|
: `<script type="text/javascript" src="${server.webviewBundleUrl}"></script>`
|
||||||
}
|
}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
import { Disposable } from "@hediet/std/disposable";
|
import { Disposable } from "@hediet/std/disposable";
|
||||||
import { debugVisualizerUIContract } from "./contract";
|
import { webviewContract } from "../webviewContract";
|
||||||
import { ConsoleRpcLogger, RequestHandlingError } from "@hediet/typed-json-rpc";
|
import { ConsoleRpcLogger, RequestHandlingError } from "@hediet/typed-json-rpc";
|
||||||
import {
|
import {
|
||||||
EvaluationWatcher,
|
EvaluationWatcher,
|
||||||
EvaluationWatchService,
|
EvaluationWatchService,
|
||||||
} from "./EvaluationWatchService";
|
} from "../EvaluationWatchService";
|
||||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||||
import { observable, autorun } from "mobx";
|
import { observable, autorun } from "mobx";
|
||||||
import { Server } from "./Server";
|
import { WebviewServer } from "./WebviewServer";
|
||||||
import * as open from "open";
|
import * as open from "open";
|
||||||
import chromeLauncher = require("chrome-launcher");
|
import chromeLauncher = require("chrome-launcher");
|
||||||
import { Config } from "./Config";
|
import { Config } from "../Config";
|
||||||
|
|
||||||
export class ClientConnection {
|
export class WebviewConnection {
|
||||||
public readonly dispose = Disposable.fn();
|
public readonly dispose = Disposable.fn();
|
||||||
@observable
|
@observable
|
||||||
private watcher: EvaluationWatcher | undefined = undefined;
|
private watcher: EvaluationWatcher | undefined = undefined;
|
||||||
|
|
||||||
private readonly client: typeof debugVisualizerUIContract["TClientInterface"];
|
private readonly client: typeof webviewContract["TClientInterface"];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
evaluationWatchService: EvaluationWatchService,
|
evaluationWatchService: EvaluationWatchService,
|
||||||
stream: WebSocketStream,
|
stream: WebSocketStream,
|
||||||
server: Server,
|
server: WebviewServer,
|
||||||
config: Config,
|
config: Config,
|
||||||
serverSecret: string
|
serverSecret: string
|
||||||
) {
|
) {
|
||||||
@@ -34,10 +34,7 @@ export class ClientConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { client, channel } = webviewContract.registerServerToStream(
|
||||||
client,
|
|
||||||
channel,
|
|
||||||
} = debugVisualizerUIContract.registerServerToStream(
|
|
||||||
stream,
|
stream,
|
||||||
new ConsoleRpcLogger(),
|
new ConsoleRpcLogger(),
|
||||||
{
|
{
|
||||||
@@ -76,7 +73,7 @@ export class ClientConnection {
|
|||||||
openInBrowser: async ({}) => {
|
openInBrowser: async ({}) => {
|
||||||
throwIfNotAuthenticated();
|
throwIfNotAuthenticated();
|
||||||
|
|
||||||
const url = server.getIndexUrl({
|
const url = server.getWebviewPageUrl({
|
||||||
mode: "standalone",
|
mode: "standalone",
|
||||||
expression: this.watcher
|
expression: this.watcher
|
||||||
? this.watcher.expression
|
? this.watcher.expression
|
||||||
@@ -141,6 +138,10 @@ export class ClientConnection {
|
|||||||
public setExpression(expression: string) {
|
public setExpression(expression: string) {
|
||||||
this.client.setExpression({ expression });
|
this.client.setExpression({ expression });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setTheme(theme: "light" | "dark"): void {
|
||||||
|
this.client.setTheme({ theme });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launchChrome(url: string): Promise<boolean> {
|
async function launchChrome(url: string): Promise<boolean> {
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||||
import { AddressInfo } from "net";
|
import { AddressInfo } from "net";
|
||||||
import WebSocket = require("ws");
|
import WebSocket = require("ws");
|
||||||
import { ClientConnection } from "./ClientConnection";
|
import { WebviewConnection } from "./WebviewConnection";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
import * as serveStatic from "serve-static";
|
import * as serveStatic from "serve-static";
|
||||||
import { Config } from "./Config";
|
import { Config } from "../Config";
|
||||||
import cryptoRandomString = require("crypto-random-string");
|
import cryptoRandomString = require("crypto-random-string");
|
||||||
import { distPath } from "debug-visualizer-webview";
|
import { distPath } from "debug-visualizer-webview";
|
||||||
import { EvaluationWatchService } from "./EvaluationWatchService/EvaluationWatchService";
|
import { EvaluationWatchService } from "../EvaluationWatchService/EvaluationWatchService";
|
||||||
|
import { URLSearchParams } from "url";
|
||||||
|
import { autorun, reaction } from "mobx";
|
||||||
|
|
||||||
export class Server {
|
export class WebviewServer {
|
||||||
private server: http.Server;
|
private readonly server: http.Server;
|
||||||
public readonly secret = cryptoRandomString({ length: 20 });
|
public readonly secret = cryptoRandomString({ length: 30 });
|
||||||
|
|
||||||
public readonly connections = new Set<ClientConnection>();
|
public readonly connections = new Set<WebviewConnection>();
|
||||||
|
|
||||||
constructor(dataSource: EvaluationWatchService, config: Config) {
|
constructor(
|
||||||
|
dataSource: EvaluationWatchService,
|
||||||
|
private readonly config: Config
|
||||||
|
) {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(serveStatic(distPath));
|
app.use(serveStatic(distPath));
|
||||||
@@ -32,7 +37,7 @@ export class Server {
|
|||||||
const wss = new WebSocket.Server({ server: this.server });
|
const wss = new WebSocket.Server({ server: this.server });
|
||||||
wss.on("connection", async ws => {
|
wss.on("connection", async ws => {
|
||||||
const stream = new WebSocketStream(ws);
|
const stream = new WebSocketStream(ws);
|
||||||
const c = new ClientConnection(
|
const c = new WebviewConnection(
|
||||||
dataSource,
|
dataSource,
|
||||||
stream,
|
stream,
|
||||||
this,
|
this,
|
||||||
@@ -43,27 +48,42 @@ export class Server {
|
|||||||
await stream.onClosed;
|
await stream.onClosed;
|
||||||
this.connections.delete(c);
|
this.connections.delete(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reaction(
|
||||||
|
() => config.theme,
|
||||||
|
theme => {
|
||||||
|
for (const c of this.connections) {
|
||||||
|
c.setTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getIndexUrl(args: {
|
public getWebviewPageUrl(args: {
|
||||||
expression?: string;
|
expression?: string;
|
||||||
mode: "standalone" | "webViewIFrame";
|
mode: "standalone" | "webviewIFrame";
|
||||||
}): string {
|
}): string {
|
||||||
const port = process.env.USE_DEV_UI ? 8080 : this.port;
|
const port = process.env.USE_DEV_UI ? 8080 : this.port;
|
||||||
const expr =
|
const params: Record<string, string> = {
|
||||||
args.expression !== undefined
|
serverPort: this.port.toString(),
|
||||||
? `&expression=${encodeURIComponent(args.expression)}`
|
serverSecret: this.secret,
|
||||||
: "";
|
mode: args.mode,
|
||||||
const inWebView =
|
theme: this.config.theme,
|
||||||
args.mode === "standalone" ? "" : "&mode=webViewIFrame";
|
};
|
||||||
return `http://localhost:${port}/index.html?serverPort=${this.port}&serverSecret=${this.secret}${inWebView}${expr}`;
|
if (args.expression !== undefined) {
|
||||||
|
params.expression = args.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `http://localhost:${port}/index.html?${new URLSearchParams(
|
||||||
|
params
|
||||||
|
).toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get publicPath(): string {
|
public get publicPath(): string {
|
||||||
return `http://localhost:${this.port}/`;
|
return `http://localhost:${this.port}/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get mainBundleUrl(): string {
|
public get webviewBundleUrl(): string {
|
||||||
return `${this.publicPath}main.js`;
|
return `${this.publicPath}main.js`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,6 +9,83 @@ import {
|
|||||||
DataExtractorId,
|
DataExtractorId,
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
} from "@hediet/debug-visualizer-data-extraction";
|
||||||
|
|
||||||
|
interface WebviewConfig {
|
||||||
|
serverSecret: string;
|
||||||
|
serverPort: number;
|
||||||
|
publicPath: string;
|
||||||
|
expression?: string;
|
||||||
|
theme: "light" | "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebviewUrlParams {
|
||||||
|
serverPort: string;
|
||||||
|
serverSecret: string;
|
||||||
|
mode: "standalone" | "webviewIFrame";
|
||||||
|
expression: string;
|
||||||
|
theme: "light" | "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WindowWithWebviewData {
|
||||||
|
webviewData?: WebviewConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const webviewContract = contract({
|
||||||
|
client: {
|
||||||
|
updateState: notificationContract({
|
||||||
|
params: types.type({
|
||||||
|
newState: unchecked<DataExtractionState>(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
updateLanguageId: notificationContract({
|
||||||
|
params: types.type({
|
||||||
|
languageId: types.union([types.null, types.string]),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
setExpression: requestContract({
|
||||||
|
params: types.type({
|
||||||
|
expression: types.string,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
setTheme: notificationContract({
|
||||||
|
params: types.type({
|
||||||
|
theme: types.union([
|
||||||
|
types.literal("light"),
|
||||||
|
types.literal("dark"),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
authenticate: requestContract({
|
||||||
|
params: types.type({
|
||||||
|
secret: types.string,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
setPreferredDataExtractor: notificationContract({
|
||||||
|
params: types.type({
|
||||||
|
dataExtractorId: unchecked<DataExtractorId>(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
setExpression: notificationContract({
|
||||||
|
params: types.type({
|
||||||
|
newExpression: types.string,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
refresh: notificationContract({}),
|
||||||
|
openInBrowser: notificationContract({}),
|
||||||
|
|
||||||
|
getCompletions: requestContract({
|
||||||
|
params: types.type({
|
||||||
|
text: types.string,
|
||||||
|
column: types.number,
|
||||||
|
}),
|
||||||
|
result: types.type({
|
||||||
|
completions: types.array(unchecked<CompletionItem>()),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function unchecked<T>(): types.Type<T, any, unknown> {
|
function unchecked<T>(): types.Type<T, any, unknown> {
|
||||||
return new types.Type<T, T, unknown>(
|
return new types.Type<T, T, unknown>(
|
||||||
"unchecked",
|
"unchecked",
|
||||||
@@ -92,52 +169,3 @@ export interface CompletionItem {
|
|||||||
*/
|
*/
|
||||||
length?: number;
|
length?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const debugVisualizerUIContract = contract({
|
|
||||||
client: {
|
|
||||||
updateState: notificationContract({
|
|
||||||
params: types.type({
|
|
||||||
newState: unchecked<DataExtractionState>(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
updateLanguageId: notificationContract({
|
|
||||||
params: types.type({
|
|
||||||
languageId: types.union([types.null, types.string]),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
setExpression: requestContract({
|
|
||||||
params: types.type({
|
|
||||||
expression: types.string,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
authenticate: requestContract({
|
|
||||||
params: types.type({
|
|
||||||
secret: types.string,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
setPreferredDataExtractor: notificationContract({
|
|
||||||
params: types.type({
|
|
||||||
dataExtractorId: unchecked<DataExtractorId>(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
setExpression: notificationContract({
|
|
||||||
params: types.type({
|
|
||||||
newExpression: types.string,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
refresh: notificationContract({}),
|
|
||||||
openInBrowser: notificationContract({}),
|
|
||||||
|
|
||||||
getCompletions: requestContract({
|
|
||||||
params: types.type({
|
|
||||||
text: types.string,
|
|
||||||
column: types.number,
|
|
||||||
}),
|
|
||||||
result: types.type({
|
|
||||||
completions: types.array(unchecked<CompletionItem>()),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
require("ts-node").register();
|
|
||||||
module.exports = require("./webpack.config.ts");
|
|
||||||
@@ -21,6 +21,7 @@ module.exports = {
|
|||||||
"@hediet/debug-visualizer-data-extraction":
|
"@hediet/debug-visualizer-data-extraction":
|
||||||
"@hediet/debug-visualizer-data-extraction",
|
"@hediet/debug-visualizer-data-extraction",
|
||||||
"debug-visualizer-webview": "debug-visualizer-webview",
|
"debug-visualizer-webview": "debug-visualizer-webview",
|
||||||
|
fsevents: "require('fsevents')"
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".ts", ".js"],
|
extensions: [".ts", ".js"],
|
||||||
|
|||||||
3813
extension/yarn.lock
3813
extension/yarn.lock
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -3,15 +3,15 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./data-extraction",
|
"./data-extraction",
|
||||||
"./extension",
|
"./extension",
|
||||||
"./webview",
|
"./webview"
|
||||||
"./visualization",
|
|
||||||
"./visualization-playground"
|
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn build-data-extraction && yarn build-extension && yarn build-visualization && yarn build-ui",
|
"build": "yarn build-data-extraction && yarn build-ui && yarn build-extension",
|
||||||
"build-data-extraction": "yarn workspace @hediet/debug-visualizer-data-extraction build",
|
"build-data-extraction": "yarn workspace @hediet/debug-visualizer-data-extraction build",
|
||||||
"build-extension": "yarn workspace debug-visualizer build",
|
"build-ui": "yarn workspace debug-visualizer-webview build",
|
||||||
"build-visualization": "yarn workspace @hediet/visualization build",
|
"build-extension": "yarn workspace debug-visualizer build"
|
||||||
"build-ui": "yarn workspace debug-visualizer-webview build"
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^1.19.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "visualization-playground",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "webpack-dev-server --hot --port 8090",
|
|
||||||
"dev-fast": "webpack-dev-server --hot --port 8090 --fast",
|
|
||||||
"build": "webpack",
|
|
||||||
"pub": "gh-pages -d dist"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@blueprintjs/core": "^3.23.1",
|
|
||||||
"@hediet/std": "^0.6.0",
|
|
||||||
"blueprintjs": "^0.0.8",
|
|
||||||
"classnames": "^2.2.6",
|
|
||||||
"mobx": "^5.15.4",
|
|
||||||
"mobx-react": "^6.1.8",
|
|
||||||
"monaco-editor": "^0.20.0",
|
|
||||||
"popper.js": "^1.16.1",
|
|
||||||
"react": "^16.12.0",
|
|
||||||
"react-dom": "^16.12.0",
|
|
||||||
"react-measure": "^2.3.0",
|
|
||||||
"@hediet/debug-visualizer-data-extraction": "*",
|
|
||||||
"react-autosize-textarea": "^7.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"gh-pages": "^2.2.0",
|
|
||||||
"@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",
|
|
||||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
|
||||||
"node-sass": "^4.13.1",
|
|
||||||
"raw-loader": "^4.0.0",
|
|
||||||
"sass": "^1.25.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { Button, Spinner } from "@blueprintjs/core";
|
|
||||||
import { observable, action } from "mobx";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { Model } from "../model/Model";
|
|
||||||
import classnames = require("classnames");
|
|
||||||
import { knownVisualizations } from "@hediet/visualization";
|
|
||||||
import { CommonDataType } from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import { LightExpressionInput } from "./LightExpressionInput";
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class GUI extends React.Component<{ model: Model }> {
|
|
||||||
render() {
|
|
||||||
const m = this.props.model;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="component-GUI"
|
|
||||||
style={{ display: "flex", flexDirection: "column" }}
|
|
||||||
>
|
|
||||||
<div className="part-header">
|
|
||||||
<LightExpressionInput model={m.jsonExpression} />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="part-visualization"
|
|
||||||
style={{ minHeight: 0, flex: 1 }}
|
|
||||||
>
|
|
||||||
{this.renderVisualization()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderVisualization() {
|
|
||||||
const m = this.props.model;
|
|
||||||
|
|
||||||
if (!m.visualizations) {
|
|
||||||
return <div>No/Invalid Data</div>;
|
|
||||||
}
|
|
||||||
if (!m.visualizations.visualization) {
|
|
||||||
return <div>Data cannot be visualized</div>;
|
|
||||||
}
|
|
||||||
return m.visualizations.visualization.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { observable, action } from "mobx";
|
|
||||||
import TextareaAutosize from "react-autosize-textarea";
|
|
||||||
|
|
||||||
export class ExpressionModel {
|
|
||||||
@observable
|
|
||||||
private _value: string;
|
|
||||||
|
|
||||||
constructor(initialValue: string) {
|
|
||||||
this._value = initialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get value(): string {
|
|
||||||
return this._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setValue(newValue: string): void {
|
|
||||||
this._value = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class LightExpressionInput extends React.Component<{
|
|
||||||
model: ExpressionModel;
|
|
||||||
}> {
|
|
||||||
@observable localText: string | undefined = undefined;
|
|
||||||
|
|
||||||
get text(): string {
|
|
||||||
if (this.localText !== undefined) {
|
|
||||||
return this.localText;
|
|
||||||
}
|
|
||||||
return this.props.model.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<TextareaAutosize
|
|
||||||
value={this.text}
|
|
||||||
spellCheck={false}
|
|
||||||
onChange={e => (this.localText = e.currentTarget.value)}
|
|
||||||
onKeyDown={e => {
|
|
||||||
if (e.keyCode == 9 || e.which == 9) {
|
|
||||||
// enable tab
|
|
||||||
const target = e.currentTarget;
|
|
||||||
e.preventDefault();
|
|
||||||
var s = target.selectionStart;
|
|
||||||
target.value =
|
|
||||||
target.value.substring(
|
|
||||||
0,
|
|
||||||
target.selectionStart
|
|
||||||
) +
|
|
||||||
"\t" +
|
|
||||||
target.value.substring(target.selectionEnd);
|
|
||||||
target.selectionEnd = s + 1;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
resize: "none",
|
|
||||||
}}
|
|
||||||
onBlur={this.handleBlur}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action.bound
|
|
||||||
private handleBlur() {
|
|
||||||
if (this.localText !== undefined) {
|
|
||||||
this.props.model.setValue(this.localText);
|
|
||||||
}
|
|
||||||
this.localText = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import * as ReactDOM from "react-dom";
|
|
||||||
import "./style.scss";
|
|
||||||
import { Model } from "./model/Model";
|
|
||||||
import Components = require("./components/GUI");
|
|
||||||
|
|
||||||
const model = new Model();
|
|
||||||
|
|
||||||
if (model.theme === "light") {
|
|
||||||
require("./vscode-light.scss");
|
|
||||||
} else {
|
|
||||||
require("./vscode-dark.scss");
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(target: HTMLDivElement) {
|
|
||||||
const c = require("./components/GUI") as typeof Components;
|
|
||||||
ReactDOM.render(<c.GUI model={model} />, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = document.createElement("div");
|
|
||||||
target.className = "target";
|
|
||||||
document.body.appendChild(target);
|
|
||||||
|
|
||||||
render(target);
|
|
||||||
|
|
||||||
declare var module: {
|
|
||||||
hot?: { accept: (componentName: string, callback: () => void) => void };
|
|
||||||
};
|
|
||||||
declare var require: (name: string) => any;
|
|
||||||
|
|
||||||
if (module.hot) {
|
|
||||||
module.hot.accept("./components/GUI", () => {
|
|
||||||
render(target);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import { observable, action, computed, when } from "mobx";
|
|
||||||
import {
|
|
||||||
Visualization,
|
|
||||||
VisualizationId,
|
|
||||||
knownVisualizations,
|
|
||||||
} from "@hediet/visualization";
|
|
||||||
import { Disposable } from "@hediet/std/disposable";
|
|
||||||
import { ExpressionModel } from "../components/LightExpressionInput";
|
|
||||||
import { ExtractedData } from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import { QueryController } from "./QueryController";
|
|
||||||
|
|
||||||
export class Model {
|
|
||||||
public readonly dispose = Disposable.fn();
|
|
||||||
|
|
||||||
@observable
|
|
||||||
public theme: "dark" | "light" = "light";
|
|
||||||
|
|
||||||
@observable private preferredVisualizationId:
|
|
||||||
| VisualizationId
|
|
||||||
| undefined = undefined;
|
|
||||||
|
|
||||||
@action
|
|
||||||
public setPreferredVisualizationId(id: VisualizationId) {
|
|
||||||
this.preferredVisualizationId = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public jsonExpression = new ExpressionModel(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
kind: { graph: true },
|
|
||||||
nodes: [
|
|
||||||
{ id: "1", label: "1" },
|
|
||||||
{ id: "2", label: "2" },
|
|
||||||
],
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
from: "1",
|
|
||||||
to: "2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
4
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
get state():
|
|
||||||
| { kind: "data"; data: ExtractedData }
|
|
||||||
| { kind: "parseError" } {
|
|
||||||
const json = this.jsonExpression.value;
|
|
||||||
let data: ExtractedData;
|
|
||||||
try {
|
|
||||||
data = JSON.parse(json);
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
kind: "parseError",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: "data",
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get visualizations():
|
|
||||||
| {
|
|
||||||
visualization: Visualization | undefined;
|
|
||||||
allVisualizations: Visualization[];
|
|
||||||
}
|
|
||||||
| undefined {
|
|
||||||
if (this.state.kind === "data") {
|
|
||||||
const vis = knownVisualizations.getBestVisualization(
|
|
||||||
this.state.data,
|
|
||||||
this.preferredVisualizationId
|
|
||||||
);
|
|
||||||
return vis;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly q = new QueryController(this);
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
|
|
||||||
const theme = url.searchParams.get("theme");
|
|
||||||
if (theme && theme === "dark") {
|
|
||||||
this.theme = "dark";
|
|
||||||
} else {
|
|
||||||
this.theme = "light";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import * as monaco from "monaco-editor";
|
|
||||||
import { Model } from "./Model";
|
|
||||||
import { Disposable } from "@hediet/std/disposable";
|
|
||||||
import { autorun } from "mobx";
|
|
||||||
|
|
||||||
export class MonacoBridge {
|
|
||||||
public readonly dispose = Disposable.fn();
|
|
||||||
|
|
||||||
constructor(private readonly model: Model) {
|
|
||||||
this.dispose.track({
|
|
||||||
dispose: autorun(() => {
|
|
||||||
monaco.editor.setTheme(
|
|
||||||
model.theme === "light" ? "vs-light" : "vs-dark"
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { Model } from "./Model";
|
|
||||||
import { autorun } from "mobx";
|
|
||||||
|
|
||||||
export class QueryController {
|
|
||||||
constructor(private readonly model: Model) {
|
|
||||||
this.loadFromQuery();
|
|
||||||
autorun(() => {
|
|
||||||
this.updateQuery();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFromQuery() {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
const val = url.searchParams.get("value");
|
|
||||||
if (val) {
|
|
||||||
this.model.jsonExpression.setValue(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateQuery() {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
url.searchParams.set("value", this.model.jsonExpression.value);
|
|
||||||
|
|
||||||
history.replaceState(null, document.title, url.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
@import "~normalize.css";
|
|
||||||
@import "~@blueprintjs/core/lib/css/blueprint.css";
|
|
||||||
@import "~@blueprintjs/icons/lib/css/blueprint-icons.css";
|
|
||||||
@import "~@hediet/visualization/style.scss";
|
|
||||||
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
.target {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
border: 0px;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
background-color: var(--vscode-editor-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-input,
|
|
||||||
.bp3-button {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-icon {
|
|
||||||
svg {
|
|
||||||
color: lighten(black, 20);
|
|
||||||
width: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-GUI {
|
|
||||||
height: 100%;
|
|
||||||
& > .part-Header {
|
|
||||||
//padding: 6px 20px 6px 0;
|
|
||||||
//background-color: var(--vscode-sideBar-background);
|
|
||||||
|
|
||||||
// border-top: solid 1px var(--vscode-button-background);
|
|
||||||
// rgba(128, 128, 128, 0.15)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-monaco-editor {
|
|
||||||
width: 100%;
|
|
||||||
min-width: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
padding: 2px;
|
|
||||||
border: 0.5px solid gray;
|
|
||||||
|
|
||||||
.part-editor {
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.current-line {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
.selectionHighlight {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-transition-container {
|
|
||||||
z-index: 100000;
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
:root {
|
|
||||||
--background-color: #263238;
|
|
||||||
--color: #eeffff;
|
|
||||||
--font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", HelveticaNeue-Light, Ubuntu, "Droid Sans", sans-serif;
|
|
||||||
--font-size: 13px;
|
|
||||||
--font-weight: normal;
|
|
||||||
--link-active-color: #eeffff;
|
|
||||||
--link-color: #80cbc4;
|
|
||||||
--vscode-activityBar-background: #192227;
|
|
||||||
--vscode-activityBar-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-activityBar-dropBackground: rgba(255, 255, 255, 0.12);
|
|
||||||
--vscode-activityBar-foreground: #eeffff;
|
|
||||||
--vscode-activityBar-inactiveForeground: rgba(238, 255, 255, 0.6);
|
|
||||||
--vscode-activityBarBadge-background: #80cbc4;
|
|
||||||
--vscode-activityBarBadge-foreground: #000000;
|
|
||||||
--vscode-badge-background: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-badge-foreground: #546e7a;
|
|
||||||
--vscode-breadcrumb-activeSelectionForeground: #80cbc4;
|
|
||||||
--vscode-breadcrumb-background: #263238;
|
|
||||||
--vscode-breadcrumb-focusForeground: #eeffff;
|
|
||||||
--vscode-breadcrumb-foreground: #5f7a87;
|
|
||||||
--vscode-breadcrumbPicker-background: #192227;
|
|
||||||
--vscode-button-background: rgba(128, 203, 196, 0.13);
|
|
||||||
--vscode-button-foreground: #ffffff;
|
|
||||||
--vscode-button-hoverBackground: rgba(175, 222, 218, 0.13);
|
|
||||||
--vscode-debugExceptionWidget-background: #420b0d;
|
|
||||||
--vscode-debugExceptionWidget-border: #a31515;
|
|
||||||
--vscode-debugToolBar-background: #263238;
|
|
||||||
--vscode-descriptionForeground: rgba(204, 204, 204, 0.7);
|
|
||||||
--vscode-diffEditor-insertedTextBackground: rgba(195, 232, 141, 0.08);
|
|
||||||
--vscode-diffEditor-removedTextBackground: rgba(255, 83, 112, 0.13);
|
|
||||||
--vscode-dropdown-background: #263238;
|
|
||||||
--vscode-dropdown-border: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-dropdown-foreground: #f0f0f0;
|
|
||||||
--vscode-editor-background: #263238;
|
|
||||||
--vscode-editor-findMatchBackground: #515c6a;
|
|
||||||
--vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
|
|
||||||
--vscode-editor-findRangeHighlightBackground: rgba(58, 61, 65, 0.4);
|
|
||||||
--vscode-editor-focusedStackFrameHighlightBackground: rgba(122, 189, 122, 0.3);
|
|
||||||
--vscode-editor-font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", HelveticaNeue-Light, Ubuntu, "Droid Sans", sans-serif;
|
|
||||||
--vscode-editor-font-size: 13px;
|
|
||||||
--vscode-editor-font-weight: normal;
|
|
||||||
--vscode-editor-foreground: #eeffff;
|
|
||||||
--vscode-editor-hoverHighlightBackground: rgba(38, 79, 120, 0.25);
|
|
||||||
--vscode-editor-inactiveSelectionBackground: rgba(128, 203, 196, 0.06);
|
|
||||||
--vscode-editor-lineHighlightBackground: rgba(0, 0, 0, 0.31);
|
|
||||||
--vscode-editor-lineHighlightBorder: #282828;
|
|
||||||
--vscode-editor-rangeHighlightBackground: rgba(255, 255, 255, 0.04);
|
|
||||||
--vscode-editor-selectionBackground: rgba(128, 203, 196, 0.13);
|
|
||||||
--vscode-editor-selectionHighlightBackground: rgba(255, 204, 0, 0.13);
|
|
||||||
--vscode-editor-snippetFinalTabstopHighlightBorder: #525252;
|
|
||||||
--vscode-editor-snippetTabstopHighlightBackground: rgba(124, 124, 124, 0.3);
|
|
||||||
--vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 0, 0.2);
|
|
||||||
--vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.72);
|
|
||||||
--vscode-editor-wordHighlightStrongBackground: rgba(0, 73, 114, 0.72);
|
|
||||||
--vscode-editorActiveLineNumber-foreground: #c6c6c6;
|
|
||||||
--vscode-editorBracketMatch-background: #263238;
|
|
||||||
--vscode-editorBracketMatch-border: rgba(255, 204, 0, 0.31);
|
|
||||||
--vscode-editorCodeLens-foreground: #999999;
|
|
||||||
--vscode-editorCursor-foreground: #ffcc00;
|
|
||||||
--vscode-editorError-foreground: rgba(255, 83, 112, 0.44);
|
|
||||||
--vscode-editorGroup-border: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-editorGroup-dropBackground: rgba(83, 89, 93, 0.5);
|
|
||||||
--vscode-editorGroupHeader-noTabsBackground: #263238;
|
|
||||||
--vscode-editorGroupHeader-tabsBackground: #263238;
|
|
||||||
--vscode-editorGutter-addedBackground: rgba(195, 232, 141, 0.38);
|
|
||||||
--vscode-editorGutter-background: #263238;
|
|
||||||
--vscode-editorGutter-commentRangeForeground: #c5c5c5;
|
|
||||||
--vscode-editorGutter-deletedBackground: rgba(255, 83, 112, 0.38);
|
|
||||||
--vscode-editorGutter-modifiedBackground: rgba(130, 170, 255, 0.38);
|
|
||||||
--vscode-editorHint-foreground: rgba(238, 238, 238, 0.7);
|
|
||||||
--vscode-editorHoverWidget-background: #263238;
|
|
||||||
--vscode-editorHoverWidget-border: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-editorIndentGuide-activeBackground: #37474f;
|
|
||||||
--vscode-editorIndentGuide-background: rgba(55, 71, 79, 0.44);
|
|
||||||
--vscode-editorInfo-foreground: #008000;
|
|
||||||
--vscode-editorLineNumber-activeForeground: #5f7a87;
|
|
||||||
--vscode-editorLineNumber-foreground: #37474f;
|
|
||||||
--vscode-editorLink-activeForeground: #eeffff;
|
|
||||||
--vscode-editorMarkerNavigation-background: rgba(238, 255, 255, 0.02);
|
|
||||||
--vscode-editorMarkerNavigationError-background: rgba(255, 83, 112, 0.44);
|
|
||||||
--vscode-editorMarkerNavigationInfo-background: #008000;
|
|
||||||
--vscode-editorMarkerNavigationWarning-background: rgba(195, 232, 141, 0.44);
|
|
||||||
--vscode-editorOverviewRuler-addedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-border: #263238;
|
|
||||||
--vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0;
|
|
||||||
--vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4);
|
|
||||||
--vscode-editorOverviewRuler-currentContentForeground: rgba(64, 200, 174, 0.5);
|
|
||||||
--vscode-editorOverviewRuler-deletedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7);
|
|
||||||
--vscode-editorOverviewRuler-findMatchForeground: rgba(246, 185, 77, 0.7);
|
|
||||||
--vscode-editorOverviewRuler-incomingContentForeground: rgba(64, 166, 255, 0.5);
|
|
||||||
--vscode-editorOverviewRuler-infoForeground: rgba(18, 18, 136, 0.7);
|
|
||||||
--vscode-editorOverviewRuler-modifiedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-selectionHighlightForeground: rgba(160, 160, 160, 0.8);
|
|
||||||
--vscode-editorOverviewRuler-warningForeground: rgba(18, 136, 18, 0.7);
|
|
||||||
--vscode-editorOverviewRuler-wordHighlightForeground: rgba(160, 160, 160, 0.8);
|
|
||||||
--vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(192, 160, 192, 0.8);
|
|
||||||
--vscode-editorPane-background: #263238;
|
|
||||||
--vscode-editorRuler-foreground: #37474f;
|
|
||||||
--vscode-editorSuggestWidget-background: #263238;
|
|
||||||
--vscode-editorSuggestWidget-border: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-editorSuggestWidget-foreground: #eeffff;
|
|
||||||
--vscode-editorSuggestWidget-highlightForeground: #80cbc4;
|
|
||||||
--vscode-editorSuggestWidget-selectedBackground: rgba(0, 0, 0, 0.31);
|
|
||||||
--vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.67);
|
|
||||||
--vscode-editorWarning-foreground: rgba(195, 232, 141, 0.44);
|
|
||||||
--vscode-editorWhitespace-foreground: rgba(238, 255, 255, 0.25);
|
|
||||||
--vscode-editorWidget-background: #192227;
|
|
||||||
--vscode-editorWidget-border: #ff0000;
|
|
||||||
--vscode-editorWidget-resizeBorder: #80cbc4;
|
|
||||||
--vscode-errorForeground: #f48771;
|
|
||||||
--vscode-extensionButton-prominentBackground: rgba(195, 232, 141, 0.56);
|
|
||||||
--vscode-extensionButton-prominentForeground: #ffffff;
|
|
||||||
--vscode-extensionButton-prominentHoverBackground: #c3e88d;
|
|
||||||
--vscode-focusBorder: rgba(255, 255, 255, 0);
|
|
||||||
--vscode-foreground: #cccccc;
|
|
||||||
--vscode-gitDecoration-addedResourceForeground: #81b88b;
|
|
||||||
--vscode-gitDecoration-conflictingResourceForeground: rgba(255, 203, 107, 0.56);
|
|
||||||
--vscode-gitDecoration-deletedResourceForeground: rgba(255, 83, 112, 0.56);
|
|
||||||
--vscode-gitDecoration-ignoredResourceForeground: rgba(95, 122, 135, 0.56);
|
|
||||||
--vscode-gitDecoration-modifiedResourceForeground: rgba(130, 170, 255, 0.56);
|
|
||||||
--vscode-gitDecoration-submoduleResourceForeground: #8db9e2;
|
|
||||||
--vscode-gitDecoration-untrackedResourceForeground: rgba(195, 232, 141, 0.56);
|
|
||||||
--vscode-gitlens-gutterBackgroundColor: rgba(255, 255, 255, 0.07);
|
|
||||||
--vscode-gitlens-gutterForegroundColor: #bebebe;
|
|
||||||
--vscode-gitlens-gutterUncommittedForegroundColor: rgba(0, 188, 242, 0.6);
|
|
||||||
--vscode-gitlens-lineHighlightBackgroundColor: rgba(0, 188, 242, 0.2);
|
|
||||||
--vscode-gitlens-lineHighlightOverviewRulerColor: rgba(0, 188, 242, 0.6);
|
|
||||||
--vscode-gitlens-trailingLineBackgroundColor: rgba(0, 0, 0, 0);
|
|
||||||
--vscode-gitlens-trailingLineForegroundColor: rgba(153, 153, 153, 0.35);
|
|
||||||
--vscode-input-background: #303c41;
|
|
||||||
--vscode-input-border: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-input-foreground: #eeffff;
|
|
||||||
--vscode-input-placeholderForeground: rgba(238, 255, 255, 0.38);
|
|
||||||
--vscode-inputOption-activeBorder: #007acc;
|
|
||||||
--vscode-inputValidation-errorBackground: #5a1d1d;
|
|
||||||
--vscode-inputValidation-errorBorder: rgba(255, 83, 112, 0.31);
|
|
||||||
--vscode-inputValidation-infoBackground: #063b49;
|
|
||||||
--vscode-inputValidation-infoBorder: rgba(130, 170, 255, 0.31);
|
|
||||||
--vscode-inputValidation-warningBackground: #352a05;
|
|
||||||
--vscode-inputValidation-warningBorder: rgba(255, 203, 107, 0.31);
|
|
||||||
--vscode-list-activeSelectionBackground: #192227;
|
|
||||||
--vscode-list-activeSelectionForeground: #80cbc4;
|
|
||||||
--vscode-list-dropBackground: rgba(238, 255, 255, 0.13);
|
|
||||||
--vscode-list-errorForeground: #f88070;
|
|
||||||
--vscode-list-focusBackground: rgba(238, 255, 255, 0.13);
|
|
||||||
--vscode-list-focusForeground: #eeffff;
|
|
||||||
--vscode-list-highlightForeground: #80cbc4;
|
|
||||||
--vscode-list-hoverBackground: #192227;
|
|
||||||
--vscode-list-hoverForeground: #ffffff;
|
|
||||||
--vscode-list-inactiveFocusBackground: #313135;
|
|
||||||
--vscode-list-inactiveSelectionBackground: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-list-inactiveSelectionForeground: #80cbc4;
|
|
||||||
--vscode-list-invalidItemForeground: #b89500;
|
|
||||||
--vscode-list-warningForeground: #4d9e4d;
|
|
||||||
--vscode-menu-background: #263238;
|
|
||||||
--vscode-menu-foreground: #eeffff;
|
|
||||||
--vscode-menu-selectionBackground: rgba(0, 0, 0, 0.31);
|
|
||||||
--vscode-menu-selectionBorder: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-menu-selectionForeground: #80cbc4;
|
|
||||||
--vscode-menu-separatorBackground: #eeffff;
|
|
||||||
--vscode-menubar-selectionBackground: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-menubar-selectionBorder: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-menubar-selectionForeground: #80cbc4;
|
|
||||||
--vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16);
|
|
||||||
--vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4);
|
|
||||||
--vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2);
|
|
||||||
--vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5);
|
|
||||||
--vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2);
|
|
||||||
--vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5);
|
|
||||||
--vscode-notificationCenterHeader-background: #314149;
|
|
||||||
--vscode-notificationLink-foreground: #80cbc4;
|
|
||||||
--vscode-notifications-background: #263238;
|
|
||||||
--vscode-notifications-border: #314149;
|
|
||||||
--vscode-notifications-foreground: #eeffff;
|
|
||||||
--vscode-panel-background: #192227;
|
|
||||||
--vscode-panel-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-panel-dropBackground: #eeffff;
|
|
||||||
--vscode-panelTitle-activeBorder: #80cbc4;
|
|
||||||
--vscode-panelTitle-activeForeground: #ffffff;
|
|
||||||
--vscode-panelTitle-inactiveForeground: #eeffff;
|
|
||||||
--vscode-peekView-border: rgba(0, 0, 0, 0.19);
|
|
||||||
--vscode-peekViewEditor-background: rgba(238, 255, 255, 0.02);
|
|
||||||
--vscode-peekViewEditor-matchHighlightBackground: rgba(128, 203, 196, 0.13);
|
|
||||||
--vscode-peekViewEditorGutter-background: rgba(238, 255, 255, 0.02);
|
|
||||||
--vscode-peekViewResult-background: rgba(238, 255, 255, 0.02);
|
|
||||||
--vscode-peekViewResult-fileForeground: #ffffff;
|
|
||||||
--vscode-peekViewResult-lineForeground: #bbbbbb;
|
|
||||||
--vscode-peekViewResult-matchHighlightBackground: rgba(128, 203, 196, 0.13);
|
|
||||||
--vscode-peekViewResult-selectionBackground: rgba(95, 122, 135, 0.44);
|
|
||||||
--vscode-peekViewResult-selectionForeground: #ffffff;
|
|
||||||
--vscode-peekViewTitle-background: rgba(238, 255, 255, 0.02);
|
|
||||||
--vscode-peekViewTitleDescription-foreground: rgba(238, 255, 255, 0.38);
|
|
||||||
--vscode-peekViewTitleLabel-foreground: #ffffff;
|
|
||||||
--vscode-pickerGroup-border: #3f3f46;
|
|
||||||
--vscode-pickerGroup-foreground: #80cbc4;
|
|
||||||
--vscode-progressBar-background: #80cbc4;
|
|
||||||
--vscode-scrollbar-shadow: rgba(38, 50, 56, 0);
|
|
||||||
--vscode-scrollbarSlider-activeBackground: #80cbc4;
|
|
||||||
--vscode-scrollbarSlider-background: rgba(238, 255, 255, 0.13);
|
|
||||||
--vscode-scrollbarSlider-hoverBackground: rgba(238, 255, 255, 0.06);
|
|
||||||
--vscode-selection-background: #eeffff;
|
|
||||||
--vscode-settings-checkboxBackground: #192227;
|
|
||||||
--vscode-settings-checkboxBorder: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-settings-checkboxForeground: #eeffff;
|
|
||||||
--vscode-settings-dropdownBackground: #192227;
|
|
||||||
--vscode-settings-dropdownBorder: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-settings-dropdownForeground: #eeffff;
|
|
||||||
--vscode-settings-dropdownListBorder: #ff0000;
|
|
||||||
--vscode-settings-headerForeground: #80cbc4;
|
|
||||||
--vscode-settings-modifiedItemIndicator: #80cbc4;
|
|
||||||
--vscode-settings-numberInputBackground: #192227;
|
|
||||||
--vscode-settings-numberInputBorder: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-settings-numberInputForeground: #eeffff;
|
|
||||||
--vscode-settings-textInputBackground: #192227;
|
|
||||||
--vscode-settings-textInputBorder: rgba(255, 255, 255, 0.06);
|
|
||||||
--vscode-settings-textInputForeground: #eeffff;
|
|
||||||
--vscode-sideBar-background: #192227;
|
|
||||||
--vscode-sideBar-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-sideBar-dropBackground: rgba(255, 255, 255, 0.12);
|
|
||||||
--vscode-sideBar-foreground: #5f7a87;
|
|
||||||
--vscode-sideBarSectionHeader-background: #192227;
|
|
||||||
--vscode-sideBarSectionHeader-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-sideBarSectionHeader-foreground: #5f7a87;
|
|
||||||
--vscode-sideBarTitle-foreground: #eeffff;
|
|
||||||
--vscode-statusBar-background: #192227;
|
|
||||||
--vscode-statusBar-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-statusBar-debuggingBackground: #c792ea;
|
|
||||||
--vscode-statusBar-debuggingBorder: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-statusBar-debuggingForeground: #ffffff;
|
|
||||||
--vscode-statusBar-foreground: #546e7a;
|
|
||||||
--vscode-statusBar-noFolderBackground: #263238;
|
|
||||||
--vscode-statusBar-noFolderBorder: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-statusBar-noFolderForeground: #546e7a;
|
|
||||||
--vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18);
|
|
||||||
--vscode-statusBarItem-hoverBackground: rgba(84, 110, 122, 0.13);
|
|
||||||
--vscode-statusBarItem-prominentBackground: #388a34;
|
|
||||||
--vscode-statusBarItem-prominentHoverBackground: #369432;
|
|
||||||
--vscode-tab-activeBackground: #263238;
|
|
||||||
--vscode-tab-activeBorder: #80cbc4;
|
|
||||||
--vscode-tab-activeForeground: #ffffff;
|
|
||||||
--vscode-tab-activeModifiedBorder: #5f7a87;
|
|
||||||
--vscode-tab-border: #263238;
|
|
||||||
--vscode-tab-inactiveBackground: #263238;
|
|
||||||
--vscode-tab-inactiveForeground: #5f7a87;
|
|
||||||
--vscode-tab-inactiveModifiedBorder: rgba(95, 122, 135, 0.5);
|
|
||||||
--vscode-tab-unfocusedActiveBorder: #546e7a;
|
|
||||||
--vscode-tab-unfocusedActiveForeground: #eeffff;
|
|
||||||
--vscode-tab-unfocusedActiveModifiedBorder: rgba(95, 122, 135, 0.5);
|
|
||||||
--vscode-tab-unfocusedInactiveForeground: rgba(95, 122, 135, 0.5);
|
|
||||||
--vscode-tab-unfocusedInactiveModifiedBorder: rgba(95, 122, 135, 0.25);
|
|
||||||
--vscode-terminal-ansiBlack: #000000;
|
|
||||||
--vscode-terminal-ansiBlue: #82aaff;
|
|
||||||
--vscode-terminal-ansiBrightBlack: #546e7a;
|
|
||||||
--vscode-terminal-ansiBrightBlue: #82aaff;
|
|
||||||
--vscode-terminal-ansiBrightCyan: #89ddff;
|
|
||||||
--vscode-terminal-ansiBrightGreen: #c3e88d;
|
|
||||||
--vscode-terminal-ansiBrightMagenta: #c792ea;
|
|
||||||
--vscode-terminal-ansiBrightRed: #ff5370;
|
|
||||||
--vscode-terminal-ansiBrightWhite: #ffffff;
|
|
||||||
--vscode-terminal-ansiBrightYellow: #ffcb6b;
|
|
||||||
--vscode-terminal-ansiCyan: #89ddff;
|
|
||||||
--vscode-terminal-ansiGreen: #c3e88d;
|
|
||||||
--vscode-terminal-ansiMagenta: #c792ea;
|
|
||||||
--vscode-terminal-ansiRed: #ff5370;
|
|
||||||
--vscode-terminal-ansiWhite: #ffffff;
|
|
||||||
--vscode-terminal-ansiYellow: #ffcb6b;
|
|
||||||
--vscode-terminal-background: #192227;
|
|
||||||
--vscode-terminal-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-terminal-foreground: #cccccc;
|
|
||||||
--vscode-terminal-selectionBackground: rgba(255, 255, 255, 0.25);
|
|
||||||
--vscode-textBlockQuote-background: rgba(127, 127, 127, 0.1);
|
|
||||||
--vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5);
|
|
||||||
--vscode-textCodeBlock-background: rgba(10, 10, 10, 0.4);
|
|
||||||
--vscode-textLink-activeForeground: #eeffff;
|
|
||||||
--vscode-textLink-foreground: #80cbc4;
|
|
||||||
--vscode-textPreformat-foreground: #d7ba7d;
|
|
||||||
--vscode-textSeparator-foreground: rgba(255, 255, 255, 0.18);
|
|
||||||
--vscode-titleBar-activeBackground: #192227;
|
|
||||||
--vscode-titleBar-activeForeground: #eeffff;
|
|
||||||
--vscode-titleBar-border: rgba(0, 0, 0, 0.38);
|
|
||||||
--vscode-titleBar-inactiveBackground: #192227;
|
|
||||||
--vscode-titleBar-inactiveForeground: #5f7a87;
|
|
||||||
--vscode-widget-shadow: rgba(0, 0, 0, 0.19);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
:root {
|
|
||||||
--vscode-activityBar-background: #2c2c2c;
|
|
||||||
--vscode-activityBar-dropBackground: rgba(255, 255, 255, 0.12);
|
|
||||||
--vscode-activityBar-foreground: #ffffff;
|
|
||||||
--vscode-activityBar-inactiveForeground: rgba(255, 255, 255, 0.6);
|
|
||||||
--vscode-activityBarBadge-background: #007acc;
|
|
||||||
--vscode-activityBarBadge-foreground: #ffffff;
|
|
||||||
--vscode-badge-background: #c4c4c4;
|
|
||||||
--vscode-badge-foreground: #333333;
|
|
||||||
--vscode-breadcrumb-activeSelectionForeground: #4e4e4e;
|
|
||||||
--vscode-breadcrumb-background: #ffffff;
|
|
||||||
--vscode-breadcrumb-focusForeground: #4e4e4e;
|
|
||||||
--vscode-breadcrumb-foreground: rgba(97, 97, 97, 0.8);
|
|
||||||
--vscode-breadcrumbPicker-background: #f3f3f3;
|
|
||||||
--vscode-button-background: #007acc;
|
|
||||||
--vscode-button-foreground: #ffffff;
|
|
||||||
--vscode-button-hoverBackground: #0062a3;
|
|
||||||
--vscode-debugExceptionWidget-background: #f1dfde;
|
|
||||||
--vscode-debugExceptionWidget-border: #a31515;
|
|
||||||
--vscode-debugToolBar-background: #f3f3f3;
|
|
||||||
--vscode-descriptionForeground: #717171;
|
|
||||||
--vscode-diffEditor-insertedTextBackground: rgba(155, 185, 85, 0.2);
|
|
||||||
--vscode-diffEditor-removedTextBackground: rgba(255, 0, 0, 0.2);
|
|
||||||
--vscode-dropdown-background: #ffffff;
|
|
||||||
--vscode-dropdown-border: #cecece;
|
|
||||||
--vscode-editor-background: #ffffff;
|
|
||||||
--vscode-editor-findMatchBackground: #a8ac94;
|
|
||||||
--vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
|
|
||||||
--vscode-editor-findRangeHighlightBackground: rgba(180, 180, 180, 0.3);
|
|
||||||
--vscode-editor-focusedStackFrameHighlightBackground: rgba(
|
|
||||||
206,
|
|
||||||
231,
|
|
||||||
206,
|
|
||||||
0.45
|
|
||||||
);
|
|
||||||
--vscode-editor-font-family: Consolas, "Courier New", monospace;
|
|
||||||
--vscode-editor-font-size: 14;
|
|
||||||
--vscode-editor-font-weight: normal;
|
|
||||||
--vscode-editor-foreground: #000000;
|
|
||||||
--vscode-editor-hoverHighlightBackground: rgba(173, 214, 255, 0.15);
|
|
||||||
--vscode-editor-inactiveSelectionBackground: #e5ebf1;
|
|
||||||
--vscode-editor-lineHighlightBorder: #eeeeee;
|
|
||||||
--vscode-editor-rangeHighlightBackground: rgba(253, 255, 0, 0.2);
|
|
||||||
--vscode-editor-selectionBackground: #add6ff;
|
|
||||||
--vscode-editor-selectionHighlightBackground: rgba(173, 214, 255, 0.5);
|
|
||||||
--vscode-editor-snippetFinalTabstopHighlightBorder: rgba(10, 50, 100, 0.5);
|
|
||||||
--vscode-editor-snippetTabstopHighlightBackground: rgba(10, 50, 100, 0.2);
|
|
||||||
--vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 102, 0.45);
|
|
||||||
--vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.25);
|
|
||||||
--vscode-editor-wordHighlightStrongBackground: rgba(14, 99, 156, 0.25);
|
|
||||||
--vscode-editorActiveLineNumber-foreground: #0b216f;
|
|
||||||
--vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1);
|
|
||||||
--vscode-editorBracketMatch-border: #b9b9b9;
|
|
||||||
--vscode-editorCodeLens-foreground: #999999;
|
|
||||||
--vscode-editorCursor-foreground: #000000;
|
|
||||||
--vscode-editorError-foreground: #e51400;
|
|
||||||
--vscode-editorGroup-border: #e7e7e7;
|
|
||||||
--vscode-editorGroup-dropBackground: rgba(38, 119, 203, 0.18);
|
|
||||||
--vscode-editorGroupHeader-noTabsBackground: #ffffff;
|
|
||||||
--vscode-editorGroupHeader-tabsBackground: #f3f3f3;
|
|
||||||
--vscode-editorGutter-addedBackground: #81b88b;
|
|
||||||
--vscode-editorGutter-background: #ffffff;
|
|
||||||
--vscode-editorGutter-commentRangeForeground: #c5c5c5;
|
|
||||||
--vscode-editorGutter-deletedBackground: #ca4b51;
|
|
||||||
--vscode-editorGutter-modifiedBackground: #66afe0;
|
|
||||||
--vscode-editorHint-foreground: #6c6c6c;
|
|
||||||
--vscode-editorHoverWidget-background: #f3f3f3;
|
|
||||||
--vscode-editorHoverWidget-border: #c8c8c8;
|
|
||||||
--vscode-editorHoverWidget-statusBarBackground: #e7e7e7;
|
|
||||||
--vscode-editorIndentGuide-activeBackground: #939393;
|
|
||||||
--vscode-editorIndentGuide-background: #d3d3d3;
|
|
||||||
--vscode-editorInfo-foreground: #008000;
|
|
||||||
--vscode-editorLineNumber-activeForeground: #0b216f;
|
|
||||||
--vscode-editorLineNumber-foreground: #237893;
|
|
||||||
--vscode-editorLink-activeForeground: #0000ff;
|
|
||||||
--vscode-editorMarkerNavigation-background: #ffffff;
|
|
||||||
--vscode-editorMarkerNavigationError-background: #e51400;
|
|
||||||
--vscode-editorMarkerNavigationInfo-background: #008000;
|
|
||||||
--vscode-editorMarkerNavigationWarning-background: #e9a700;
|
|
||||||
--vscode-editorOverviewRuler-addedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3);
|
|
||||||
--vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0;
|
|
||||||
--vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4);
|
|
||||||
--vscode-editorOverviewRuler-currentContentForeground: rgba(
|
|
||||||
64,
|
|
||||||
200,
|
|
||||||
174,
|
|
||||||
0.5
|
|
||||||
);
|
|
||||||
--vscode-editorOverviewRuler-deletedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7);
|
|
||||||
--vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49);
|
|
||||||
--vscode-editorOverviewRuler-incomingContentForeground: rgba(
|
|
||||||
64,
|
|
||||||
166,
|
|
||||||
255,
|
|
||||||
0.5
|
|
||||||
);
|
|
||||||
--vscode-editorOverviewRuler-infoForeground: #008000;
|
|
||||||
--vscode-editorOverviewRuler-modifiedForeground: rgba(0, 122, 204, 0.6);
|
|
||||||
--vscode-editorOverviewRuler-rangeHighlightForeground: rgba(
|
|
||||||
0,
|
|
||||||
122,
|
|
||||||
204,
|
|
||||||
0.6
|
|
||||||
);
|
|
||||||
--vscode-editorOverviewRuler-selectionHighlightForeground: rgba(
|
|
||||||
160,
|
|
||||||
160,
|
|
||||||
160,
|
|
||||||
0.8
|
|
||||||
);
|
|
||||||
--vscode-editorOverviewRuler-warningForeground: #e9a700;
|
|
||||||
--vscode-editorOverviewRuler-wordHighlightForeground: rgba(
|
|
||||||
160,
|
|
||||||
160,
|
|
||||||
160,
|
|
||||||
0.8
|
|
||||||
);
|
|
||||||
--vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(
|
|
||||||
192,
|
|
||||||
160,
|
|
||||||
192,
|
|
||||||
0.8
|
|
||||||
);
|
|
||||||
--vscode-editorPane-background: #ffffff;
|
|
||||||
--vscode-editorRuler-foreground: #d3d3d3;
|
|
||||||
--vscode-editorSuggestWidget-background: #f3f3f3;
|
|
||||||
--vscode-editorSuggestWidget-border: #c8c8c8;
|
|
||||||
--vscode-editorSuggestWidget-foreground: #000000;
|
|
||||||
--vscode-editorSuggestWidget-highlightForeground: #0066bf;
|
|
||||||
--vscode-editorSuggestWidget-selectedBackground: #d6ebff;
|
|
||||||
--vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.47);
|
|
||||||
--vscode-editorWarning-foreground: #e9a700;
|
|
||||||
--vscode-editorWhitespace-foreground: rgba(51, 51, 51, 0.2);
|
|
||||||
--vscode-editorWidget-background: #f3f3f3;
|
|
||||||
--vscode-editorWidget-border: #c8c8c8;
|
|
||||||
--vscode-errorForeground: #a1260d;
|
|
||||||
--vscode-extensionBadge-remoteBackground: #007acc;
|
|
||||||
--vscode-extensionBadge-remoteForeground: #ffffff;
|
|
||||||
--vscode-extensionButton-prominentBackground: #327e36;
|
|
||||||
--vscode-extensionButton-prominentForeground: #ffffff;
|
|
||||||
--vscode-extensionButton-prominentHoverBackground: #28632b;
|
|
||||||
--vscode-focusBorder: rgba(0, 122, 204, 0.4);
|
|
||||||
--vscode-font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC",
|
|
||||||
"Segoe UI", "Ubuntu", "Droid Sans", ans-serif;
|
|
||||||
--vscode-font-size: 13px;
|
|
||||||
--vscode-font-weight: normal;
|
|
||||||
--vscode-foreground: #616161;
|
|
||||||
--vscode-gitDecoration-addedResourceForeground: #587c0c;
|
|
||||||
--vscode-gitDecoration-conflictingResourceForeground: #6c6cc4;
|
|
||||||
--vscode-gitDecoration-deletedResourceForeground: #ad0707;
|
|
||||||
--vscode-gitDecoration-ignoredResourceForeground: #8e8e90;
|
|
||||||
--vscode-gitDecoration-modifiedResourceForeground: #895503;
|
|
||||||
--vscode-gitDecoration-submoduleResourceForeground: #1258a7;
|
|
||||||
--vscode-gitDecoration-untrackedResourceForeground: #007100;
|
|
||||||
--vscode-gitlens-gutterBackgroundColor: rgba(0, 0, 0, 0.05);
|
|
||||||
--vscode-gitlens-gutterForegroundColor: #747474;
|
|
||||||
--vscode-gitlens-gutterUncommittedForegroundColor: rgba(0, 188, 242, 0.6);
|
|
||||||
--vscode-gitlens-lineHighlightBackgroundColor: rgba(0, 188, 242, 0.2);
|
|
||||||
--vscode-gitlens-lineHighlightOverviewRulerColor: rgba(0, 188, 242, 0.6);
|
|
||||||
--vscode-gitlens-trailingLineBackgroundColor: rgba(0, 0, 0, 0);
|
|
||||||
--vscode-gitlens-trailingLineForegroundColor: rgba(153, 153, 153, 0.35);
|
|
||||||
--vscode-input-background: #ffffff;
|
|
||||||
--vscode-input-foreground: #616161;
|
|
||||||
--vscode-input-placeholderForeground: #767676;
|
|
||||||
--vscode-inputOption-activeBorder: #007acc;
|
|
||||||
--vscode-inputValidation-errorBackground: #f2dede;
|
|
||||||
--vscode-inputValidation-errorBorder: #be1100;
|
|
||||||
--vscode-inputValidation-infoBackground: #d6ecf2;
|
|
||||||
--vscode-inputValidation-infoBorder: #007acc;
|
|
||||||
--vscode-inputValidation-warningBackground: #f6f5d2;
|
|
||||||
--vscode-inputValidation-warningBorder: #b89500;
|
|
||||||
--vscode-list-activeSelectionBackground: #0074e8;
|
|
||||||
--vscode-list-activeSelectionForeground: #ffffff;
|
|
||||||
--vscode-list-dropBackground: #d6ebff;
|
|
||||||
--vscode-list-errorForeground: #b01011;
|
|
||||||
--vscode-list-focusBackground: #d6ebff;
|
|
||||||
--vscode-list-highlightForeground: #0066bf;
|
|
||||||
--vscode-list-hoverBackground: #e8e8e8;
|
|
||||||
--vscode-list-inactiveSelectionBackground: #e4e6f1;
|
|
||||||
--vscode-list-invalidItemForeground: #b89500;
|
|
||||||
--vscode-list-warningForeground: #855f00;
|
|
||||||
--vscode-listFilterWidget-background: #efc1ad;
|
|
||||||
--vscode-listFilterWidget-noMatchesOutline: #be1100;
|
|
||||||
--vscode-listFilterWidget-outline: rgba(0, 0, 0, 0);
|
|
||||||
--vscode-menu-background: #ffffff;
|
|
||||||
--vscode-menu-foreground: #616161;
|
|
||||||
--vscode-menu-selectionBackground: #0074e8;
|
|
||||||
--vscode-menu-selectionForeground: #ffffff;
|
|
||||||
--vscode-menu-separatorBackground: #888888;
|
|
||||||
--vscode-menubar-selectionBackground: rgba(0, 0, 0, 0.1);
|
|
||||||
--vscode-menubar-selectionForeground: #333333;
|
|
||||||
--vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16);
|
|
||||||
--vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4);
|
|
||||||
--vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2);
|
|
||||||
--vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5);
|
|
||||||
--vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2);
|
|
||||||
--vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5);
|
|
||||||
--vscode-notificationCenterHeader-background: #e7e7e7;
|
|
||||||
--vscode-notificationLink-foreground: #006ab1;
|
|
||||||
--vscode-notifications-background: #f3f3f3;
|
|
||||||
--vscode-notifications-border: #e7e7e7;
|
|
||||||
--vscode-panel-background: #ffffff;
|
|
||||||
--vscode-panel-border: rgba(128, 128, 128, 0.35);
|
|
||||||
--vscode-panel-dropBackground: rgba(38, 119, 203, 0.18);
|
|
||||||
--vscode-panelInput-border: #dddddd;
|
|
||||||
--vscode-panelTitle-activeBorder: rgba(128, 128, 128, 0.35);
|
|
||||||
--vscode-panelTitle-activeForeground: #424242;
|
|
||||||
--vscode-panelTitle-inactiveForeground: rgba(66, 66, 66, 0.75);
|
|
||||||
--vscode-peekView-border: #007acc;
|
|
||||||
--vscode-peekViewEditor-background: #f2f8fc;
|
|
||||||
--vscode-peekViewEditor-matchHighlightBackground: rgba(245, 216, 2, 0.87);
|
|
||||||
--vscode-peekViewEditorGutter-background: #f2f8fc;
|
|
||||||
--vscode-peekViewResult-background: #f3f3f3;
|
|
||||||
--vscode-peekViewResult-fileForeground: #1e1e1e;
|
|
||||||
--vscode-peekViewResult-lineForeground: #646465;
|
|
||||||
--vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3);
|
|
||||||
--vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2);
|
|
||||||
--vscode-peekViewResult-selectionForeground: #6c6c6c;
|
|
||||||
--vscode-peekViewTitle-background: #ffffff;
|
|
||||||
--vscode-peekViewTitleDescription-foreground: rgba(108, 108, 108, 0.7);
|
|
||||||
--vscode-peekViewTitleLabel-foreground: #333333;
|
|
||||||
--vscode-pickerGroup-border: #cccedb;
|
|
||||||
--vscode-pickerGroup-foreground: #0066bf;
|
|
||||||
--vscode-progressBar-background: #0e70c0;
|
|
||||||
--vscode-quickInput-background: #f3f3f3;
|
|
||||||
--vscode-scrollbar-shadow: #dddddd;
|
|
||||||
--vscode-scrollbarSlider-activeBackground: rgba(0, 0, 0, 0.6);
|
|
||||||
--vscode-scrollbarSlider-background: rgba(100, 100, 100, 0.4);
|
|
||||||
--vscode-scrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
|
|
||||||
--vscode-settings-checkboxBackground: #ffffff;
|
|
||||||
--vscode-settings-checkboxBorder: #cecece;
|
|
||||||
--vscode-settings-dropdownBackground: #ffffff;
|
|
||||||
--vscode-settings-dropdownBorder: #cecece;
|
|
||||||
--vscode-settings-dropdownListBorder: #c8c8c8;
|
|
||||||
--vscode-settings-headerForeground: #444444;
|
|
||||||
--vscode-settings-modifiedItemIndicator: #66afe0;
|
|
||||||
--vscode-settings-numberInputBackground: #ffffff;
|
|
||||||
--vscode-settings-numberInputBorder: #cecece;
|
|
||||||
--vscode-settings-numberInputForeground: #616161;
|
|
||||||
--vscode-settings-textInputBackground: #ffffff;
|
|
||||||
--vscode-settings-textInputBorder: #cecece;
|
|
||||||
--vscode-settings-textInputForeground: #616161;
|
|
||||||
--vscode-sideBar-background: #f3f3f3;
|
|
||||||
--vscode-sideBar-dropBackground: rgba(0, 0, 0, 0.1);
|
|
||||||
--vscode-sideBarSectionHeader-background: rgba(128, 128, 128, 0.2);
|
|
||||||
--vscode-sideBarTitle-foreground: #6f6f6f;
|
|
||||||
--vscode-statusBar-background: #007acc;
|
|
||||||
--vscode-statusBar-debuggingBackground: #cc6633;
|
|
||||||
--vscode-statusBar-debuggingForeground: #ffffff;
|
|
||||||
--vscode-statusBar-foreground: #ffffff;
|
|
||||||
--vscode-statusBar-noFolderBackground: #68217a;
|
|
||||||
--vscode-statusBar-noFolderForeground: #ffffff;
|
|
||||||
--vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18);
|
|
||||||
--vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12);
|
|
||||||
--vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5);
|
|
||||||
--vscode-statusBarItem-prominentForeground: #ffffff;
|
|
||||||
--vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3);
|
|
||||||
--vscode-statusBarItem-remoteBackground: #16825d;
|
|
||||||
--vscode-statusBarItem-remoteForeground: #ffffff;
|
|
||||||
--vscode-tab-activeBackground: #ffffff;
|
|
||||||
--vscode-tab-activeForeground: #333333;
|
|
||||||
--vscode-tab-activeModifiedBorder: #33aaee;
|
|
||||||
--vscode-tab-border: #f3f3f3;
|
|
||||||
--vscode-tab-inactiveBackground: #ececec;
|
|
||||||
--vscode-tab-inactiveForeground: rgba(51, 51, 51, 0.7);
|
|
||||||
--vscode-tab-inactiveModifiedBorder: rgba(51, 170, 238, 0.5);
|
|
||||||
--vscode-tab-unfocusedActiveBackground: #ffffff;
|
|
||||||
--vscode-tab-unfocusedActiveForeground: rgba(51, 51, 51, 0.7);
|
|
||||||
--vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 170, 238, 0.7);
|
|
||||||
--vscode-tab-unfocusedInactiveForeground: rgba(51, 51, 51, 0.35);
|
|
||||||
--vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 170, 238, 0.25);
|
|
||||||
--vscode-terminal-ansiBlack: #000000;
|
|
||||||
--vscode-terminal-ansiBlue: #0451a5;
|
|
||||||
--vscode-terminal-ansiBrightBlack: #666666;
|
|
||||||
--vscode-terminal-ansiBrightBlue: #0451a5;
|
|
||||||
--vscode-terminal-ansiBrightCyan: #0598bc;
|
|
||||||
--vscode-terminal-ansiBrightGreen: #14ce14;
|
|
||||||
--vscode-terminal-ansiBrightMagenta: #bc05bc;
|
|
||||||
--vscode-terminal-ansiBrightRed: #cd3131;
|
|
||||||
--vscode-terminal-ansiBrightWhite: #a5a5a5;
|
|
||||||
--vscode-terminal-ansiBrightYellow: #b5ba00;
|
|
||||||
--vscode-terminal-ansiCyan: #0598bc;
|
|
||||||
--vscode-terminal-ansiGreen: #00bc00;
|
|
||||||
--vscode-terminal-ansiMagenta: #bc05bc;
|
|
||||||
--vscode-terminal-ansiRed: #cd3131;
|
|
||||||
--vscode-terminal-ansiWhite: #555555;
|
|
||||||
--vscode-terminal-ansiYellow: #949800;
|
|
||||||
--vscode-terminal-background: #ffffff;
|
|
||||||
--vscode-terminal-border: rgba(128, 128, 128, 0.35);
|
|
||||||
--vscode-terminal-foreground: #333333;
|
|
||||||
--vscode-terminal-selectionBackground: rgba(0, 0, 0, 0.25);
|
|
||||||
--vscode-textBlockQuote-background: rgba(127, 127, 127, 0.1);
|
|
||||||
--vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5);
|
|
||||||
--vscode-textCodeBlock-background: rgba(220, 220, 220, 0.4);
|
|
||||||
--vscode-textLink-activeForeground: #006ab1;
|
|
||||||
--vscode-textLink-foreground: #006ab1;
|
|
||||||
--vscode-textPreformat-foreground: #a31515;
|
|
||||||
--vscode-textSeparator-foreground: rgba(0, 0, 0, 0.18);
|
|
||||||
--vscode-titleBar-activeBackground: #dddddd;
|
|
||||||
--vscode-titleBar-activeForeground: #333333;
|
|
||||||
--vscode-titleBar-inactiveBackground: rgba(221, 221, 221, 0.6);
|
|
||||||
--vscode-titleBar-inactiveForeground: rgba(51, 51, 51, 0.6);
|
|
||||||
--vscode-tree-indentGuidesStroke: #a9a9a9;
|
|
||||||
--vscode-widget-shadow: #a8a8a8;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"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/**/*"]
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
require("ts-node").register();
|
|
||||||
module.exports = require("./webpack.config.ts");
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
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 { EnabledVisualizers } from "@hediet/visualization";
|
|
||||||
|
|
||||||
const r = (file: string) => path.resolve(__dirname, file);
|
|
||||||
|
|
||||||
const mode = process.argv.some(v => v === "--fast") ? "fast" : "default";
|
|
||||||
|
|
||||||
const enabledVisualizers: EnabledVisualizers =
|
|
||||||
mode === "fast"
|
|
||||||
? {
|
|
||||||
VisJsGraphVisualizer: true,
|
|
||||||
TreeVisualizer: false,
|
|
||||||
GraphvizGraphVisualizer: false,
|
|
||||||
SvgVisualizer: false,
|
|
||||||
GraphvizDotVisualizer: false,
|
|
||||||
TextVisualizer: false,
|
|
||||||
PlotlyVisualizer: false,
|
|
||||||
GridVisualizer: false,
|
|
||||||
MonacoTextVisualizer: false,
|
|
||||||
AstVisualizer: false,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
VisJsGraphVisualizer: true,
|
|
||||||
TreeVisualizer: true,
|
|
||||||
GraphvizGraphVisualizer: true,
|
|
||||||
SvgVisualizer: true,
|
|
||||||
GraphvizDotVisualizer: true,
|
|
||||||
TextVisualizer: true,
|
|
||||||
PlotlyVisualizer: true,
|
|
||||||
GridVisualizer: true,
|
|
||||||
MonacoTextVisualizer: true,
|
|
||||||
AstVisualizer: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const stringifiedEnabledVisualizers: Record<string, string> = {};
|
|
||||||
for (const [key, val] of Object.entries(enabledVisualizers)) {
|
|
||||||
stringifiedEnabledVisualizers[key] = JSON.stringify(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: [r("src/index.tsx")],
|
|
||||||
output: {
|
|
||||||
path: r("dist"),
|
|
||||||
filename: "[name].js",
|
|
||||||
chunkFilename: "[name]-[hash].js",
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"],
|
|
||||||
},
|
|
||||||
devtool: "source-map",
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.css$/, loader: "style-loader!css-loader" },
|
|
||||||
{ test: /\.scss$/, loader: "style-loader!css-loader!sass-loader" },
|
|
||||||
{
|
|
||||||
test: /\.(jpe?g|png|gif|eot|ttf|svg|woff|woff2|md)$/i,
|
|
||||||
loader: "file-loader",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.tsx?$/,
|
|
||||||
loader: "ts-loader",
|
|
||||||
options: { transpileOnly: true },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
fs: "empty",
|
|
||||||
},
|
|
||||||
plugins: (() => {
|
|
||||||
const plugins: any[] = [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
title: "Debug Visualizer",
|
|
||||||
}),
|
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
|
||||||
new CleanWebpackPlugin(),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
ENABLED_VISUALIZERS: stringifiedEnabledVisualizers,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
plugins.push(
|
|
||||||
new MonacoWebpackPlugin({
|
|
||||||
// Add more languages here once webworker issues are solved.
|
|
||||||
languages: ["typescript"],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return plugins;
|
|
||||||
})(),
|
|
||||||
} as webpack.Configuration;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@hediet/visualization",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "tsc --watch",
|
|
||||||
"build": "tsc"
|
|
||||||
},
|
|
||||||
"main": "./dist/index.js",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"dependencies": {
|
|
||||||
"@hediet/std": "^0.6.0",
|
|
||||||
"classnames": "^2.2.6",
|
|
||||||
"mobx": "^5.15.4",
|
|
||||||
"mobx-react": "^6.1.8",
|
|
||||||
"monaco-editor": "^0.20.0",
|
|
||||||
"react": "^16.12.0",
|
|
||||||
"react-dom": "^16.12.0",
|
|
||||||
"react-measure": "^2.3.0",
|
|
||||||
"react-svg-pan-zoom": "^3.8.0",
|
|
||||||
"viz.js": "^2.1.2",
|
|
||||||
"line-column": "^1.0.2",
|
|
||||||
"vis-network": "^7.3.6",
|
|
||||||
"vis-data": "^6.4.1",
|
|
||||||
"react-plotly.js": "^2.4.0",
|
|
||||||
"plotly.js": "^1.52.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/plotly.js": "1.44.28",
|
|
||||||
"@types/react-plotly.js": "^2.2.4",
|
|
||||||
"@types/html-webpack-plugin": "^3.2.2",
|
|
||||||
"@types/react-measure": "^2.0.6",
|
|
||||||
"@types/react-svg-pan-zoom": "^3.3.0",
|
|
||||||
"@types/webpack": "^4.41.6",
|
|
||||||
"@types/classnames": "^2.2.9",
|
|
||||||
"@types/react": "^16.9.22",
|
|
||||||
"@types/react-dom": "^16.9.5",
|
|
||||||
"@types/line-column": "^1.0.0",
|
|
||||||
"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",
|
|
||||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
|
||||||
"node-sass": "^4.13.1",
|
|
||||||
"raw-loader": "^4.0.0",
|
|
||||||
"sass": "^1.25.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
export interface EnabledVisualizers {
|
|
||||||
VisJsGraphVisualizer: boolean;
|
|
||||||
TreeVisualizer: boolean;
|
|
||||||
GraphvizGraphVisualizer: boolean;
|
|
||||||
SvgVisualizer: boolean;
|
|
||||||
GraphvizDotVisualizer: boolean;
|
|
||||||
TextVisualizer: boolean;
|
|
||||||
PlotlyVisualizer: boolean;
|
|
||||||
GridVisualizer: boolean;
|
|
||||||
MonacoTextVisualizer: boolean;
|
|
||||||
AstVisualizer: boolean;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { observable, runInAction } from "mobx";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
|
|
||||||
export function makeLazyLoadable<TProps>(
|
|
||||||
selector: () => Promise<React.ComponentClass<TProps>>
|
|
||||||
): React.ComponentClass<TProps> {
|
|
||||||
@observer
|
|
||||||
class MyComponent extends React.Component<TProps> {
|
|
||||||
@observable
|
|
||||||
component: React.ComponentClass<TProps> | undefined;
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
const result = await selector();
|
|
||||||
runInAction("Set Lazily Loaded Component", () => {
|
|
||||||
this.component = result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.component) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
const C = this.component;
|
|
||||||
return <C {...this.props} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MyComponent;
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { ExtractedData } from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
|
|
||||||
export interface VisualizationId extends String {
|
|
||||||
__brand: "VisualizationId";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function asVisualizationId(id: string): VisualizationId {
|
|
||||||
return (id as unknown) as VisualizationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Visualization {
|
|
||||||
readonly name: string;
|
|
||||||
readonly id: VisualizationId;
|
|
||||||
readonly priority: number;
|
|
||||||
render(): React.ReactElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VisualizationCollector {
|
|
||||||
addVisualization(visualization: Visualization): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class Visualizer {
|
|
||||||
abstract visualize(
|
|
||||||
data: ExtractedData,
|
|
||||||
collector: VisualizationCollector
|
|
||||||
): void;
|
|
||||||
|
|
||||||
getVisualizationsArray(data: ExtractedData): Visualization[] {
|
|
||||||
const result = new Array<Visualization>();
|
|
||||||
this.visualize(data, {
|
|
||||||
addVisualization(visualization) {
|
|
||||||
result.push(visualization);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBestVisualization(
|
|
||||||
data: ExtractedData,
|
|
||||||
preferredVisualization: VisualizationId | undefined
|
|
||||||
): {
|
|
||||||
visualization: Visualization | undefined;
|
|
||||||
allVisualizations: Visualization[];
|
|
||||||
} {
|
|
||||||
const allVisualizations = this.getVisualizationsArray(data);
|
|
||||||
allVisualizations.sort((a, b) => b.priority - a.priority);
|
|
||||||
|
|
||||||
let visualization: Visualization | undefined = allVisualizations[0];
|
|
||||||
if (preferredVisualization) {
|
|
||||||
const preferred = allVisualizations.find(
|
|
||||||
vis => vis.id === preferredVisualization
|
|
||||||
);
|
|
||||||
if (preferred) {
|
|
||||||
visualization = preferred;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { visualization, allVisualizations };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ComposedVisualizer extends Visualizer {
|
|
||||||
constructor(private readonly composedProviders: ReadonlyArray<Visualizer>) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
for (const c of this.composedProviders) {
|
|
||||||
c.visualize(data, collector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export class VisualizerView extends React.Component {
|
|
||||||
render(): React.ReactNode {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from "./visualizers";
|
|
||||||
export * from "./Visualizer";
|
|
||||||
export * from "./VisualizerView";
|
|
||||||
export * from "./EnabledVisualizers";
|
|
||||||
22
visualization/src/types.d.ts
vendored
22
visualization/src/types.d.ts
vendored
@@ -1,22 +0,0 @@
|
|||||||
declare module "viz.js" {
|
|
||||||
const x: any;
|
|
||||||
export default x;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "viz.js/full.render.js" {
|
|
||||||
export const render: any;
|
|
||||||
export const Module: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "line-column" {
|
|
||||||
export = function(
|
|
||||||
text: string
|
|
||||||
): {
|
|
||||||
fromIndex(
|
|
||||||
idx: number
|
|
||||||
): {
|
|
||||||
line: number;
|
|
||||||
col: number;
|
|
||||||
};
|
|
||||||
} {};
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
function sqr(a: number) {
|
|
||||||
return a * a;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PointLike =
|
|
||||||
| Point
|
|
||||||
| { x?: number; y: number }
|
|
||||||
| { x: number; y?: number };
|
|
||||||
|
|
||||||
export function point(data: PointLike) {
|
|
||||||
if (data instanceof Point) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return new Point(data.x || 0, data.y || 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Point {
|
|
||||||
public static readonly Zero = new Point(0, 0);
|
|
||||||
|
|
||||||
constructor(public readonly x: number, public readonly y: number) {}
|
|
||||||
|
|
||||||
public mapXY(fn: (arg: number) => number): Point {
|
|
||||||
return new Point(fn(this.x), fn(this.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
public distance(other: PointLike = Point.Zero): number {
|
|
||||||
const d = this.sub(other);
|
|
||||||
return Math.sqrt(sqr(d.x) + sqr(d.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sub(other: PointLike): Point {
|
|
||||||
const o = point(other);
|
|
||||||
return new Point(this.x - o.x, this.y - o.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(other: PointLike): Point {
|
|
||||||
const o = point(other);
|
|
||||||
return new Point(this.x + o.x, this.y + o.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public mul(scalar: number): Point {
|
|
||||||
return new Point(this.x * scalar, this.y * scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public div(scalar: number): Point {
|
|
||||||
return new Point(this.x / scalar, this.y / scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public equals(other: PointLike) {
|
|
||||||
const o = point(other);
|
|
||||||
return this.x === o.x && this.y === o.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public angle(): number {
|
|
||||||
const angle = Math.atan2(this.x, this.y);
|
|
||||||
return angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getPointCloserTo(dest: PointLike, dist: number): Point {
|
|
||||||
if (this.equals(dest)) return this;
|
|
||||||
|
|
||||||
const angle = point(dest)
|
|
||||||
.sub(this)
|
|
||||||
.angle();
|
|
||||||
|
|
||||||
const result = new Point(
|
|
||||||
this.x + Math.sin(angle) * dist,
|
|
||||||
this.y + Math.cos(angle) * dist
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJson(): { x: number; y: number } {
|
|
||||||
return { x: this.x, y: this.y };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function turn(p1: Point, p2: Point, p3: Point): number {
|
|
||||||
const A = (p3.y - p1.y) * (p2.x - p1.x);
|
|
||||||
const B = (p2.y - p1.y) * (p3.x - p1.x);
|
|
||||||
return A > B + Number.MIN_VALUE ? 1 : A + Number.MIN_VALUE < B ? -1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isIntersect(
|
|
||||||
aStart: Point,
|
|
||||||
aEnd: Point,
|
|
||||||
bStart: Point,
|
|
||||||
bEnd: Point
|
|
||||||
): boolean {
|
|
||||||
return (
|
|
||||||
turn(aStart, bStart, bEnd) != turn(aEnd, bStart, bEnd) &&
|
|
||||||
turn(aStart, aEnd, bStart) != turn(aStart, aEnd, bEnd)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Liang-Barsky function by Daniel White
|
|
||||||
*
|
|
||||||
* @link http://www.skytopia.com/project/articles/compsci/clipping.html
|
|
||||||
*
|
|
||||||
* @param {number} x0
|
|
||||||
* @param {number} y0
|
|
||||||
* @param {number} x1
|
|
||||||
* @param {number} y1
|
|
||||||
* @param {array<number>} bbox
|
|
||||||
* @return {array<array<number>>|null}
|
|
||||||
*/
|
|
||||||
function liangBarsky(
|
|
||||||
x0: number,
|
|
||||||
y0: number,
|
|
||||||
x1: number,
|
|
||||||
y1: number,
|
|
||||||
bbox: [number, number, number, number]
|
|
||||||
): [[number, number], [number, number]] | null {
|
|
||||||
let [xmin, xmax, ymin, ymax] = bbox;
|
|
||||||
let t0 = 0,
|
|
||||||
t1 = 1;
|
|
||||||
let dx = x1 - x0,
|
|
||||||
dy = y1 - y0;
|
|
||||||
let p = 0,
|
|
||||||
q = 0,
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
for (let edge: 0 | 1 | 2 | 3 = 0; edge < 4; edge++) {
|
|
||||||
// Traverse through left, right, bottom, top edges.
|
|
||||||
if (edge === 0) {
|
|
||||||
p = -dx;
|
|
||||||
q = -(xmin - x0);
|
|
||||||
} else if (edge === 1) {
|
|
||||||
p = dx;
|
|
||||||
q = xmax - x0;
|
|
||||||
} else if (edge === 2) {
|
|
||||||
p = -dy;
|
|
||||||
q = -(ymin - y0);
|
|
||||||
} else if (edge === 3) {
|
|
||||||
p = dy;
|
|
||||||
q = ymax - y0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = q / p;
|
|
||||||
|
|
||||||
if (p === 0 && q < 0) return null; // Don't draw line at all. (parallel line outside)
|
|
||||||
|
|
||||||
if (p < 0) {
|
|
||||||
if (r > t1) return null;
|
|
||||||
// Don't draw line at all.
|
|
||||||
else if (r > t0) t0 = r; // Line is clipped!
|
|
||||||
} else if (p > 0) {
|
|
||||||
if (r < t0) return null;
|
|
||||||
// Don't draw line at all.
|
|
||||||
else if (r < t1) t1 = r; // Line is clipped!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[x0 + t0 * dx, y0 + t0 * dy], [x0 + t1 * dx, y0 + t1 * dy]];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function intersectRectWithLine(
|
|
||||||
start: Point,
|
|
||||||
end: Point,
|
|
||||||
rect: Rectangle
|
|
||||||
): { first: Point; second: Point } | undefined {
|
|
||||||
const r = liangBarsky(start.x, start.y, end.x, end.y, [
|
|
||||||
rect.topLeft.x,
|
|
||||||
rect.bottomRight.x,
|
|
||||||
rect.topLeft.y,
|
|
||||||
rect.bottomRight.y,
|
|
||||||
]);
|
|
||||||
if (!r) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
first: new Point(r[0][0], r[0][1]),
|
|
||||||
second: new Point(r[1][0], r[1][1]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// first zoom then translate
|
|
||||||
export function scale(
|
|
||||||
clientOffset: Point,
|
|
||||||
clientSize: Point,
|
|
||||||
viewSize: Point
|
|
||||||
): { clientZoom: number; clientOffset: Point } {
|
|
||||||
const clientRatio = clientSize.x / clientSize.y;
|
|
||||||
const viewRatio = viewSize.x / viewSize.y;
|
|
||||||
|
|
||||||
let zoom = 1;
|
|
||||||
|
|
||||||
if (clientRatio < viewRatio) zoom = viewSize.y / clientSize.y;
|
|
||||||
else zoom = viewSize.x / clientSize.x;
|
|
||||||
|
|
||||||
const clientMid = clientOffset.mul(zoom).add(clientSize.mul(zoom / 2));
|
|
||||||
const viewMid = viewSize.div(2);
|
|
||||||
|
|
||||||
const clientOffset2 = viewMid.sub(clientMid);
|
|
||||||
|
|
||||||
return { clientOffset: clientOffset2, clientZoom: zoom };
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Rectangle {
|
|
||||||
public static ofSize(position: PointLike, size: PointLike): Rectangle {
|
|
||||||
const pos = point(position);
|
|
||||||
return new Rectangle(pos, pos.add(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static spanning(first: Point, ...rest: Point[]): Rectangle {
|
|
||||||
let min = first.toJson();
|
|
||||||
let max = first.toJson();
|
|
||||||
for (const p of rest) {
|
|
||||||
min.x = Math.min(min.x, p.x);
|
|
||||||
min.y = Math.min(min.y, p.y);
|
|
||||||
max.x = Math.max(max.x, p.x);
|
|
||||||
max.y = Math.max(max.y, p.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rectangle(point(min), point(max));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public readonly topLeft: Point,
|
|
||||||
public readonly bottomRight: Point
|
|
||||||
) {}
|
|
||||||
|
|
||||||
get center(): Point {
|
|
||||||
return this.bottomRight.add(this.topLeft).div(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
get size(): Point {
|
|
||||||
return this.bottomRight.sub(this.topLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
get topRight(): Point {
|
|
||||||
return new Point(this.bottomRight.x, this.topLeft.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
get bottomLeft(): Point {
|
|
||||||
return new Point(this.topLeft.x, this.bottomRight.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public intersects(selectionRect: Rectangle): boolean {
|
|
||||||
return !(
|
|
||||||
selectionRect.topLeft.x > this.bottomRight.x ||
|
|
||||||
selectionRect.bottomRight.x < this.topLeft.x ||
|
|
||||||
selectionRect.topLeft.y > this.bottomRight.y ||
|
|
||||||
selectionRect.bottomRight.y < this.topLeft.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
equals(other: Rectangle) {
|
|
||||||
return (
|
|
||||||
this.topLeft.equals(other.topLeft) &&
|
|
||||||
this.bottomRight.equals(other.bottomRight)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import { Point, Rectangle } from "./Point";
|
|
||||||
import * as React from "react";
|
|
||||||
import { Properties } from "csstype";
|
|
||||||
|
|
||||||
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
|
||||||
|
|
||||||
export function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
|
|
||||||
const newObj: Omit<T, K> = {} as any;
|
|
||||||
for (const [key, val] of Object.entries(obj)) {
|
|
||||||
if (!keys.includes(key as K)) {
|
|
||||||
(newObj as any)[key] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SvgAttributes<T> extends React.DOMAttributes<T> {
|
|
||||||
stroke?: string;
|
|
||||||
className?: string;
|
|
||||||
style?: Properties;
|
|
||||||
pointerEvents?: "none" | "auto" | "initial";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgText(
|
|
||||||
props: {
|
|
||||||
position: Point;
|
|
||||||
children: any;
|
|
||||||
childRef?: React.Ref<SVGTextElement>;
|
|
||||||
textAnchor?: "middle" | "end" | "start";
|
|
||||||
dominantBaseline?: "central" | "middle";
|
|
||||||
} & SvgAttributes<SVGTextElement>
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<text
|
|
||||||
x={props.position.x}
|
|
||||||
y={props.position.y}
|
|
||||||
ref={props.childRef}
|
|
||||||
{...omit(props, ["position", "childRef"])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgCircle(
|
|
||||||
props: {
|
|
||||||
center: Point;
|
|
||||||
radius: number;
|
|
||||||
childRef?: React.Ref<SVGCircleElement>;
|
|
||||||
} & SvgAttributes<SVGCircleElement>
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<circle
|
|
||||||
cx={props.center.x}
|
|
||||||
cy={props.center.y}
|
|
||||||
r={props.radius}
|
|
||||||
ref={props.childRef}
|
|
||||||
{...omit(props, ["center", "radius", "childRef"])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgLine(
|
|
||||||
props: { start: Point; end: Point } & SvgAttributes<SVGLineElement>
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<line
|
|
||||||
x1={props.start.x}
|
|
||||||
y1={props.start.y}
|
|
||||||
x2={props.end.x}
|
|
||||||
y2={props.end.y}
|
|
||||||
{...omit(props, ["end", "start"])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgRect(
|
|
||||||
props: { rectangle: Rectangle; fill?: string } & SvgAttributes<
|
|
||||||
SVGRectElement
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
const r = props.rectangle;
|
|
||||||
return (
|
|
||||||
<rect
|
|
||||||
x={r.topLeft.x}
|
|
||||||
y={r.topLeft.y}
|
|
||||||
width={r.size.x}
|
|
||||||
height={r.size.y}
|
|
||||||
{...omit(props, ["rectangle"])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
CommonDataTypes,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import { createTreeViewModelFromTreeNodeData } from "./TreeVisualizer/TreeVisualizer";
|
|
||||||
import { TreeWithPathView, TreeViewModel } from "./TreeVisualizer/Views";
|
|
||||||
import * as React from "react";
|
|
||||||
import { observer, disposeOnUnmount } from "mobx-react";
|
|
||||||
import { observable, autorun, trace } from "mobx";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import { getLanguageId } from "./text-visualizers/MonacoTextVisualizer";
|
|
||||||
import LineColumn from "line-column";
|
|
||||||
|
|
||||||
export class AstVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { ast: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("ast"),
|
|
||||||
name: "AST",
|
|
||||||
priority: 110,
|
|
||||||
render() {
|
|
||||||
const m = createTreeViewModelFromTreeNodeData(data.root);
|
|
||||||
const l = LineColumn(data.text);
|
|
||||||
function translatePosition(pos: number): monaco.IPosition {
|
|
||||||
const r = l.fromIndex(pos);
|
|
||||||
return {
|
|
||||||
column: r.col,
|
|
||||||
lineNumber: r.line,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const nodeInfoToRange = (info: NodeInfo): monaco.IRange => {
|
|
||||||
const start = translatePosition(info.position);
|
|
||||||
const end = translatePosition(
|
|
||||||
info.position + info.length
|
|
||||||
);
|
|
||||||
const range = monaco.Range.fromPositions(start, end);
|
|
||||||
return range;
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<AstTree
|
|
||||||
model={m}
|
|
||||||
data={data}
|
|
||||||
nodeInfoToRange={nodeInfoToRange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NodeInfo {
|
|
||||||
position: number;
|
|
||||||
length: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class AstTree extends React.Component<{
|
|
||||||
model: TreeViewModel<NodeInfo>;
|
|
||||||
data: CommonDataTypes.Ast;
|
|
||||||
nodeInfoToRange: (info: NodeInfo) => monaco.IRange;
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const model = this.props.model;
|
|
||||||
const data = this.props.data;
|
|
||||||
let languageId = "text";
|
|
||||||
if (data.fileName) {
|
|
||||||
languageId = getLanguageId(data.fileName);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="component-AstTree">
|
|
||||||
<div className="part-tree2">
|
|
||||||
<TreeWithPathView model={model} />
|
|
||||||
</div>
|
|
||||||
<div className="part-editor">
|
|
||||||
<MonacoEditor
|
|
||||||
nodeInfoToRange={this.props.nodeInfoToRange}
|
|
||||||
model={model}
|
|
||||||
languageId={languageId}
|
|
||||||
text={data.text}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class MonacoEditor extends React.Component<{
|
|
||||||
text: string;
|
|
||||||
languageId: string;
|
|
||||||
model: TreeViewModel<NodeInfo>;
|
|
||||||
nodeInfoToRange: (info: NodeInfo) => monaco.IRange;
|
|
||||||
}> {
|
|
||||||
@observable private editor: monaco.editor.IStandaloneCodeEditor | undefined;
|
|
||||||
|
|
||||||
private modelNotObservable:
|
|
||||||
| monaco.editor.ITextModel
|
|
||||||
| undefined = undefined;
|
|
||||||
|
|
||||||
@observable private model: monaco.editor.ITextModel | undefined = undefined;
|
|
||||||
|
|
||||||
private markedDecorations: string[] = [];
|
|
||||||
private selectedDecorations: string[] = [];
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
private _updateMarkedDecorations = autorun(
|
|
||||||
() => {
|
|
||||||
if (this.editor && this.model) {
|
|
||||||
const editorModel = this.model;
|
|
||||||
const ranges = this.props.model.marked.map(s =>
|
|
||||||
this.props.nodeInfoToRange(s.data)
|
|
||||||
);
|
|
||||||
this.markedDecorations = this.editor.deltaDecorations(
|
|
||||||
this.markedDecorations,
|
|
||||||
ranges.map(range => ({
|
|
||||||
range,
|
|
||||||
options: { className: "marked" },
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
if (ranges.length > 0) {
|
|
||||||
this.editor.revealRange(
|
|
||||||
ranges[0],
|
|
||||||
monaco.editor.ScrollType.Smooth
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ name: "updateMarkedDecorations" }
|
|
||||||
);
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
private _updateSelectedDecoration = autorun(
|
|
||||||
() => {
|
|
||||||
if (this.editor && this.model) {
|
|
||||||
const selected = this.props.model.selected;
|
|
||||||
if (selected) {
|
|
||||||
const range = this.props.nodeInfoToRange(selected.data);
|
|
||||||
this.selectedDecorations = this.editor.deltaDecorations(
|
|
||||||
this.selectedDecorations,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
range,
|
|
||||||
options: { className: "selected" },
|
|
||||||
},
|
|
||||||
]
|
|
||||||
);
|
|
||||||
this.editor.revealRange(
|
|
||||||
range,
|
|
||||||
monaco.editor.ScrollType.Smooth
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.selectedDecorations = this.editor.deltaDecorations(
|
|
||||||
this.selectedDecorations,
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ name: "updateDecorations" }
|
|
||||||
);
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
private _updateText = autorun(() => {
|
|
||||||
if (this.editor) {
|
|
||||||
const model = monaco.editor.createModel(
|
|
||||||
this.props.text,
|
|
||||||
this.props.languageId,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
this.editor.setModel(model);
|
|
||||||
|
|
||||||
if (this.modelNotObservable) {
|
|
||||||
this.modelNotObservable.dispose();
|
|
||||||
}
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private readonly setEditorDiv = (editorDiv: HTMLDivElement) => {
|
|
||||||
if (!editorDiv) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor = monaco.editor.create(editorDiv, {
|
|
||||||
model: null,
|
|
||||||
automaticLayout: true,
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
minimap: { enabled: false },
|
|
||||||
fixedOverflowWidgets: true,
|
|
||||||
readOnly: true,
|
|
||||||
lineDecorationsWidth: 0,
|
|
||||||
lineNumbersMinChars: 4,
|
|
||||||
glyphMargin: false,
|
|
||||||
smoothScrolling: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="component-monaco-editor">
|
|
||||||
<div className="part-editor" ref={this.setEditorDiv} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,399 +0,0 @@
|
|||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
CommonDataTypes,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import * as React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { computed, action, observable } from "mobx";
|
|
||||||
import { Icon } from "@blueprintjs/core";
|
|
||||||
|
|
||||||
export class GridVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { array: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("grid"),
|
|
||||||
name: "Grid",
|
|
||||||
priority: 90,
|
|
||||||
render: () => <DecoratedGridComponent data={data} />,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DecoratedGridComponent extends React.Component<{
|
|
||||||
data: CommonDataTypes.Grid;
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const d = this.props.data;
|
|
||||||
|
|
||||||
const map = new Set<string>();
|
|
||||||
function getUniqueId(tag: string): string {
|
|
||||||
let n = 0;
|
|
||||||
while (true) {
|
|
||||||
const id = tag + (n === 0 ? "" : `-${n}`);
|
|
||||||
if (!map.has(id)) {
|
|
||||||
map.add(id);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows: GridComponent["props"]["rows"] = [];
|
|
||||||
rows.push();
|
|
||||||
let rowIdx = 0;
|
|
||||||
let columnCount = 0;
|
|
||||||
for (const row of d.rows) {
|
|
||||||
columnCount = Math.max(columnCount, row.columns.length);
|
|
||||||
rows.push({
|
|
||||||
columns: row.columns.map((c, colIdx) => ({
|
|
||||||
content: (
|
|
||||||
<Cell>
|
|
||||||
{c.content !== undefined ? c.content : c.tag || ""}
|
|
||||||
</Cell>
|
|
||||||
),
|
|
||||||
id:
|
|
||||||
c.tag !== undefined
|
|
||||||
? getUniqueId(c.tag)
|
|
||||||
: `${rowIdx}-${colIdx}`,
|
|
||||||
color: c.color,
|
|
||||||
kind: "data",
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
rowIdx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.unshift({
|
|
||||||
columns: [...new Array(columnCount)].map((v, idx) => ({
|
|
||||||
id: `column-header-${idx}`,
|
|
||||||
content: <Cell>{idx}</Cell>,
|
|
||||||
kind: "header",
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (d.markers) {
|
|
||||||
for (const m of d.markers) {
|
|
||||||
rows.push({
|
|
||||||
columns: [],
|
|
||||||
});
|
|
||||||
const r = rows[rows.length - 1];
|
|
||||||
for (let i = 0; i < m.column; i++) {
|
|
||||||
r.columns.push({
|
|
||||||
content: "",
|
|
||||||
kind: "empty",
|
|
||||||
id: `marker-spacer-${i}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
r.columns.push({
|
|
||||||
id: m.id,
|
|
||||||
kind: "empty",
|
|
||||||
content: (
|
|
||||||
<Cell>
|
|
||||||
<Icon
|
|
||||||
icon="arrow-up"
|
|
||||||
color="var(--vscode-editor-foreground)"
|
|
||||||
/>
|
|
||||||
{m.label || m.id}
|
|
||||||
</Cell>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "100%",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: 10,
|
|
||||||
display: "flex",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GridComponent rows={rows} markers={[]} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Cell(props: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: 10,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CellData {
|
|
||||||
content: React.ReactNode;
|
|
||||||
id: string;
|
|
||||||
color?: string;
|
|
||||||
kind: "data" | "header" | "empty";
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
class GridComponent extends React.Component<{
|
|
||||||
rows: {
|
|
||||||
columns: CellData[];
|
|
||||||
}[];
|
|
||||||
markers: {
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
row: number;
|
|
||||||
column: number;
|
|
||||||
rows?: number;
|
|
||||||
columns?: number;
|
|
||||||
|
|
||||||
label?: string;
|
|
||||||
color?: string;
|
|
||||||
}[];
|
|
||||||
}> {
|
|
||||||
private readonly cells = new Map<string, CellInfo>();
|
|
||||||
|
|
||||||
@computed get cellsGrid(): { maxWidth: number; grid: CellInfo[][] } {
|
|
||||||
const unusedIds = new Set(this.cells.keys());
|
|
||||||
|
|
||||||
const rows = this.props.rows;
|
|
||||||
|
|
||||||
const grid = new Array<Array<CellInfo>>();
|
|
||||||
|
|
||||||
let maxWidth = 0;
|
|
||||||
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
||||||
const row = rows[rowIdx];
|
|
||||||
maxWidth = Math.max(maxWidth, row.columns.length);
|
|
||||||
const cellRow = new Array<CellInfo>();
|
|
||||||
grid.push(cellRow);
|
|
||||||
|
|
||||||
for (let colIdx = 0; colIdx < row.columns.length; colIdx++) {
|
|
||||||
const col = row.columns[colIdx];
|
|
||||||
const key = col.id;
|
|
||||||
unusedIds.delete(key);
|
|
||||||
let c = this.cells.get(key);
|
|
||||||
if (!c) {
|
|
||||||
// New cell
|
|
||||||
c = new CellInfo(col);
|
|
||||||
this.cells.set(key, c);
|
|
||||||
} else {
|
|
||||||
c.updateCellData(col);
|
|
||||||
}
|
|
||||||
cellRow.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const id in unusedIds.values()) {
|
|
||||||
this.cells.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { maxWidth, grid };
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed
|
|
||||||
private get layout(): {
|
|
||||||
cells: {
|
|
||||||
cell: CellInfo;
|
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}[];
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
} {
|
|
||||||
const { maxWidth, grid } = this.cellsGrid;
|
|
||||||
|
|
||||||
const maxWidths = new Array<number>();
|
|
||||||
for (let colIdx = 0; colIdx < maxWidth; colIdx++) {
|
|
||||||
let maxWidth = 30;
|
|
||||||
for (const row of grid) {
|
|
||||||
const col = row[colIdx];
|
|
||||||
if (col) {
|
|
||||||
maxWidth = Math.max(maxWidth, col.contentWidth || 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maxWidths.push(maxWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = new Array<{
|
|
||||||
cell: CellInfo;
|
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let width = 0;
|
|
||||||
let height = 0;
|
|
||||||
let top = 0;
|
|
||||||
let left = 0;
|
|
||||||
|
|
||||||
for (const row of grid) {
|
|
||||||
let maxHeight = 0;
|
|
||||||
for (const col of row) {
|
|
||||||
maxHeight = Math.max(maxHeight, col.contentHeight || 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let colIdx = 0; colIdx < row.length; colIdx++) {
|
|
||||||
const maxWidth = maxWidths[colIdx];
|
|
||||||
const col = row[colIdx];
|
|
||||||
result.push({
|
|
||||||
cell: col,
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
height: maxHeight,
|
|
||||||
width: maxWidth,
|
|
||||||
});
|
|
||||||
height = Math.max(height, top + maxHeight);
|
|
||||||
width = Math.max(width, left + maxWidth);
|
|
||||||
col.lastTop = col.top;
|
|
||||||
col.lastLeft = col.left;
|
|
||||||
col.top = top;
|
|
||||||
col.left = left;
|
|
||||||
|
|
||||||
col.lastContentArea = col.contentArea;
|
|
||||||
col.contentArea = maxHeight * maxWidth;
|
|
||||||
left += maxWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
left = 0;
|
|
||||||
top += maxHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.sort((a, b) => a.cell.id.localeCompare(b.cell.id));
|
|
||||||
return { cells: result, height, width };
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const l = this.layout;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="component-Grid"
|
|
||||||
style={{
|
|
||||||
position: "relative",
|
|
||||||
height: l.height,
|
|
||||||
width: l.width,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{l.cells.map(i => (
|
|
||||||
<div
|
|
||||||
key={i.cell.id}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: i.top,
|
|
||||||
left: i.left,
|
|
||||||
height: i.height,
|
|
||||||
width: i.width,
|
|
||||||
zIndex: i.cell.distance,
|
|
||||||
transition:
|
|
||||||
i.cell.lastContentArea !== 0 ? "all 1s" : "",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`part-${i.cell.kind}`}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
empty: {},
|
|
||||||
data: {
|
|
||||||
display: "flex",
|
|
||||||
overflow: "hidden",
|
|
||||||
justifyContent: "center",
|
|
||||||
margin: 2,
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
overflow: "hidden",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
margin: 2,
|
|
||||||
},
|
|
||||||
}[i.cell.kind]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={i.cell.handleRef}
|
|
||||||
style={{
|
|
||||||
width: "fit-content",
|
|
||||||
height: "fit-content",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{i.cell.content}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.updateContentSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.updateContentSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updateContentSize() {
|
|
||||||
for (const c of this.cells.values()) {
|
|
||||||
if (c.ref) {
|
|
||||||
const r = c.ref.getBoundingClientRect();
|
|
||||||
c.contentHeight = r.height + 6;
|
|
||||||
c.contentWidth = r.width + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CellInfo {
|
|
||||||
@observable public contentWidth: number | undefined = undefined;
|
|
||||||
@observable public contentHeight: number | undefined = undefined;
|
|
||||||
|
|
||||||
public contentArea = 0;
|
|
||||||
public lastContentArea = 0;
|
|
||||||
|
|
||||||
public lastTop = 0;
|
|
||||||
public top = 0;
|
|
||||||
public lastLeft = 0;
|
|
||||||
public left = 0;
|
|
||||||
|
|
||||||
public get distance(): number {
|
|
||||||
return Math.floor(
|
|
||||||
Math.pow(this.top - this.lastTop, 2) +
|
|
||||||
Math.pow(this.left - this.lastLeft, 2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
content!: React.ReactNode;
|
|
||||||
|
|
||||||
ref: HTMLDivElement | null = null;
|
|
||||||
|
|
||||||
public readonly id: string;
|
|
||||||
public readonly kind: CellData["kind"];
|
|
||||||
|
|
||||||
constructor(cellData: CellData) {
|
|
||||||
this.id = cellData.id;
|
|
||||||
this.kind = cellData.kind;
|
|
||||||
this.updateCellData(cellData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public updateCellData(cellData: CellData) {
|
|
||||||
this.content = cellData.content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly handleRef = (ref: HTMLDivElement | null) => {
|
|
||||||
this.ref = ref;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import Measure, { ContentRect } from "react-measure";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { observable, action } from "mobx";
|
|
||||||
import { Tool, ReactSVGPanZoom, Value } from "react-svg-pan-zoom";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
|
|
||||||
export class SvgVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { svg: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("svg"),
|
|
||||||
name: "Svg",
|
|
||||||
priority: 100,
|
|
||||||
render() {
|
|
||||||
return <SvgViewer svgContent={data.text} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function widthOrDefault(r: ContentRect): number {
|
|
||||||
if (r.bounds && r.bounds.width) {
|
|
||||||
return r.bounds.width;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function heightOrDefault(r: ContentRect): number {
|
|
||||||
console.log(r);
|
|
||||||
if (r.bounds && r.bounds.height) {
|
|
||||||
return r.bounds.height;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class SvgViewer extends React.Component<{
|
|
||||||
svgContent: string;
|
|
||||||
svgRef?: (element: SVGSVGElement | null) => void;
|
|
||||||
}> {
|
|
||||||
@observable tool: Tool = "pan";
|
|
||||||
|
|
||||||
@action.bound
|
|
||||||
private setTool(tool: Tool): void {
|
|
||||||
this.tool = tool;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setRef(element: ReactSVGPanZoom | null) {
|
|
||||||
if (this.props.svgRef) {
|
|
||||||
if (!element) {
|
|
||||||
this.props.svgRef(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const svg = (element as any).ViewerDOM as SVGSVGElement;
|
|
||||||
this.props.svgRef(svg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observable value: Value | {} = {};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let { svgContent } = this.props;
|
|
||||||
let width: number = 0;
|
|
||||||
let height: number = 0;
|
|
||||||
svgContent = svgContent.replace(
|
|
||||||
/viewBox="[0-9\.]+ [0-9\.]+ ([0-9\.]+) ([0-9\.]+)"/,
|
|
||||||
(r, w, h) => {
|
|
||||||
width = parseFloat(w);
|
|
||||||
height = parseFloat(h);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const tool = this.tool;
|
|
||||||
const val = this.value;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Measure bounds>
|
|
||||||
{({ measureRef, contentRect }) => (
|
|
||||||
<div ref={measureRef} className="svgViewer">
|
|
||||||
<ReactSVGPanZoom
|
|
||||||
width={widthOrDefault(contentRect)}
|
|
||||||
height={heightOrDefault(contentRect)}
|
|
||||||
tool={tool}
|
|
||||||
value={val as any}
|
|
||||||
onChangeValue={v => (this.value = v)}
|
|
||||||
onChangeTool={this.setTool}
|
|
||||||
ref={e => this.setRef(e)}
|
|
||||||
>
|
|
||||||
<svg width={width} height={height}>
|
|
||||||
<g
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: svgContent,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</ReactSVGPanZoom>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Measure>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import {
|
|
||||||
TreeViewModel,
|
|
||||||
TreeNodeViewModel,
|
|
||||||
TreeView,
|
|
||||||
TreeWithPathView,
|
|
||||||
} from "./Views";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
TreeNode,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export class TreeVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { tree: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("tree"),
|
|
||||||
name: "Tree",
|
|
||||||
priority: 100,
|
|
||||||
render() {
|
|
||||||
const m = createTreeViewModelFromTreeNodeData(data.root);
|
|
||||||
return <TreeWithPathView model={m} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createTreeViewModelFromTreeNodeData<TData>(
|
|
||||||
root: TreeNode<TData>
|
|
||||||
): TreeViewModel<TData> {
|
|
||||||
const m = new TreeViewModel<TData>();
|
|
||||||
m.root = recurse(root, m);
|
|
||||||
return m;
|
|
||||||
|
|
||||||
function recurse(
|
|
||||||
node: TreeNode<TData>,
|
|
||||||
viewModel: TreeViewModel<TData>
|
|
||||||
): TreeNodeViewModel<TData> {
|
|
||||||
const children: TreeNodeViewModel<TData>[] = node.children.map(c =>
|
|
||||||
recurse(c, viewModel)
|
|
||||||
);
|
|
||||||
const model = new TreeNodeViewModel<TData>(
|
|
||||||
viewModel,
|
|
||||||
node.id,
|
|
||||||
node.name,
|
|
||||||
node.value,
|
|
||||||
node.emphasizedValue,
|
|
||||||
children,
|
|
||||||
node.data
|
|
||||||
);
|
|
||||||
model.isMarked = !!node.isMarked;
|
|
||||||
for (const c of children) {
|
|
||||||
c.parent = model;
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,530 +0,0 @@
|
|||||||
import { observer, disposeOnUnmount } from "mobx-react";
|
|
||||||
import { observable, autorun, computed, action, runInAction } from "mobx";
|
|
||||||
import * as React from "react";
|
|
||||||
import * as classNames from "classnames";
|
|
||||||
import { Rectangle, Point } from "../../utils/Point";
|
|
||||||
import { SvgLine } from "../../utils/SvgElements";
|
|
||||||
// import "./style.scss";
|
|
||||||
|
|
||||||
export class TreeViewModel<TData = unknown> {
|
|
||||||
private started = true;
|
|
||||||
start() {
|
|
||||||
this.started = true;
|
|
||||||
this.updateOffsets();
|
|
||||||
}
|
|
||||||
stop() {
|
|
||||||
this.started = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private version = 0;
|
|
||||||
private lastUpdateVersion = -1;
|
|
||||||
|
|
||||||
@observable public root: TreeNodeViewModel<TData> | undefined = undefined;
|
|
||||||
|
|
||||||
public invalidateOffsets() {
|
|
||||||
this.version++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
public updateOffsets() {
|
|
||||||
if (!this.started) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.root) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.lastUpdateVersion === this.version) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastUpdateVersion = this.version;
|
|
||||||
console.log("update");
|
|
||||||
this.root.updateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get marked(): TreeNodeViewModel<TData>[] {
|
|
||||||
const result = new Array<TreeNodeViewModel<TData>>();
|
|
||||||
function recurse(node: TreeNodeViewModel<TData>) {
|
|
||||||
if (node.isMarked) {
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
for (const c of node.children) {
|
|
||||||
recurse(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.root) {
|
|
||||||
recurse(this.root);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observable selected: TreeNodeViewModel<TData> | undefined = undefined;
|
|
||||||
|
|
||||||
@action public select(current: TreeNodeViewModel<TData> | undefined) {
|
|
||||||
if (this.selected === current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.selected) {
|
|
||||||
this.selected.isSelected = false;
|
|
||||||
}
|
|
||||||
if (current) {
|
|
||||||
current.isSelected = true;
|
|
||||||
}
|
|
||||||
this.selected = current;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action public toggleSelect(current: TreeNodeViewModel<TData>) {
|
|
||||||
if (this.selected === current) {
|
|
||||||
this.select(undefined);
|
|
||||||
} else {
|
|
||||||
this.select(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private selectOnHoverEnabled = false;
|
|
||||||
private selectedBeforeHover:
|
|
||||||
| TreeNodeViewModel<TData>
|
|
||||||
| undefined = undefined;
|
|
||||||
|
|
||||||
@action public handleHoverEvent(
|
|
||||||
current: TreeNodeViewModel<TData> | undefined
|
|
||||||
) {
|
|
||||||
if (this.selectOnHoverEnabled) {
|
|
||||||
if (current) {
|
|
||||||
this.select(current);
|
|
||||||
} else {
|
|
||||||
this.select(this.selectedBeforeHover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectOnHover(enable: boolean) {
|
|
||||||
if (this.selectOnHoverEnabled === enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
this.selectedBeforeHover = this.selected;
|
|
||||||
} else if (!this.selected) {
|
|
||||||
this.select(this.selectedBeforeHover);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectOnHoverEnabled = enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
export class TreeNodeViewModel<TData = unknown> {
|
|
||||||
public parent: TreeNodeViewModel<TData> | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public readonly treeViewModel: TreeViewModel<TData>,
|
|
||||||
public readonly id: string | undefined,
|
|
||||||
public readonly name: string,
|
|
||||||
public readonly value: string | undefined,
|
|
||||||
public readonly emphasizedValue: string | undefined,
|
|
||||||
public readonly children: TreeNodeViewModel<TData>[],
|
|
||||||
public readonly data: TData
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@observable expanderOffsetRect: Rectangle | undefined = undefined;
|
|
||||||
private expanderDiv: HTMLDivElement | null = null;
|
|
||||||
|
|
||||||
@action.bound
|
|
||||||
setExpanderDiv(div: HTMLDivElement | null) {
|
|
||||||
this.expanderDiv = div;
|
|
||||||
this.treeViewModel.invalidateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
@observable public expanded: boolean = false;
|
|
||||||
@observable public isMarked: boolean = false;
|
|
||||||
@observable public isSelected: boolean = false;
|
|
||||||
|
|
||||||
@action
|
|
||||||
public toggleExpanded() {
|
|
||||||
this.expanded = !this.expanded;
|
|
||||||
this.treeViewModel.invalidateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed public get isChildOrThisMarked(): boolean {
|
|
||||||
if (this.isMarked) return true;
|
|
||||||
return this.children.some(c => c.isChildOrThisMarked);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed public get isChildOrThisSelected(): boolean {
|
|
||||||
if (this.isSelected) return true;
|
|
||||||
return this.children.some(c => c.isChildOrThisSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed public get isParentOrThisSelected(): boolean {
|
|
||||||
if (this.isSelected) return true;
|
|
||||||
if (this.parent) return this.parent.isParentOrThisSelected;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get path(): string[] {
|
|
||||||
const result = new Array<string>();
|
|
||||||
let c: TreeNodeViewModel = this;
|
|
||||||
while (c.parent) {
|
|
||||||
result.unshift(c.id || c.name);
|
|
||||||
c = c.parent;
|
|
||||||
}
|
|
||||||
if (result.length === 0) {
|
|
||||||
result.push("root");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOffsets() {
|
|
||||||
for (const c of this.children) {
|
|
||||||
c.updateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.expanderDiv) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newRect = Rectangle.ofSize(
|
|
||||||
new Point(this.expanderDiv.offsetLeft, this.expanderDiv.offsetTop),
|
|
||||||
new Point(
|
|
||||||
this.expanderDiv.offsetWidth,
|
|
||||||
this.expanderDiv.offsetHeight
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
!this.expanderOffsetRect ||
|
|
||||||
!newRect.equals(this.expanderOffsetRect)
|
|
||||||
) {
|
|
||||||
this.expanderOffsetRect = newRect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValidFunctionName = (function() {
|
|
||||||
var validName = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
||||||
var reserved = {
|
|
||||||
abstract: true,
|
|
||||||
boolean: true,
|
|
||||||
// ...
|
|
||||||
with: true,
|
|
||||||
} as any;
|
|
||||||
return function(s: string) {
|
|
||||||
// Ensure a valid name and not reserved.
|
|
||||||
return validName.test(s) && !reserved[s];
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class TreeWithPathView extends React.Component<{
|
|
||||||
model: TreeViewModel;
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const model = this.props.model;
|
|
||||||
return (
|
|
||||||
<div className="component-TreeWithPathView">
|
|
||||||
<div className="part-path">
|
|
||||||
<span>
|
|
||||||
{model.selected ? (
|
|
||||||
<span>
|
|
||||||
{model.selected.path.reduce((acc, v) => {
|
|
||||||
acc = acc.slice();
|
|
||||||
function add() {
|
|
||||||
acc.push(
|
|
||||||
<span
|
|
||||||
className="part-path-item"
|
|
||||||
key={acc.length}
|
|
||||||
>
|
|
||||||
{v}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isValidFunctionName(v)) {
|
|
||||||
if (acc.length > 0) {
|
|
||||||
acc.push(
|
|
||||||
<span key={acc.length}>.</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
add();
|
|
||||||
} else {
|
|
||||||
acc.push(
|
|
||||||
<span key={acc.length}>[</span>
|
|
||||||
);
|
|
||||||
add();
|
|
||||||
acc.push(
|
|
||||||
<span key={acc.length}>]</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, new Array<React.ReactElement>())}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
"(Nothing Selected)"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="part-tree">
|
|
||||||
<TreeView model={model} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class TreeView extends React.Component<{ model: TreeViewModel }> {
|
|
||||||
componentWillUpdate(newProps: this["props"]) {
|
|
||||||
newProps.model.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.props.model.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.props.model.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.model.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const model = this.props.model;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
tabIndex={0}
|
|
||||||
className="component-TreeView"
|
|
||||||
onKeyDown={e => {
|
|
||||||
if (e.keyCode === 18) {
|
|
||||||
// alt
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
model.setSelectOnHover(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onKeyUp={e => {
|
|
||||||
if (e.keyCode === 18) {
|
|
||||||
// alt
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
model.setSelectOnHover(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{model.root && (
|
|
||||||
<>
|
|
||||||
<svg className="part-svg">
|
|
||||||
<TreeNodeSvgView model={model.root} />
|
|
||||||
</svg>
|
|
||||||
<div className="part-node">
|
|
||||||
<TreeNodeView model={model.root} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class TreeNodeSvgView extends React.Component<{
|
|
||||||
model: TreeNodeViewModel;
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const model = this.props.model;
|
|
||||||
if (
|
|
||||||
!model.expanderOffsetRect ||
|
|
||||||
model.children.length === 0 ||
|
|
||||||
model.expanded
|
|
||||||
) {
|
|
||||||
return <g />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findLastExpanderOffset(
|
|
||||||
predicate: (v: TreeNodeViewModel) => boolean
|
|
||||||
): Rectangle | undefined {
|
|
||||||
const all = model.children
|
|
||||||
.filter(predicate)
|
|
||||||
.map(c => c.expanderOffsetRect);
|
|
||||||
const last = all[all.length - 1];
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastOffset = findLastExpanderOffset(() => true);
|
|
||||||
const selectedOffset = findLastExpanderOffset(
|
|
||||||
v => v.isChildOrThisSelected
|
|
||||||
);
|
|
||||||
const markedOffset = findLastExpanderOffset(v => v.isChildOrThisMarked);
|
|
||||||
|
|
||||||
if (!lastOffset) {
|
|
||||||
return <g />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = model.expanderOffsetRect.center.add({ y: 10 });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g className="component-TreeNodeSvgView">
|
|
||||||
{markedOffset && (
|
|
||||||
<SvgLine
|
|
||||||
className="path childOrThisMarked"
|
|
||||||
start={start}
|
|
||||||
end={new Point(start.x, markedOffset.center.y)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{selectedOffset && (
|
|
||||||
<SvgLine
|
|
||||||
className="path childOrThisSelected"
|
|
||||||
start={start}
|
|
||||||
end={new Point(start.x, selectedOffset.center.y)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<SvgLine
|
|
||||||
className="path"
|
|
||||||
start={start}
|
|
||||||
end={new Point(start.x, lastOffset.center.y)}
|
|
||||||
/>
|
|
||||||
{model.children.map((c, idx) => {
|
|
||||||
const end = c.expanderOffsetRect!.center.sub({ x: 5 });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g key={idx}>
|
|
||||||
{c.isChildOrThisMarked && (
|
|
||||||
<SvgLine
|
|
||||||
className={classNames(
|
|
||||||
"path",
|
|
||||||
"childOrThisMarked"
|
|
||||||
)}
|
|
||||||
start={new Point(start.x, end.y)}
|
|
||||||
end={end}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<SvgLine
|
|
||||||
className={classNames(
|
|
||||||
"path",
|
|
||||||
c.isChildOrThisSelected &&
|
|
||||||
"childOrThisSelected"
|
|
||||||
)}
|
|
||||||
start={new Point(start.x, end.y)}
|
|
||||||
end={end}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{model.children.map((c, idx) => (
|
|
||||||
<TreeNodeSvgView key={idx} model={c} />
|
|
||||||
))}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class TreeNodeView extends React.Component<{
|
|
||||||
model: TreeNodeViewModel;
|
|
||||||
}> {
|
|
||||||
@action.bound
|
|
||||||
private clickHandler() {
|
|
||||||
this.props.model.treeViewModel.toggleSelect(this.props.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleMouseEnterOrLeave(enter: boolean) {
|
|
||||||
this.props.model.treeViewModel.handleHoverEvent(
|
|
||||||
enter ? this.props.model : undefined
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@observable
|
|
||||||
private rootDiv: HTMLElement | null = null;
|
|
||||||
@action.bound
|
|
||||||
private setRootDiv(div: HTMLElement | null) {
|
|
||||||
this.rootDiv = div;
|
|
||||||
}
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
private readonly _autorun = autorun(() => {
|
|
||||||
if (this.props.model.isMarked && this.rootDiv) {
|
|
||||||
this.rootDiv.scrollIntoView();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public componentDidMount() {
|
|
||||||
this.props.model.treeViewModel.updateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidUpdate() {
|
|
||||||
this.props.model.treeViewModel.updateOffsets();
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const model = this.props.model;
|
|
||||||
const collapsed = model.expanded;
|
|
||||||
console.log("render");
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"component-TreeNodeView",
|
|
||||||
model.expanded && "collapsed",
|
|
||||||
model.isMarked && "selected",
|
|
||||||
model.isSelected && "hovered",
|
|
||||||
model.isChildOrThisSelected &&
|
|
||||||
!model.isSelected &&
|
|
||||||
"childHovered",
|
|
||||||
model.isParentOrThisSelected &&
|
|
||||||
!model.isSelected &&
|
|
||||||
"parentHovered"
|
|
||||||
)}
|
|
||||||
ref={this.setRootDiv}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="part-header"
|
|
||||||
onMouseOver={e => {
|
|
||||||
this.handleMouseEnterOrLeave(true);
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
onMouseOut={e => {
|
|
||||||
this.handleMouseEnterOrLeave(false);
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={model.setExpanderDiv}
|
|
||||||
onClick={() => model.toggleExpanded()}
|
|
||||||
className={classNames(
|
|
||||||
"part-collapser",
|
|
||||||
model.children.length > 0 &&
|
|
||||||
"decorator-collapse-icon",
|
|
||||||
!collapsed && "expanded"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="part-text" onClick={this.clickHandler}>
|
|
||||||
{model.id && (
|
|
||||||
<>
|
|
||||||
<span className="part-id">{model.id}</span>
|
|
||||||
<span>: </span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<span className="part-name">{model.name}</span>
|
|
||||||
{model.value && (
|
|
||||||
<span className="part-value">{model.value}</span>
|
|
||||||
)}
|
|
||||||
{model.emphasizedValue && (
|
|
||||||
<span className="part-emphasized-value">
|
|
||||||
{model.emphasizedValue}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{model.children.length > 0 && !model.expanded && (
|
|
||||||
<div className="part-children">
|
|
||||||
{model.children.map((c, idx) => (
|
|
||||||
<TreeNodeView model={c} key={idx} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
.component-TreeView {
|
|
||||||
overflow: auto;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
|
|
||||||
& > .part-svg,
|
|
||||||
& > .part-node {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
grid-column: 1;
|
|
||||||
grid-row: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-TreeNodeSvgView {
|
|
||||||
.path {
|
|
||||||
stroke: gray;
|
|
||||||
stroke-dasharray: 1;
|
|
||||||
|
|
||||||
&.childOrThisMarked {
|
|
||||||
stroke: orange;
|
|
||||||
stroke-width: 5;
|
|
||||||
stroke-dasharray: 0;
|
|
||||||
stroke-linecap: round;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.childOrThisSelected {
|
|
||||||
stroke: black;
|
|
||||||
stroke-width: 2;
|
|
||||||
stroke-dasharray: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-TreeNodeView {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe WPC, Segoe UI,
|
|
||||||
HelveticaNeue-Light, Ubuntu, Droid Sans, sans-serif;
|
|
||||||
|
|
||||||
.part-header {
|
|
||||||
padding: 2px;
|
|
||||||
padding-left: 5px;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.part-collapser {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-text {
|
|
||||||
cursor: pointer;
|
|
||||||
.part-id {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-value {
|
|
||||||
color: darkgreen;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
/*&::before,
|
|
||||||
&::after {
|
|
||||||
content: '"';
|
|
||||||
}*/
|
|
||||||
|
|
||||||
padding: 1px 3px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-emphasized-value {
|
|
||||||
color: darkred;
|
|
||||||
border: black solid 1px;
|
|
||||||
padding: 1px 3px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-children {
|
|
||||||
margin-left: 0px;
|
|
||||||
margin-top: 0px;
|
|
||||||
|
|
||||||
padding: 5px;
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
& > .part-header {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.hovered {
|
|
||||||
& > .part-header > span {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
& > .part-children {
|
|
||||||
background: transparentize(gray, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-TreeWithPathView {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.part-path {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: pre;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-bottom: solid gray 1px;
|
|
||||||
|
|
||||||
color: lightgray;
|
|
||||||
|
|
||||||
.part-path-item {
|
|
||||||
color: black;
|
|
||||||
margin-left: 1px;
|
|
||||||
margin-right: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.part-tree {
|
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.decorator-collapse-icon {
|
|
||||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath fill='%23646465' d='M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z'/%3E%3C/svg%3E");
|
|
||||||
|
|
||||||
background-position: 50%;
|
|
||||||
background-position-x: 50%;
|
|
||||||
background-position-y: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
&.expanded {
|
|
||||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath fill='%23646465' d='M11 10.07H5.344L11 4.414v5.656z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { observer, disposeOnUnmount } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { observable, autorun, runInAction } from "mobx";
|
|
||||||
import { SvgViewer } from "../SvgVisualizer";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
|
|
||||||
export class GraphvizDotVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { dotGraph: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("graphviz-dot"),
|
|
||||||
name: "Graphviz (Dot Data)",
|
|
||||||
priority: 100,
|
|
||||||
render() {
|
|
||||||
return <GraphvizDotViewer dotCode={data.text} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VisLoader {
|
|
||||||
private result: any | undefined;
|
|
||||||
|
|
||||||
async getViz(): Promise<any> {
|
|
||||||
if (!this.result) {
|
|
||||||
const Viz = await import("viz.js");
|
|
||||||
const { Module, render } = await import("viz.js/full.render.js");
|
|
||||||
|
|
||||||
const viz = new Viz.default({
|
|
||||||
Module: () => Module({ TOTAL_MEMORY: 1 << 30 }),
|
|
||||||
render,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.result = viz;
|
|
||||||
}
|
|
||||||
return this.result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const vizLoader = new VisLoader();
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class GraphvizDotViewer extends React.Component<{
|
|
||||||
dotCode: string;
|
|
||||||
svgRef?: (element: SVGSVGElement | null) => void;
|
|
||||||
}> {
|
|
||||||
@observable private svg: string | null = null;
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
// @ts-ignore
|
|
||||||
private readonly _updateSvgAutorun = autorun(async () => {
|
|
||||||
const viz = await vizLoader.getViz();
|
|
||||||
const svg = await viz.renderString(this.props.dotCode);
|
|
||||||
runInAction("Update svg", () => (this.svg = svg));
|
|
||||||
});
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.svg) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
return <SvgViewer svgRef={this.props.svgRef} svgContent={this.svg} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { observer } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { GraphvizDotViewer } from "./GraphvizDotVisualizer";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
|
|
||||||
export class GraphvizGraphVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { graph: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("graphviz-graph"),
|
|
||||||
name: "Graphviz",
|
|
||||||
priority: 100,
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<GraphvizGraphViewer
|
|
||||||
edges={data.edges}
|
|
||||||
nodes={data.nodes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class GraphvizGraphViewer extends React.Component<{
|
|
||||||
nodes: { id: string; label?: string }[];
|
|
||||||
edges: { from: string; to: string; label?: string }[];
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const { nodes, edges } = this.props;
|
|
||||||
const dotContent = `
|
|
||||||
digraph MyGraph {
|
|
||||||
${nodes
|
|
||||||
.map(
|
|
||||||
n =>
|
|
||||||
`"${n.id}" [ label = ${JSON.stringify(
|
|
||||||
n.label !== undefined ? n.label : n.id
|
|
||||||
)} ];`
|
|
||||||
)
|
|
||||||
.join("\n ")}
|
|
||||||
${edges
|
|
||||||
.map(
|
|
||||||
e =>
|
|
||||||
`"${e.from}" -> "${
|
|
||||||
e.to
|
|
||||||
}" [ label = ${JSON.stringify(
|
|
||||||
e.label !== undefined ? e.label : ""
|
|
||||||
)} ];`
|
|
||||||
)
|
|
||||||
.join("\n")}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
return <GraphvizDotViewer dotCode={dotContent} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import { DataSet, Network, Options } from "vis-network";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
NodeGraphData,
|
|
||||||
EdgeGraphData,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class VisJsGraphViewer extends React.Component<{
|
|
||||||
nodes: NodeGraphData[];
|
|
||||||
edges: EdgeGraphData[];
|
|
||||||
}> {
|
|
||||||
private readonly divRef = React.createRef<HTMLDivElement>();
|
|
||||||
private readonly nodes = new DataSet<{
|
|
||||||
id: string;
|
|
||||||
label?: string;
|
|
||||||
color?: string;
|
|
||||||
shape?: string;
|
|
||||||
}>();
|
|
||||||
private readonly edges = new DataSet<{
|
|
||||||
id: string;
|
|
||||||
from: string;
|
|
||||||
to: string;
|
|
||||||
label?: string;
|
|
||||||
color?: string;
|
|
||||||
dashes?: boolean;
|
|
||||||
shape?: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <div style={{ height: "100%" }} ref={this.divRef} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronizeData() {
|
|
||||||
const newNodes = new Set<string>();
|
|
||||||
for (const n of this.props.nodes) {
|
|
||||||
newNodes.add(n.id);
|
|
||||||
this.nodes.update({
|
|
||||||
id: n.id,
|
|
||||||
label: n.label !== undefined ? n.label : n.id,
|
|
||||||
color: n.color,
|
|
||||||
shape: n.shape,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.nodes.forEach(item => {
|
|
||||||
if (!newNodes.has(item.id)) {
|
|
||||||
this.nodes.remove(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getIdOfEdge(e: EdgeGraphData): string {
|
|
||||||
if (e.id) {
|
|
||||||
return e.id;
|
|
||||||
}
|
|
||||||
return e.from + "####" + e.to + "|" + e.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newEdges = new Set<string>();
|
|
||||||
for (const n of this.props.edges) {
|
|
||||||
const id = getIdOfEdge(n);
|
|
||||||
newEdges.add(id);
|
|
||||||
this.edges.update({
|
|
||||||
id: id,
|
|
||||||
label: n.label !== undefined ? n.label : "",
|
|
||||||
from: n.from,
|
|
||||||
to: n.to,
|
|
||||||
color: n.color,
|
|
||||||
dashes: n.dashes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.edges.forEach(item => {
|
|
||||||
if (!newEdges.has(item.id)) {
|
|
||||||
this.edges.remove(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.synchronizeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.synchronizeData();
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
nodes: this.nodes,
|
|
||||||
edges: this.edges,
|
|
||||||
};
|
|
||||||
const options: Options = {
|
|
||||||
edges: {
|
|
||||||
arrows: {
|
|
||||||
to: { enabled: true, scaleFactor: 1, type: "arrow" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const network = new Network(this.divRef.current!, data, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import { makeLazyLoadable } from "../../LazyLoadable";
|
|
||||||
// import { DataSet, Network, Options } from "vis-network";
|
|
||||||
|
|
||||||
const VisJsGraphViewerLazyLoadable = makeLazyLoadable(
|
|
||||||
async () => (await import("./VisJsGraphViewer")).VisJsGraphViewer
|
|
||||||
);
|
|
||||||
|
|
||||||
export class VisJsGraphVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { graph: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("vis-js-graph"),
|
|
||||||
name: "vis.js",
|
|
||||||
priority: 1001,
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<VisJsGraphViewerLazyLoadable
|
|
||||||
edges={data.edges}
|
|
||||||
nodes={data.nodes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { ComposedVisualizer, Visualizer } from "../Visualizer";
|
|
||||||
import { EnabledVisualizers } from "../EnabledVisualizers";
|
|
||||||
|
|
||||||
declare const ENABLED_VISUALIZERS: EnabledVisualizers;
|
|
||||||
|
|
||||||
function isVisualizer(v: Visualizer | false): v is Visualizer {
|
|
||||||
return !!v;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const knownVisualizations = new ComposedVisualizer(
|
|
||||||
[
|
|
||||||
ENABLED_VISUALIZERS.VisJsGraphVisualizer &&
|
|
||||||
new (require("./graph-visualizers/VisJsGraphVisualizer") as typeof import("./graph-visualizers/VisJsGraphVisualizer")).VisJsGraphVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.TreeVisualizer &&
|
|
||||||
new (require("./TreeVisualizer/TreeVisualizer") as typeof import("./TreeVisualizer/TreeVisualizer")).TreeVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.GraphvizGraphVisualizer &&
|
|
||||||
new (require("./graph-visualizers/GraphvizGraphVisualizer") as typeof import("./graph-visualizers/GraphvizGraphVisualizer")).GraphvizGraphVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.SvgVisualizer &&
|
|
||||||
new (require("./SvgVisualizer") as typeof import("./SvgVisualizer")).SvgVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.GraphvizDotVisualizer &&
|
|
||||||
new (require("./graph-visualizers/GraphvizDotVisualizer") as typeof import("./graph-visualizers/GraphvizDotVisualizer")).GraphvizDotVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.TextVisualizer &&
|
|
||||||
new (require("./text-visualizers/TextVisualizer") as typeof import("./text-visualizers/TextVisualizer")).TextVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.PlotlyVisualizer &&
|
|
||||||
new (require("./plotly") as typeof import("./plotly")).PlotlyVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.GridVisualizer &&
|
|
||||||
new (require("./GridVisualizer") as typeof import("./GridVisualizer")).GridVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.MonacoTextVisualizer &&
|
|
||||||
new (require("./text-visualizers/MonacoTextVisualizer") as typeof import("./text-visualizers/MonacoTextVisualizer")).MonacoTextVisualizer(),
|
|
||||||
ENABLED_VISUALIZERS.AstVisualizer &&
|
|
||||||
new (require("./AstVisualizer") as typeof import("./AstVisualizer")).AstVisualizer(),
|
|
||||||
].filter(isVisualizer)
|
|
||||||
);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { observer } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import Plot from "react-plotly.js";
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class PlotlyViewer extends React.Component<{
|
|
||||||
data: Partial<Plotly.Data>[];
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Plot
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
data={this.props.data}
|
|
||||||
layout={{}}
|
|
||||||
config={{ responsive: true }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import { makeLazyLoadable } from "../../LazyLoadable";
|
|
||||||
|
|
||||||
const PlotlyViewerLazyLoadable = makeLazyLoadable(
|
|
||||||
async () => (await import("./PlotlyViewer")).PlotlyViewer
|
|
||||||
);
|
|
||||||
|
|
||||||
export class PlotlyVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { plotly: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("plotly"),
|
|
||||||
name: "plotly",
|
|
||||||
priority: 1001,
|
|
||||||
render() {
|
|
||||||
return <PlotlyViewerLazyLoadable data={data.data} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
.component-monaco-editor {
|
|
||||||
min-height: 0;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
// overflow: hidden;
|
|
||||||
|
|
||||||
.part-editor {
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-AstTree {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.part-tree2 {
|
|
||||||
flex: 2;
|
|
||||||
min-height: 0;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
.part-editor {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
min-width: 0;
|
|
||||||
border-top: solid gray 1px;
|
|
||||||
|
|
||||||
.marked {
|
|
||||||
background: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
background: yellow;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
.part-tree2 {
|
|
||||||
flex: 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-editor {
|
|
||||||
min-height: 0;
|
|
||||||
min-width: 0;
|
|
||||||
flex: 1;
|
|
||||||
background: red;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-Grid {
|
|
||||||
.part-data {
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-header {
|
|
||||||
background: transparentize(white, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-empty {
|
|
||||||
color: var(--vscode-editor-foreground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import * as React from "react";
|
|
||||||
import { observer, disposeOnUnmount } from "mobx-react";
|
|
||||||
import { autorun, observable } from "mobx";
|
|
||||||
|
|
||||||
export class MonacoTextVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { text: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("monaco-text"),
|
|
||||||
name: "Monaco Editor",
|
|
||||||
priority: 95,
|
|
||||||
render() {
|
|
||||||
let id = "text";
|
|
||||||
if (data.fileName) {
|
|
||||||
id = getLanguageId(data.fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <MonacoEditor text={data.text} languageId={id} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLanguageId(fileName: string): string {
|
|
||||||
const l = monaco.languages.getLanguages();
|
|
||||||
const result = l.find(l => {
|
|
||||||
if (l.filenamePatterns) {
|
|
||||||
for (const p of l.filenamePatterns) {
|
|
||||||
if (new RegExp(p).test(fileName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (l.extensions) {
|
|
||||||
for (const p of l.extensions) {
|
|
||||||
if (fileName.endsWith(p)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return result.id;
|
|
||||||
}
|
|
||||||
return "text";
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class MonacoEditor extends React.Component<{
|
|
||||||
text: string;
|
|
||||||
languageId: string;
|
|
||||||
}> {
|
|
||||||
@observable private editor: monaco.editor.IStandaloneCodeEditor | undefined;
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private model: monaco.editor.ITextModel | undefined = undefined;
|
|
||||||
|
|
||||||
@disposeOnUnmount
|
|
||||||
private _updateText = autorun(() => {
|
|
||||||
if (this.editor) {
|
|
||||||
const model = monaco.editor.createModel(
|
|
||||||
this.props.text,
|
|
||||||
this.props.languageId,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
this.editor.setModel(model);
|
|
||||||
|
|
||||||
/* this.editor!.updateOptions({ readOnly: false });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const a = this.editor!.getAction(
|
|
||||||
"editor.action.formatDocument"
|
|
||||||
);
|
|
||||||
a.run().then(() => {
|
|
||||||
this.editor!.updateOptions({ readOnly: true });
|
|
||||||
});
|
|
||||||
}, 200);*/
|
|
||||||
|
|
||||||
if (this.model) {
|
|
||||||
this.model.dispose();
|
|
||||||
}
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private readonly setEditorDiv = (editorDiv: HTMLDivElement) => {
|
|
||||||
if (!editorDiv) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor = monaco.editor.create(editorDiv, {
|
|
||||||
model: null,
|
|
||||||
automaticLayout: true,
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
minimap: { enabled: false },
|
|
||||||
fixedOverflowWidgets: true,
|
|
||||||
readOnly: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="component-monaco-editor">
|
|
||||||
<div className="part-editor" ref={this.setEditorDiv} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import {
|
|
||||||
Visualizer,
|
|
||||||
VisualizationCollector,
|
|
||||||
asVisualizationId,
|
|
||||||
} from "../../Visualizer";
|
|
||||||
import {
|
|
||||||
ExtractedData,
|
|
||||||
isCommonDataType,
|
|
||||||
} from "@hediet/debug-visualizer-data-extraction";
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export class TextVisualizer extends Visualizer {
|
|
||||||
visualize(data: ExtractedData, collector: VisualizationCollector): void {
|
|
||||||
if (isCommonDataType(data, { text: true })) {
|
|
||||||
collector.addVisualization({
|
|
||||||
id: asVisualizationId("text"),
|
|
||||||
name: "Plain Text",
|
|
||||||
priority: 90,
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<pre style={{ marginLeft: 20, marginRight: 20 }}>
|
|
||||||
{data.text}
|
|
||||||
</pre>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
@import "./src/visualizers/style.scss";
|
|
||||||
@import "./src/visualizers/TreeVisualizer/style.scss";
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"module": "es2020",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"strict": true,
|
|
||||||
"outDir": "dist",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"rootDir": "./src",
|
|
||||||
"newLine": "LF",
|
|
||||||
"sourceMap": true,
|
|
||||||
"jsx": "react",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*"]
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server --hot",
|
"dev": "webpack-dev-server --hot",
|
||||||
"build": "webpack"
|
"build": "node --max_old_space_size=4096 ../node_modules/webpack/bin/webpack.js --mode production"
|
||||||
},
|
},
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -19,7 +19,11 @@
|
|||||||
"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.1.1",
|
||||||
|
"@hediet/visualization-core": "^0.1.0",
|
||||||
|
"@knuddels/mobx-logger": "^1.1.1",
|
||||||
|
"less-loader": "^6.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/html-webpack-plugin": "^3.2.2",
|
"@types/html-webpack-plugin": "^3.2.2",
|
||||||
@@ -34,9 +38,8 @@
|
|||||||
"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": "^1.9.0",
|
||||||
"node-sass": "^4.13.1",
|
|
||||||
"raw-loader": "^4.0.0",
|
|
||||||
"sass": "^1.25.0",
|
"sass": "^1.25.0",
|
||||||
|
"raw-loader": "^4.0.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"style-loader": "^1.1.3",
|
"style-loader": "^1.1.3",
|
||||||
"ts-loader": "^6.2.1",
|
"ts-loader": "^6.2.1",
|
||||||
|
|||||||
26
webview/src/components/App.tsx
Normal file
26
webview/src/components/App.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { hotComponent } from "../hotComponent";
|
||||||
|
import { GUI } from "./GUI";
|
||||||
|
import { Model } from "../model/Model";
|
||||||
|
|
||||||
|
@hotComponent(module)
|
||||||
|
export class App extends React.Component {
|
||||||
|
private readonly model = new Model();
|
||||||
|
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
if (this.model.runningMode !== "webview") {
|
||||||
|
if (this.model.theme === "light") {
|
||||||
|
require("../vscode-light.scss");
|
||||||
|
} else {
|
||||||
|
require("../vscode-dark.scss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(this.model.theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <GUI model={this.model} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user