From e7c8ba8b08b688a2017a1171d97acecc8a954ebc Mon Sep 17 00:00:00 2001 From: Philipp von Radziewsky Date: Sat, 27 Aug 2016 06:36:28 +0200 Subject: [PATCH 1/4] Update interface --- interface/debugger.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/debugger.js b/interface/debugger.js index a17d0cc..13d43ab 100644 --- a/interface/debugger.js +++ b/interface/debugger.js @@ -98,6 +98,8 @@ declare module 'debugger' { onTargetEvent(callback: ((event: TargetEvent) => void)): Disposable; + onFrameChange(callback: (() => void)): Disposable; + findBreakpoint(location: BreakpointLocation): ?Breakpoint; removeBreakpoint(breakpoint: Breakpoint): boolean; From b1357824691b6f475189c6428e73815e44f2e493 Mon Sep 17 00:00:00 2001 From: Philipp von Radziewsky Date: Sat, 27 Aug 2016 06:37:40 +0200 Subject: [PATCH 2/4] Implement scope view --- lib/debugger-scope-view.js | 149 ++++++++++++++++++++- lib/debugger-view.js | 4 +- styles/debugger-ui-default.scope-view.less | 41 ++++++ 3 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 styles/debugger-ui-default.scope-view.less diff --git a/lib/debugger-scope-view.js b/lib/debugger-scope-view.js index ec18c39..99a827d 100644 --- a/lib/debugger-scope-view.js +++ b/lib/debugger-scope-view.js @@ -1,18 +1,157 @@ 'use babel' /* @flow */ + import DebuggerPanelSection from './debugger-panel-section' +import type { + DebuggerController, + DebuggerProxy, + SessionEvent, + Variable +} from 'debugger' + +class DebuggerScopeViewPrivate { + element: DebuggerPanelSection; + variableListElement: HTMLElement; + + debuggerProxy: DebuggerProxy; + + createCallbacks(): void { + + const proxy = this.debuggerProxy + + proxy.onSessionEvent( (event: SessionEvent) => { + + if (event.type !== 'suspended') { return } + + const list = this.variableListElement + + while (list.firstChild) { + list.removeChild(list.firstChild); + } + + proxy.getVariableList().then( (result: Array) => { + for (let i=0; i { + const list = this.variableListElement + + while (list.firstChild) { + list.removeChild(list.firstChild); + } + + proxy.getVariableList().then( (result: Array) => { + for (let i=0; i { + + if (event.type !== 'resumed') { return } + + const list = this.variableListElement + + while (list.firstChild) { + list.removeChild(list.firstChild); + } + }) + + proxy.onSessionEvent( (event: SessionEvent) => { + + if (event.type !== 'terminated') { return } + + const list = this.variableListElement + + while (list.firstChild) { + list.removeChild(list.firstChild); + } + }) + } + + createScopeView(): void { + + const list = document.createElement('ul') + + list.classList.add('list-group') + + this.element.appendChild(list) + this.variableListElement = list + } + + createVariableView(variable: Variable): void { + + const variableView = document.createElement('li') + this.variableListElement.appendChild(variableView) + + variableView.classList.add('variable') + + const nameDiv = document.createElement('div') + const nameElement = document.createElement('span') + variableView.appendChild(nameDiv) + nameDiv.appendChild(nameElement) + + nameDiv.classList.add('name') + nameElement.classList.add('name', 'highlight') + nameElement.appendChild(new Text(variable.name)) + + const typeElement = document.createElement('span') + variableView.appendChild(typeElement) + + typeElement.classList.add('type') + typeElement.appendChild(new Text(variable.type)) + + const spacerElement = document.createElement('span') + variableView.appendChild(spacerElement) + + spacerElement.classList.add('spacer') + + if (variable.value !== undefined) { + const valueElement = document.createElement('span') + variableView.appendChild(valueElement) + + valueElement.classList.add('value') + + if (variable.value !== null) { + valueElement.appendChild(new Text(variable.value)) + } else { + valueElement.appendChild(new Text('unknown')) + valueElement.classList.add('unknown') + } + } + } + + activate(controller: DebuggerController): void { + + this.debuggerProxy = controller.debuggerRegistry.getDebuggerProxy() + + this.createCallbacks() + this.createScopeView() + } +} + export default class DebuggerScopeView { element: DebuggerPanelSection; constructor() { - this.element = new DebuggerPanelSection - this.element.innerHTML = 'DebuggerScopeView' - } + const p = new DebuggerScopeViewPrivate + + p.element = new DebuggerPanelSection - getElement(): DebuggerPanelSection { - return this.element + p.element.classList.add('scope-view') + + this.getElement = () => { return p.element } + this.activate = (controller) => { p.activate(controller) } } + + activate: (controller: DebuggerController) => void; + + getElement: () => DebuggerPanelSection; } diff --git a/lib/debugger-view.js b/lib/debugger-view.js index a7aa22e..2f81eec 100644 --- a/lib/debugger-view.js +++ b/lib/debugger-view.js @@ -61,9 +61,9 @@ export default class DebuggerView { if (!event.executionLine || typeof event.executionLine !== 'object') { throw Error('SessionEvent.executionLine must be an object') } else if (typeof event.executionLine.filePath !== 'string') { - throw Error('SessionEvent.executionLine.filePath must be a string') + return } else if (typeof event.executionLine.bufferRow !== 'number') { - throw Error('SessionEvent.executionLine.bufferRow must be a string') + return } atom.workspace.open(event.executionLine.filePath).then((editor) => { diff --git a/styles/debugger-ui-default.scope-view.less b/styles/debugger-ui-default.scope-view.less new file mode 100644 index 0000000..41c3945 --- /dev/null +++ b/styles/debugger-ui-default.scope-view.less @@ -0,0 +1,41 @@ +@import 'ui-variables'; + +debugger-panel-section.scope-view { + @panel-padding: @component-padding / 2; + + display: flex; + padding: @panel-padding; + + ul.list-group { + flex-grow: 1; + } + + li.variable { + align-items: baseline; + display: flex; + } + + .spacer { + flex-grow: 1; + } + + .type { + font-family: monospace; + margin-left: 0.5em; + margin-right: 0.5em; + font-size: 0.9em; + + &::before { + content: '('; + } + + &::after { + content: ')'; + } + } + + .value.unknown { + font-style: italic; + color: @text-color-subtle; + } +} From 8a0f12939cc45925a42417bff4a7519fd6556ada Mon Sep 17 00:00:00 2001 From: Philipp von Radziewsky Date: Sun, 28 Aug 2016 19:41:50 +0200 Subject: [PATCH 3/4] Update interface to debugger --- interface/debugger.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/interface/debugger.js b/interface/debugger.js index 13d43ab..e76951e 100644 --- a/interface/debugger.js +++ b/interface/debugger.js @@ -54,9 +54,11 @@ declare module 'debugger' { } declare type Variable = { + id: string, name: string, value: string, - type: ?string + type: ?string, + has_children: boolean } declare class SessionEvent { @@ -79,10 +81,20 @@ declare module 'debugger' { constructor(type: TargetEventType, message: string): void; } + declare type VariableEventType = 'updated' | 'left-scope' | 'entered-scope' + + declare class VariableEvent { + type: VariableEventType; + variable: Variable; + + constructor(type: VariableEventType, variable: Variable): void; + } + declare interface EventDefs { BreakpointEvent: BreakpointEvent; SessionEvent: SessionEvent; TargetEvent: TargetEvent; + VariableEvent: VariableEvent; } declare interface DebuggerTarget { @@ -98,7 +110,7 @@ declare module 'debugger' { onTargetEvent(callback: ((event: TargetEvent) => void)): Disposable; - onFrameChange(callback: (() => void)): Disposable; + onVariableEvent(callback: ((event: VariableEvent) => void)): Disposable; findBreakpoint(location: BreakpointLocation): ?Breakpoint; @@ -110,7 +122,7 @@ declare module 'debugger' { setSelectedFrame(level: number): void; - getVariableList(): Promise>; + getVariableChildren(variable: Variable): Promise>; } declare interface DebuggerRegistry { From 51f479bef1646b4514eb3efa56940ce1d88071ea Mon Sep 17 00:00:00 2001 From: Philipp von Radziewsky Date: Sun, 28 Aug 2016 19:43:06 +0200 Subject: [PATCH 4/4] Implement display of composite variables and simplify code --- lib/debugger-scope-view.js | 109 ++++++++++++--------- styles/debugger-ui-default.scope-view.less | 4 +- 2 files changed, 64 insertions(+), 49 deletions(-) diff --git a/lib/debugger-scope-view.js b/lib/debugger-scope-view.js index 99a827d..85cb6d0 100644 --- a/lib/debugger-scope-view.js +++ b/lib/debugger-scope-view.js @@ -8,9 +8,15 @@ import type { DebuggerController, DebuggerProxy, SessionEvent, - Variable + Variable, + VariableEvent } from 'debugger' +const errorHandler = (error: mixed) => { + const message = (error && error.msg) ? error.msg : 'Unknown error occured' + atom.notifications.addError(message, { dismissable: true }) +} + class DebuggerScopeViewPrivate { element: DebuggerPanelSection; variableListElement: HTMLElement; @@ -21,57 +27,24 @@ class DebuggerScopeViewPrivate { const proxy = this.debuggerProxy - proxy.onSessionEvent( (event: SessionEvent) => { - - if (event.type !== 'suspended') { return } + proxy.onVariableEvent( (event: VariableEvent) => { - const list = this.variableListElement - - while (list.firstChild) { - list.removeChild(list.firstChild); - } + if (event.type !== 'entered-scope') { return } - proxy.getVariableList().then( (result: Array) => { - for (let i=0; i { - const list = this.variableListElement + proxy.onVariableEvent( (event: VariableEvent) => { - while (list.firstChild) { - list.removeChild(list.firstChild); - } + if (event.type !== 'left-scope') { return } - proxy.getVariableList().then( (result: Array) => { - for (let i=0; i { + if (!element) { throw new Error('Could not find corresponding element') } - if (event.type !== 'resumed') { return } + const parent = element.parentElement - const list = this.variableListElement - - while (list.firstChild) { - list.removeChild(list.firstChild); - } - }) - - proxy.onSessionEvent( (event: SessionEvent) => { - - if (event.type !== 'terminated') { return } - - const list = this.variableListElement - - while (list.firstChild) { - list.removeChild(list.firstChild); - } + if (parent) { parent.removeChild(element) } }) } @@ -79,16 +52,58 @@ class DebuggerScopeViewPrivate { const list = document.createElement('ul') - list.classList.add('list-group') + list.classList.add('list-tree', 'has-collapsable-children') this.element.appendChild(list) this.variableListElement = list } - createVariableView(variable: Variable): void { + createVariableView(variable: Variable, parent: HTMLElement): void { + + let variableView = document.createElement('li') + parent.appendChild(variableView) + + variableView.dataset.id = variable.id + + if (variable.has_children) { + variableView.classList.add('list-nested-item', 'collapsed') + let variableHeader = document.createElement('div') + variableView.appendChild(variableHeader) + + variableHeader.classList.add('list-item') + + const view = variableView + + variableHeader.addEventListener('click', event => { - const variableView = document.createElement('li') - this.variableListElement.appendChild(variableView) + if (event.shiftKey || event.metaKey || event.ctrlKey) { return } + + if (view.classList.contains('collapsed')) { + view.classList.remove('collapsed') + + if (!view.querySelector('.list-tree')) { + const proxy = this.debuggerProxy + const childView = document.createElement('ul') + childView.classList.add('list-tree') + + view.appendChild(childView) + + proxy.getVariableChildren(variable) + .then( (children: Array) => { + for (let i=0; i