This commit is contained in:
Henning Dieterichs
2020-02-10 12:13:07 +01:00
parent 51957d52eb
commit 8aae7f7b0d
45 changed files with 625 additions and 413 deletions

View File

@@ -1,90 +1,42 @@
import React = require("react");
import { Model } from "../Model/Model";
import { observer } from "mobx-react";
import { observable, computed } from "mobx";
import { Popover, Button, Spinner } from "@blueprintjs/core";
import classnames = require("classnames");
import { Popover } from "@blueprintjs/core";
import { DataExtractorInfo } from "@hediet/debug-visualizer-data-extraction";
import { Visualizer } from "./Visualizer";
import { ExpressionInput } from "./ExpressionInput";
@observer
export class GUI extends React.Component<{ model: Model }> {
@observable text: string = "node";
@observable expanded = false;
render() {
const m = this.props.model;
return (
<div className="component-GUI" tabIndex={0}>
<div className="part-header">
<ExpandButton
expanded={this.expanded}
setExpanded={v => (this.expanded = v)}
/>
<div className="part-header-content">
<div className="part-header-main">
<div className="part-expression-input ">
<ExpressionInput model={m} />
</div>
<div style={{ width: 4 }} />
{m.loading ? (
<div style={{ padding: "0 4px" }}>
<Spinner size={Spinner.SIZE_SMALL} />
</div>
) : (
<Button
minimal
small
className="part-icon"
icon="refresh"
onClick={() => m.refresh()}
/>
)}
<Button
minimal
small
className="part-icon"
icon="log-in"
onClick={() => m.openBrowser()}
/>
<Button
minimal
small
className="part-icon"
icon="locate"
onClick={() => m.useSelectionAsExpression()}
/>
</div>
{this.expanded && <ExpandedOptions model={m} />}
</div>
</div>
<div className="part-visualizer">
<Visualizer model={m} />
</div>
</div>
);
}
renderValue(value: string) {
return <div>{`${value.length}`}</div>;
}
}
import { computed } from "mobx";
import { observer } from "mobx-react";
import { Model } from "../Model/Model";
import { VisualizationId } from "../Visualizers/Visualizer";
@observer
export class ExpandedOptions extends React.Component<{ model: Model }> {
render() {
const m = this.props.model;
let availableExtractors = new Array<DataExtractorInfo>();
let selected = -1;
let selectedExtractor = -1;
if (m.state.kind === "data") {
const result = m.state.result;
availableExtractors = result.availableExtractors;
selected = result.availableExtractors.findIndex(
selectedExtractor = result.availableExtractors.findIndex(
i => i.id === result.usedExtractor.id
);
}
let visualizations = new Array<{
label: string;
id: VisualizationId;
}>();
let selectedVisualization = -1;
if (m.visualizations) {
visualizations = m.visualizations.allVisualizations.map(v => ({
label: v.name,
id: v.id,
}));
if (m.visualizations.visualization) {
selectedVisualization = visualizations.findIndex(
v => v.id === m.visualizations!.visualization!.id
);
}
}
return (
<>
<div style={{ height: 6 }} />
@@ -99,7 +51,7 @@ export class ExpandedOptions extends React.Component<{ model: Model }> {
<NamedSelect
name="Extractor"
selected={selected}
selected={selectedExtractor}
options={availableExtractors.map(e => ({
label: e.name,
id: e.id,
@@ -112,7 +64,7 @@ export class ExpandedOptions extends React.Component<{ model: Model }> {
<NamedSelect
name="Visualizer"
selected={0}
selected={selectedVisualization}
onSelected={item => {
m.setPreferredVisualizationId(item.id);
}}
@@ -131,23 +83,6 @@ export class ExpandedOptions extends React.Component<{ model: Model }> {
}
}
export class ExpandButton extends React.Component<{
expanded: boolean;
setExpanded: (val: boolean) => void;
}> {
render() {
return (
<div
onClick={() => this.props.setExpanded(!this.props.expanded)}
className={classnames(
"component-ExpandButton",
this.props.expanded && "expanded"
)}
/>
);
}
}
function NamedSelect<T extends SelectableItem>(props: {
name: string;
selected: number;
@@ -169,7 +104,7 @@ interface SelectableItem {
}
@observer
export class Select<T extends SelectableItem> extends React.Component<{
class Select<T extends SelectableItem> extends React.Component<{
selected: number;
onSelected: (item: T) => void;
options: T[];

View File

@@ -0,0 +1,93 @@
import React = require("react");
import { Button, Spinner } from "@blueprintjs/core";
import { observable } from "mobx";
import { observer } from "mobx-react";
import { Model } from "../Model/Model";
import { ExpressionInput } from "./ExpressionInput";
import { Visualizer } from "./Visualizer";
import classnames = require("classnames");
import { ExpandedOptions } from "./ExpandedOptions";
@observer
export class GUI extends React.Component<{ model: Model }> {
@observable text: string = "node";
@observable expanded = false;
render() {
const m = this.props.model;
return (
<div className="component-GUI" tabIndex={0}>
<div className="part-header">
<ExpandButton
expanded={this.expanded}
setExpanded={v => (this.expanded = v)}
/>
<div className="part-header-content">
<div className="part-header-main">
<div className="part-expression-input ">
<ExpressionInput model={m} />
</div>
<div style={{ width: 4 }} />
{m.loading ? (
<div style={{ padding: "0 4px" }}>
<Spinner size={Spinner.SIZE_SMALL} />
</div>
) : (
<Button
minimal
small
className="part-icon"
icon="refresh"
onClick={() => m.refresh()}
/>
)}
<Button
minimal
small
className="part-icon"
icon="log-in"
onClick={() => m.openBrowser()}
/>
{/*
TODO
<Button
minimal
small
className="part-icon"
icon="locate"
onClick={() => m.useSelectionAsExpression()}
/>
*/}
</div>
{this.expanded && <ExpandedOptions model={m} />}
</div>
</div>
<div className="part-visualizer">
<Visualizer model={m} />
</div>
</div>
);
}
renderValue(value: string) {
return <div>{`${value.length}`}</div>;
}
}
export class ExpandButton extends React.Component<{
expanded: boolean;
setExpanded: (val: boolean) => void;
}> {
render() {
return (
<div
onClick={() => this.props.setExpanded(!this.props.expanded)}
className={classnames(
"component-ExpandButton",
this.props.expanded && "expanded"
)}
/>
);
}
}

View File

@@ -13,10 +13,12 @@ import { MonacoBridge } from "./MonacoBridge";
declare const window: Window & {
serverPort?: number;
serverSecret?: string;
};
export class Model {
port: number;
private port: number;
private serverSecret: string;
@observable expression: string = "";
@observable state: DataExtractionState | { kind: "noExpression" } = {
@@ -67,6 +69,7 @@ export class Model {
constructor() {
if (window.serverPort) {
this.port = window.serverPort;
this.serverSecret = window.serverSecret!;
} else {
const url = new URL(window.location.href);
const portStr = url.searchParams.get("serverPort");
@@ -79,6 +82,12 @@ export class Model {
if (expr) {
this.setExpression(expr);
}
const secret = url.searchParams.get("serverSecret");
if (!secret) {
throw new Error("Server secret not set.");
}
this.serverSecret = secret;
}
this.stayConnected();
@@ -141,6 +150,11 @@ export class Model {
},
}
);
try {
await server.authenticate({ secret: this.serverSecret });
} catch (e) {
console.error(e);
}
this.server = server;
await stream.onClosed;

View File

@@ -9,16 +9,12 @@ import {
CommonDataTypes,
} from "@hediet/debug-visualizer-data-extraction";
import { createTreeViewModelFromTreeNodeData } from "./TreeVisualizer/Visualizer";
import {
TreeWithPathView,
TreeViewModel,
TreeView,
} from "./TreeVisualizer/Views";
import { TreeWithPathView, TreeViewModel } from "./TreeVisualizer/Views";
import React = require("react");
import { observer, disposeOnUnmount } from "mobx-react";
import { observable, autorun, trace } from "mobx";
import * as monaco from "monaco-editor";
import { getLanguageId } from "./MonacoTextVisualizer";
import { getLanguageId } from "./TextVisualizer/MonacoTextVisualizer";
import LineColumn = require("line-column");
export class AstVisualizer extends VisualizationProvider {
@@ -70,7 +66,7 @@ interface NodeInfo {
@observer
export class AstTree extends React.Component<{
model: TreeViewModel<NodeInfo>;
data: CommonDataTypes.AstData;
data: CommonDataTypes.Ast;
nodeInfoToRange: (info: NodeInfo) => monaco.IRange;
}> {
render() {

View File

@@ -3,12 +3,12 @@ import * as React from "react";
import { observable, autorun, runInAction } from "mobx";
import Viz from "viz.js";
import { Module, render } from "viz.js/full.render.js";
import { SvgViewer } from "./SvgVisualizer";
import { SvgViewer } from "../SvgVisualizer";
import {
VisualizationProvider,
VisualizationCollector,
asVisualizationId,
} from "./Visualizer";
} from "../Visualizer";
import {
ExtractedData,
isCommonDataType,
@@ -19,18 +19,18 @@ const viz: any = new Viz({
render,
});
export class DotVisualizer extends VisualizationProvider {
export class GraphvizDotVisualizer extends VisualizationProvider {
getVisualizations(
data: ExtractedData,
collector: VisualizationCollector
): void {
if (isCommonDataType(data, { dotGraph: true })) {
collector.addVisualization({
id: asVisualizationId("dot-graph"),
name: "Dot Graph",
id: asVisualizationId("graphviz-dot"),
name: "Graphviz (Dot Data)",
priority: 100,
render() {
return <DotViewer dotCode={data.text} />;
return <GraphvizDotViewer dotCode={data.text} />;
},
});
}
@@ -38,7 +38,7 @@ export class DotVisualizer extends VisualizationProvider {
}
@observer
export class DotViewer extends React.Component<{
export class GraphvizDotViewer extends React.Component<{
dotCode: string;
svgRef?: (element: SVGSVGElement | null) => void;
}> {

View File

@@ -1,29 +1,32 @@
import { observer } from "mobx-react";
import * as React from "react";
import { DotViewer } from "./DotVisualizer";
import { GraphvizDotViewer } from "./GraphvizDotVisualizer";
import {
VisualizationProvider,
VisualizationCollector,
asVisualizationId,
} from "./Visualizer";
} from "../Visualizer";
import {
ExtractedData,
isCommonDataType,
} from "@hediet/debug-visualizer-data-extraction";
export class DotGraphVisualizer extends VisualizationProvider {
export class GraphvizGraphVisualizer extends VisualizationProvider {
getVisualizations(
data: ExtractedData,
collector: VisualizationCollector
): void {
if (isCommonDataType(data, { graph: true })) {
collector.addVisualization({
id: asVisualizationId("dot-graph"),
name: "Dot Graph",
id: asVisualizationId("graphviz-graph"),
name: "Graphviz",
priority: 100,
render() {
return (
<DotGraphViewer edges={data.edges} nodes={data.nodes} />
<GraphvizGraphViewer
edges={data.edges}
nodes={data.nodes}
/>
);
},
});
@@ -32,7 +35,7 @@ export class DotGraphVisualizer extends VisualizationProvider {
}
@observer
export class DotGraphViewer extends React.Component<{
export class GraphvizGraphViewer extends React.Component<{
nodes: { id: string; label: string }[];
edges: { from: string; to: string; label: string }[];
}> {
@@ -53,6 +56,6 @@ export class DotGraphViewer extends React.Component<{
.join("\n")}
}
`;
return <DotViewer dotCode={dotContent} />;
return <GraphvizDotViewer dotCode={dotContent} />;
}
}

View File

@@ -4,10 +4,12 @@ import {
VisualizationProvider,
VisualizationCollector,
asVisualizationId,
} from "./Visualizer";
} from "../Visualizer";
import {
ExtractedData,
isCommonDataType,
NodeGraphData,
EdgeGraphData,
} from "@hediet/debug-visualizer-data-extraction";
import { DataSet, Network } from "vis-network";
@@ -19,7 +21,7 @@ export class VisJsGraphVisualizer extends VisualizationProvider {
if (isCommonDataType(data, { graph: true })) {
collector.addVisualization({
id: asVisualizationId("vis-js-graph"),
name: "VisJs Graph",
name: "vis.js",
priority: 1001,
render() {
return (
@@ -34,34 +36,26 @@ export class VisJsGraphVisualizer extends VisualizationProvider {
}
}
interface GraphNode {
id: string;
label: string;
}
interface GraphEdge {
id?: string;
label: string;
from: string;
to: string;
}
@observer
export class VisJsGraphViewer extends React.Component<{
nodes: GraphNode[];
edges: GraphEdge[];
nodes: NodeGraphData[];
edges: EdgeGraphData[];
}> {
private readonly divRef = React.createRef<HTMLDivElement>();
private readonly nodes = new DataSet<{ id: string; label: string }>();
private readonly nodes = new DataSet<{
id: string;
label: string;
color?: string;
}>();
private readonly edges = new DataSet<{
id: string;
label: string;
from: string;
to: string;
color?: string;
}>();
render() {
//const { nodes, edges } = this.props;
return <div style={{ height: "100%" }} ref={this.divRef} />;
}
@@ -69,7 +63,11 @@ export class VisJsGraphViewer extends React.Component<{
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 });
this.nodes.update({
id: n.id,
label: n.label,
color: n.color,
});
}
this.nodes.forEach(item => {
if (!newNodes.has(item.id)) {
@@ -77,11 +75,11 @@ export class VisJsGraphViewer extends React.Component<{
}
});
function getIdOfEdge(e: GraphEdge): string {
function getIdOfEdge(e: EdgeGraphData): string {
if (e.id) {
return e.id;
}
return e.from + "####" + e.to;
return e.from + "####" + e.to + "|" + e.label;
}
const newEdges = new Set<string>();
@@ -93,6 +91,7 @@ export class VisJsGraphViewer extends React.Component<{
label: n.label,
from: n.from,
to: n.to,
color: n.color,
});
}
this.edges.forEach(item => {
@@ -121,6 +120,5 @@ export class VisJsGraphViewer extends React.Component<{
},
};
const network = new Network(this.divRef.current!, data, options);
//network.stabilize();
}
}

View File

@@ -2,7 +2,7 @@ import {
VisualizationProvider,
VisualizationCollector,
asVisualizationId,
} from "./Visualizer";
} from "../Visualizer";
import {
ExtractedData,
isCommonDataType,

View File

@@ -2,7 +2,7 @@ import {
VisualizationProvider,
VisualizationCollector,
asVisualizationId,
} from "./Visualizer";
} from "../Visualizer";
import {
ExtractedData,
isCommonDataType,

View File

@@ -5,7 +5,6 @@ import {
TreeWithPathView,
} from "./Views";
import {
CommonDataTypes,
ExtractedData,
isCommonDataType,
TreeNode,
@@ -40,7 +39,7 @@ export function createTreeViewModelFromTreeNodeData<TData>(
children,
node.data
);
model.isMarked = node.isMarked;
model.isMarked = !!node.isMarked;
for (const c of children) {
c.parent = model;
}

View File

@@ -1,20 +1,20 @@
import "./style.scss";
import { ComposedVisualizationProvider } from "./Visualizer";
import { TreeVisualizer } from "./TreeVisualizer/Visualizer";
import { DotGraphVisualizer } from "./DotGraphVisualizer";
import { GraphvizGraphVisualizer } from "./GraphVisualizer/GraphvizGraphVisualizer";
import { SvgVisualizer } from "./SvgVisualizer";
import { DotVisualizer } from "./DotVisualizer";
import { TextVisualizer } from "./TextVisualizer";
import { MonacoTextVisualizer } from "./MonacoTextVisualizer";
import { GraphvizDotVisualizer } from "./GraphVisualizer/GraphvizDotVisualizer";
import { TextVisualizer } from "./TextVisualizer/TextVisualizer";
import { MonacoTextVisualizer } from "./TextVisualizer/MonacoTextVisualizer";
import { AstVisualizer } from "./AstViewer";
import { VisJsGraphVisualizer } from "./VisJsGraphVisualizer";
import { VisJsGraphVisualizer } from "./GraphVisualizer/VisJsGraphVisualizer";
export const knownVisualizations = new ComposedVisualizationProvider([
new TreeVisualizer(),
new DotGraphVisualizer(),
new GraphvizGraphVisualizer(),
new VisJsGraphVisualizer(),
new SvgVisualizer(),
new DotVisualizer(),
new GraphvizDotVisualizer(),
new TextVisualizer(),
new MonacoTextVisualizer(),
new AstVisualizer(),

View File

@@ -2,12 +2,12 @@ import * as React from "react";
import * as ReactDOM from "react-dom";
import "./style.scss";
import { Model } from "./Model/Model";
import Components = require("./Components");
import Components = require("./Components/GUI");
const model = new Model();
function render(target: HTMLDivElement) {
const c = require("./Components") as typeof Components;
const c = require("./Components/GUI") as typeof Components;
ReactDOM.render(<c.GUI model={model} />, target);
}
@@ -23,7 +23,7 @@ declare var module: {
declare var require: (name: string) => any;
if (module.hot) {
module.hot.accept("./Components", () => {
module.hot.accept("./Components/GUI", () => {
render(target);
});
}