From 99a0f854a3282c6407cf49d0f2a3694748ba4ad9 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 1 Aug 2019 20:18:36 +0200 Subject: [PATCH] Progress. --- extension/src/ConnectionHandler.ts | 5 + extension/src/DataSource/JsDebuggerSource.ts | 12 +- extension/src/WebViews.ts | 44 +++++- extension/ui/src/Components/index.tsx | 128 ++++++++++++------ extension/ui/src/Model.ts | 81 ++++++++++- .../TreeVisualizer/SampleTreeVisualizer.tsx | 1 + .../src/Visualizers/TreeVisualizer/Views.tsx | 9 +- extension/ui/src/style.scss | 12 ++ shared/src/contract.ts | 10 +- 9 files changed, 249 insertions(+), 53 deletions(-) diff --git a/extension/src/ConnectionHandler.ts b/extension/src/ConnectionHandler.ts index ba3c03c..d8e3ff1 100644 --- a/extension/src/ConnectionHandler.ts +++ b/extension/src/ConnectionHandler.ts @@ -37,6 +37,11 @@ export class ConnectionHandler { openInBrowser: async ({}) => { open(server.indexUrl); }, + setPreferredDataExtractor: async ({ dataExtractorId }) => { + if (this.watcher) { + this.watcher.setPreferredDataExtractor(dataExtractorId); + } + }, } ); diff --git a/extension/src/DataSource/JsDebuggerSource.ts b/extension/src/DataSource/JsDebuggerSource.ts index 60027fb..6eabc7e 100644 --- a/extension/src/DataSource/JsDebuggerSource.ts +++ b/extension/src/DataSource/JsDebuggerSource.ts @@ -66,10 +66,16 @@ class JsDebuggerSourceImplementation implements JsDataSource { try { w._state = { kind: "loading" }; - const expression = `(${selfContainedGetInitializedDataExtractorApi.toString()})().getData(${ - w.expression - }, expr => eval(expr), undefined)`; + const fnSrc = selfContainedGetInitializedDataExtractorApi.toString(); + const preferredExtractor = w.preferredDataExtractor + ? `"${w.preferredDataExtractor}"` + : "undefined"; + + const expression = `(${fnSrc})().getData(${ + w.expression + }, expr => eval(expr), ${preferredExtractor})`; + // uia const reply = await session.customRequest("evaluate", { expression, frameId: this.lastFrameId, diff --git a/extension/src/WebViews.ts b/extension/src/WebViews.ts index 3151bec..3df0193 100644 --- a/extension/src/WebViews.ts +++ b/extension/src/WebViews.ts @@ -1,12 +1,31 @@ import { window, ViewColumn, WebviewPanel } from "vscode"; import { Server } from "./Server"; +import { Disposable } from "@hediet/std/disposable"; export const debugVisualizer = "debugVisualizer"; export class WebViews { private readonly debugVisualizations = new Map(); - constructor(private readonly server: Server) {} + public readonly dispose = Disposable.fn(); + + constructor(private readonly server: Server) { + this.dispose.track( + window.registerWebviewPanelSerializer(debugVisualizer, { + deserializeWebviewPanel: async (panel, state) => { + this.restore(panel); + }, + }) + ); + + this.dispose.track({ + dispose: () => { + for (const panel of this.debugVisualizations.keys()) { + panel.dispose(); + } + }, + }); + } public createNew() { const panel = window.createWebviewPanel( @@ -31,12 +50,6 @@ export class WebViews { this.debugVisualizations.delete(webviewPanel); }); } - - public dispose() { - for (const panel of this.debugVisualizations.keys()) { - panel.dispose(); - } - } } export class WebView { @@ -48,6 +61,7 @@ export class WebView { export function getHtml(server: Server) { const isDev = true; + // uiae return ` @@ -70,6 +84,22 @@ export function getHtml(server: Server) { server.mainBundle }">` } + `; diff --git a/extension/ui/src/Components/index.tsx b/extension/ui/src/Components/index.tsx index 670fd49..6bcd7bc 100644 --- a/extension/ui/src/Components/index.tsx +++ b/extension/ui/src/Components/index.tsx @@ -2,13 +2,16 @@ import React = require("react"); import { Model } from "../Model"; import { observer } from "mobx-react"; import { observable, action, computed } from "mobx"; -import { Popover, Icon, Button } from "@blueprintjs/core"; +import { Popover, Icon, Button, Spinner, InputGroup } from "@blueprintjs/core"; import classnames = require("classnames"); import Measure from "react-measure"; import { DotGraphViewer } from "./GraphViz"; import { TreeView } from "../Visualizers/TreeVisualizer/Views"; import { createTreeViewModelFromTreeNodeData } from "../Visualizers/TreeVisualizer/Visualizers"; -import { CommonDataTypes } from "@hediet/debug-visualizer-data-extraction"; +import { + CommonDataTypes, + DataExtractorInfo, +} from "@hediet/debug-visualizer-data-extraction"; @observer export class GUI extends React.Component<{ model: Model }> { @observable text: string = "node"; @@ -27,6 +30,7 @@ export class GUI extends React.Component<{ model: Model }> {
+
- {this.expanded && ( - <> -
-
- -
- - -
- - -
- - )} + {this.expanded && }
@@ -89,6 +68,54 @@ export class GUI extends React.Component<{ model: Model }> { } } +@observer +export class ExpandedOptions extends React.Component<{ model: Model }> { + render() { + const m = this.props.model; + let availableExtractors = new Array(); + let selected = -1; + if (m.state.kind === "data") { + const result = m.state.result; + availableExtractors = result.availableExtractors; + selected = result.availableExtractors.findIndex( + i => i.id === result.usedExtractor.id + ); + } + return ( + <> +
+
+ +
+ + ({ + label: e.name, + id: e.id, + }))} + onSelected={item => { + m.setPreferredExtractorId(item.id); + }} + /> +
+ + +
+ + ); + } +} + @observer export class ExpressionInput extends React.Component<{ model: Model }> { @observable editedExpression: string | undefined; @@ -113,11 +140,23 @@ export class ExpressionInput extends React.Component<{ model: Model }> { } render() { + const model = this.props.model; return ( - + ) : ( + undefined + ) + } value={this.currentExpression} - onChange={e => this.updateCurrentExpression(e.target.value)} + onChange={ + ((e: { target: { value: string } }) => + this.updateCurrentExpression(e.target.value)) as any + } onBlur={this.submit} onKeyPress={e => { if (e.charCode === 13) { @@ -127,6 +166,12 @@ export class ExpressionInput extends React.Component<{ model: Model }> { }} /> ); + /* + + */ } } @@ -137,7 +182,7 @@ export class Visualizer extends React.Component<{ model: Model }> {
{this.renderContent()}
); /* - + (props: { name: string; selected: number; - options: SelectableItem[]; + onSelected: (item: T) => void; + options: T[]; }) { return (
@@ -265,11 +311,12 @@ interface SelectableItem { } @observer -export class Select extends React.Component<{ +export class Select extends React.Component<{ selected: number; - options: SelectableItem[]; + onSelected: (item: T) => void; + options: T[]; }> { - @computed get selected(): SelectableItem { + @computed get selected(): T { return this.props.options[this.props.selected]; } @@ -286,7 +333,12 @@ export class Select extends React.Component<{
{this.props.options.map((o, idx) => ( -
{o.label}
+
this.props.onSelected(o)} + > + {o.label} +
))}
diff --git a/extension/ui/src/Model.ts b/extension/ui/src/Model.ts index fd8a245..033ec1e 100644 --- a/extension/ui/src/Model.ts +++ b/extension/ui/src/Model.ts @@ -5,11 +5,60 @@ import { import { WebSocketStream } from "@hediet/typed-json-rpc-websocket"; import { ConsoleRpcLogger } from "@hediet/typed-json-rpc"; import { observable, action } from "mobx"; +import { Barrier } from "@hediet/std/synchronization"; +import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction"; declare const window: Window & { serverPort?: number; }; +interface VSCodeApi { + getState(): Promise; + setState(state: TState): Promise; +} + +type IncomingCommand = { + command: "getStateResult"; + state: unknown; +}; + +type OutgoingCommand = + | { + command: "setState"; + state: unknown; + } + | { command: "getState" }; + +class IFrameVSCodeApi implements VSCodeApi { + private currentGetStatePromise: Barrier | undefined = undefined; + + constructor() { + window.addEventListener("message", event => { + const data = event.data as IncomingCommand; + if (data.command === "getStateResult") { + this.currentGetStatePromise!.unlock(data.state as any); + } + }); + } + + private sendCommand(command: OutgoingCommand) { + window.parent.postMessage(command, "*"); + } + + getState(): Promise { + this.sendCommand({ command: "getState" }); + if (this.currentGetStatePromise) { + throw new Error("Get state already in progress."); + } + this.currentGetStatePromise = new Barrier(); + return this.currentGetStatePromise.onUnlocked; + } + + async setState(state: TState): Promise { + this.sendCommand({ command: "setState", state }); + } +} + export class Model { port: number; @@ -22,6 +71,14 @@ export class Model { | typeof debugVisualizerUIContract.TServerInterface | undefined = undefined; + private readonly vsCodeApi = new IFrameVSCodeApi<{ expression: string }>(); + + @observable private _loading = false; + + public get loading(): boolean { + return this._loading; + } + constructor() { if (window.serverPort) { this.port = window.serverPort; @@ -35,6 +92,12 @@ export class Model { } this.stayConnected(); + + this.vsCodeApi.getState().then(state => { + if (state) { + this.setExpression(state.expression); + } + }); } @action @@ -43,6 +106,19 @@ export class Model { if (this.server) { this.server.setExpression({ newExpression }); } + + this.vsCodeApi.setState({ + expression: newExpression, + }); + } + + setPreferredExtractorId(id: DataExtractorId) { + console.log(id); + if (this.server) { + this.server.setPreferredDataExtractor({ + dataExtractorId: id, + }); + } } refresh() { @@ -65,7 +141,10 @@ export class Model { new ConsoleRpcLogger(), { updateState: ({ newState }) => { - this.state = newState; + this._loading = newState.kind === "loading"; + if (!this._loading) { + this.state = newState; + } }, } ); diff --git a/extension/ui/src/Visualizers/TreeVisualizer/SampleTreeVisualizer.tsx b/extension/ui/src/Visualizers/TreeVisualizer/SampleTreeVisualizer.tsx index 7eb92b1..6dc23d1 100644 --- a/extension/ui/src/Visualizers/TreeVisualizer/SampleTreeVisualizer.tsx +++ b/extension/ui/src/Visualizers/TreeVisualizer/SampleTreeVisualizer.tsx @@ -25,6 +25,7 @@ export function SampleTreeVisualizer() { viewModel, root.parentPropertyName, root.name, + undefined, children ); for (const c of children) { diff --git a/extension/ui/src/Visualizers/TreeVisualizer/Views.tsx b/extension/ui/src/Visualizers/TreeVisualizer/Views.tsx index 1378128..a30bf40 100644 --- a/extension/ui/src/Visualizers/TreeVisualizer/Views.tsx +++ b/extension/ui/src/Visualizers/TreeVisualizer/Views.tsx @@ -36,8 +36,9 @@ export class TreeViewModel { if (this.lastUpdateVersion === this.version) { return; } - this.lastUpdateVersion = this.version; + this.lastUpdateVersion = this.version; + console.log("update"); this.root.updateOffsets(); } @@ -81,6 +82,8 @@ export class TreeViewModel { } } +let i = 0; + export class TreeNodeViewModel { public parent: TreeNodeViewModel | null = null; @@ -191,8 +194,8 @@ export class TreeWithPathView extends React.Component<{ @observer export class TreeView extends React.Component<{ model: TreeViewModel }> { - componentWillUpdate() { - this.props.model.stop(); + componentWillUpdate(newProps: this["props"]) { + newProps.model.stop(); } componentWillMount() { diff --git a/extension/ui/src/style.scss b/extension/ui/src/style.scss index efcde13..23c934a 100644 --- a/extension/ui/src/style.scss +++ b/extension/ui/src/style.scss @@ -14,6 +14,18 @@ body { padding: 0; } +.bp3-input, +.bp3-button { + border-radius: 0; +} + +.bp3-icon { + svg { + color: lighten(black, 20); + width: 14px; + } +} + html, body, .target { diff --git a/shared/src/contract.ts b/shared/src/contract.ts index 71cfd08..c015873 100644 --- a/shared/src/contract.ts +++ b/shared/src/contract.ts @@ -1,5 +1,8 @@ import { contract, notificationContract, types } from "@hediet/typed-json-rpc"; -import { DataExtractionResult } from "@hediet/debug-visualizer-data-extraction"; +import { + DataExtractionResult, + DataExtractorId, +} from "@hediet/debug-visualizer-data-extraction"; function unchecked(): types.Type { return new types.Type( @@ -28,6 +31,11 @@ export const debugVisualizerUIContract = contract({ }), }, server: { + setPreferredDataExtractor: notificationContract({ + params: types.type({ + dataExtractorId: unchecked(), + }), + }), setExpression: notificationContract({ params: types.type({ newExpression: types.string,