14 Commits

Author SHA1 Message Date
Henning Dieterichs
80c5d34c81 v1.1.0 2020-05-13 18:43:20 +02:00
Henning Dieterichs
787fc73b33 Configures port mapping for webviews so that remote development is supported.
Enabled retainContextWhenHidden to get faster performance.
2020-05-13 18:43:06 +02:00
Henning Dieterichs
33b076cbd3 Removes default context. 2020-05-13 18:22:26 +02:00
Henning Dieterichs
d73b3797cb Treats pwa-chrome as js debug adapter. 2020-05-13 18:22:26 +02:00
Henning Dieterichs
04ea412322 Adds keywords and bugs property. 2020-05-13 18:21:27 +02:00
Henning Dieterichs
b65ba5fdbe Improves error messages and uses repl mode by default. 2020-05-13 18:20:54 +02:00
Henning Dieterichs
410e5d3048 Adds prettier as local dependency. 2020-05-13 00:11:34 +02:00
Henning Dieterichs
e9803a0e98 Makes server readonly. 2020-05-13 00:11:08 +02:00
Henning Dieterichs
0a65f6425e Fixes another Debug Session tracking bug. 2020-05-13 00:10:58 +02:00
Henning Dieterichs
c9625d4939 Fixes a bug that it does not track a debug session if the debug visualizer is initialized after the debugging has started. 2020-05-12 23:08:39 +02:00
Henning Dieterichs
59f94eda45 Merge pull request #51 from Yardanico/addnim
Add Nim demo
2020-05-11 10:22:55 +02:00
Danil Yarantsev
507e4fc695 Add license 2020-05-11 11:11:34 +03:00
Danil Yarantsev
52599482f3 Add Nim demo 2020-05-11 11:04:39 +03:00
Henning Dieterichs
415f061697 Updates extension readme. Adds comparison to the watch window. 2020-04-11 16:23:51 +02:00
16 changed files with 227 additions and 32 deletions

18
demos/nim/.vscode/launch.json vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
# For Nim source code lines
debugger:native

View File

@@ -1,5 +1,15 @@
# Change Log
## 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
- "Debug Visualizer: Use Selection as Expression" will open a new view if no Debug Visualizer view was opened.

View File

@@ -3,6 +3,7 @@
[![](https://img.shields.io/twitter/follow/hediet_dev.svg?style=social)](https://twitter.com/intent/follow?screen_name=hediet_dev)
A VS Code extension for visualizing data structures while debugging.
Like the VS Code's watch view, but with custom visualizations.
![](../docs/demo.gif)
@@ -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.
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_.
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.
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.
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
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.
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.

View File

@@ -4,12 +4,24 @@
"displayName": "Debug Visualizer",
"description": "An extension that visualizes values while debugging.",
"icon": "docs/logo.png",
"version": "1.0.0",
"version": "1.1.0",
"license": "GPL-3.0",
"engines": {
"vscode": "^1.35.0"
},
"publisher": "hediet",
"keywords": [
"debugger",
"debugging",
"debug",
"visual",
"javascript",
"graph",
"ast"
],
"bugs": {
"url": "https://github.com/hediet/vscode-debug-visualizer/issues"
},
"author": {
"email": "henning.dieterichs@live.de",
"name": "Henning Dieterichs"

View File

@@ -51,6 +51,7 @@ export class GenericEvaluator implements Evaluator {
message: {
kind: "list",
items: [
"An error occurred while evaluating the expression:",
error.message,
`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: {
@@ -76,6 +79,7 @@ export class GenericEvaluator implements Evaluator {
}
protected getContext(): "watch" | "repl" {
return "watch";
// we will use "repl" as default so that results are not truncated.
return "repl";
}
}

View File

@@ -24,6 +24,7 @@ export class JsEvaluationEngine implements EvaluationEngine {
"node2",
"extensionHost",
"chrome",
"pwa-chrome",
];
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
return new JsEvaluator(session);
@@ -74,6 +75,7 @@ class JsEvaluator implements Evaluator {
const reply = await this.session.evaluate({
expression: wrappedExpr,
frameId,
context: "repl",
});
const resultStr = reply.result;
const jsonData = resultStr.substr(1, resultStr.length - 2);
@@ -115,6 +117,7 @@ class JsEvaluator implements Evaluator {
await this.session.evaluate({
expression,
frameId,
context: "repl",
});
return true;

View File

@@ -4,8 +4,13 @@ import {
} from "@hediet/debug-visualizer-data-extraction";
import { FormattedMessage } from "../../contract";
export interface ParseEvaluationResultContext {
debugAdapterType: string;
}
export function parseEvaluationResultFromGenericDebugAdapter(
resultText: string
resultText: string,
context: ParseEvaluationResultContext
):
| { kind: "data"; result: DataExtractionResult }
| { kind: "error"; message: FormattedMessage } {
@@ -26,12 +31,12 @@ export function parseEvaluationResultFromGenericDebugAdapter(
// Just in case no quoting is done.
jsonData2 = jsonData;
}
resultObj = parseJson(jsonData2);
resultObj = parseJson(jsonData2, context);
} catch (e) {
// in case of C++: `"{ \"kind\": { ... }, \"text\": \"some\\ntext\" }"`
const str = parseJson(jsonData);
const str = parseJson(jsonData, context);
// str is now `{ "kind": { ... }, "text": "some\ntext" }"`
resultObj = parseJson(str);
resultObj = parseJson(str, context);
// result is now { kind: { ... }, text: "some\ntext" }
}
@@ -85,7 +90,7 @@ function isEnclosedWith(str: string, char: string): boolean {
return str.startsWith(char) && str.endsWith(char);
}
function parseJson(str: string) {
function parseJson(str: string, context: ParseEvaluationResultContext) {
try {
return JSON.parse(str);
} catch (error) {
@@ -104,6 +109,7 @@ function parseJson(str: string) {
},
],
},
`Used debug adapter: ${context.debugAdapterType}`,
],
});
}

View File

@@ -11,7 +11,7 @@ import { distPath } from "debug-visualizer-webview";
import { EvaluationWatchService } from "./EvaluationWatchService/EvaluationWatchService";
export class Server {
private server: http.Server;
private readonly server: http.Server;
public readonly secret = cryptoRandomString({ length: 20 });
public readonly connections = new Set<ClientConnection>();

View File

@@ -2,7 +2,7 @@ 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";
import { observable, runInAction, action } from "mobx";
export class VsCodeDebugger {
public readonly dispose = Disposable.fn();
@@ -14,7 +14,12 @@ export class VsCodeDebugger {
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
public getDebugSession(session: DebugSession): VsCodeDebugSession {
return this.sessions.get(session)!;
let result = this.sessions.get(session);
if (!result) {
result = new VsCodeDebugSession(session);
this.sessions.set(session, result);
}
return result;
}
constructor() {
@@ -24,15 +29,11 @@ export class VsCodeDebugger {
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);
const extendedSession = this.getDebugSession(session);
return {
onWillReceiveMessage: msg => {
console.log(msg.type, msg.event, msg);
@@ -159,15 +160,20 @@ export class VsCodeDebugSession {
}
}
/**
* 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";
context: "watch" | "repl";
}): Promise<{ result: string }> {
const reply = await this.session.customRequest("evaluate", {
expression: args.expression,
frameId: args.frameId,
context: args.context || "watch",
context: args.context,
});
return { result: reply.result };
}
@@ -193,17 +199,19 @@ export class VsCodeDebuggerView {
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;
}
});
this.updateActiveDebugSession(activeSession);
})
);
this.updateActiveDebugSession(debug.activeDebugSession);
}
@action
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
if (!activeSession) {
this._activeDebugSession = undefined;
} else {
const s = this.vsCodeDebugger.getDebugSession(activeSession);
this._activeDebugSession = s;
}
}
}

View File

@@ -32,7 +32,16 @@ export class WebViews {
debugVisualizer,
"Debug Visualizer",
ViewColumn.Two,
{ enableScripts: true }
{
enableScripts: true,
retainContextWhenHidden: true,
portMapping: [
{
webviewPort: this.server.port,
extensionHostPort: this.server.port,
},
],
}
);
this.setupView(panel, expression);

View File

@@ -13,5 +13,8 @@
"build-extension": "yarn workspace debug-visualizer build",
"build-visualization": "yarn workspace @hediet/visualization build",
"build-ui": "yarn workspace debug-visualizer-webview build"
},
"devDependencies": {
"prettier": "^1.19.1"
}
}

View File

@@ -7690,6 +7690,11 @@ prepend-http@^1.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
prettier@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
pretty-error@^2.0.2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"