diff --git a/src/breakpoints/body.tsx b/src/breakpoints/body.tsx
index 1d0b94d4..668ae176 100644
--- a/src/breakpoints/body.tsx
+++ b/src/breakpoints/body.tsx
@@ -5,19 +5,19 @@ import { ReactWidget } from '@jupyterlab/apputils';
import React, { useEffect, useState } from 'react';
-import { Breakpoints } from '.';
-
import { IDebugger } from '../tokens';
+import { BreakpointsModel } from './model';
+
/**
* The body for a Breakpoints Panel.
*/
-export class Body extends ReactWidget {
+export class BreakpointsBody extends ReactWidget {
/**
* Instantiate a new Body for the Breakpoints Panel.
* @param model The model for the breakpoints.
*/
- constructor(model: Breakpoints.Model) {
+ constructor(model: BreakpointsModel) {
super();
this._model = model;
this.addClass('jp-DebuggerBreakpoints-body');
@@ -30,27 +30,27 @@ export class Body extends ReactWidget {
return ;
}
- private _model: Breakpoints.Model;
+ private _model: BreakpointsModel;
}
/**
* A React component to display a list of breakpoints.
* @param model The model for the breakpoints.
*/
-const BreakpointsComponent = ({ model }: { model: Breakpoints.Model }) => {
+const BreakpointsComponent = ({ model }: { model: BreakpointsModel }) => {
const [breakpoints, setBreakpoints] = useState(
Array.from(model.breakpoints.entries())
);
useEffect(() => {
const updateBreakpoints = (
- _: Breakpoints.Model,
+ _: BreakpointsModel,
updates: IDebugger.IBreakpoint[]
) => {
setBreakpoints(Array.from(model.breakpoints.entries()));
};
- const restoreBreakpoints = (_: Breakpoints.Model) => {
+ const restoreBreakpoints = (_: BreakpointsModel) => {
setBreakpoints(Array.from(model.breakpoints.entries()));
};
@@ -86,7 +86,7 @@ const BreakpointCellComponent = ({
model
}: {
breakpoints: IDebugger.IBreakpoint[];
- model: Breakpoints.Model;
+ model: BreakpointsModel;
}) => {
return (
<>
@@ -115,7 +115,7 @@ const BreakpointComponent = ({
model
}: {
breakpoint: IDebugger.IBreakpoint;
- model: Breakpoints.Model;
+ model: BreakpointsModel;
}) => {
return (
{
- return this._changed;
- }
-
- /**
- * Signal emitted when the breakpoints are restored.
- */
- get restored(): Signal {
- return this._restored;
- }
-
- /**
- * Signal emitted when a breakpoint is clicked.
- */
- get clicked(): Signal {
- return this._clicked;
- }
-
- /**
- * Get all the breakpoints.
- */
- get breakpoints(): Map {
- return this._breakpoints;
- }
-
- /**
- * Dispose the model.
- */
- dispose(): void {
- if (this._isDisposed) {
- return;
- }
- this._isDisposed = true;
- Signal.clearData(this);
- }
-
- /**
- * Set the breakpoints for a given id (path).
- * @param id The code id (path).
- * @param breakpoints The list of breakpoints.
- */
- setBreakpoints(id: string, breakpoints: IDebugger.IBreakpoint[]) {
- this._breakpoints.set(id, breakpoints);
- this._changed.emit(breakpoints);
- }
-
- /**
- * Get the breakpoints for a given id (path).
- * @param id The code id (path).
- */
- getBreakpoints(id: string): IDebugger.IBreakpoint[] {
- return this._breakpoints.get(id) ?? [];
- }
-
- /**
- * Restore a map of breakpoints.
- * @param breakpoints The map of breakpoints
- */
- restoreBreakpoints(breakpoints: Map) {
- this._breakpoints = breakpoints;
- this._restored.emit();
- }
-
- private _isDisposed = false;
- private _breakpoints = new Map();
- private _changed = new Signal(this);
- private _restored = new Signal(this);
- private _clicked = new Signal(this);
- }
-
/**
* Instantiation options for `Breakpoints`.
*/
export interface IOptions extends Panel.IOptions {
/**
- * The debugger model.
+ * The breakpoints model.
*/
- model: Model;
+ model: BreakpointsModel;
/**
* The debugger service.
diff --git a/src/breakpoints/model.ts b/src/breakpoints/model.ts
new file mode 100644
index 00000000..179ebc7a
--- /dev/null
+++ b/src/breakpoints/model.ts
@@ -0,0 +1,92 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { IDisposable } from '@phosphor/disposable';
+
+import { ISignal, Signal } from '@phosphor/signaling';
+
+import { IDebugger } from '../tokens';
+
+/**
+ * A model for a list of breakpoints.
+ */
+export class BreakpointsModel implements IDisposable {
+ /**
+ * Whether the model is disposed.
+ */
+ get isDisposed(): boolean {
+ return this._isDisposed;
+ }
+
+ /**
+ * Signal emitted when the model changes.
+ */
+ get changed(): ISignal {
+ return this._changed;
+ }
+
+ /**
+ * Signal emitted when the breakpoints are restored.
+ */
+ get restored(): Signal {
+ return this._restored;
+ }
+
+ /**
+ * Signal emitted when a breakpoint is clicked.
+ */
+ get clicked(): Signal {
+ return this._clicked;
+ }
+
+ /**
+ * Get all the breakpoints.
+ */
+ get breakpoints(): Map {
+ return this._breakpoints;
+ }
+
+ /**
+ * Dispose the model.
+ */
+ dispose(): void {
+ if (this._isDisposed) {
+ return;
+ }
+ this._isDisposed = true;
+ Signal.clearData(this);
+ }
+
+ /**
+ * Set the breakpoints for a given id (path).
+ * @param id The code id (path).
+ * @param breakpoints The list of breakpoints.
+ */
+ setBreakpoints(id: string, breakpoints: IDebugger.IBreakpoint[]) {
+ this._breakpoints.set(id, breakpoints);
+ this._changed.emit(breakpoints);
+ }
+
+ /**
+ * Get the breakpoints for a given id (path).
+ * @param id The code id (path).
+ */
+ getBreakpoints(id: string): IDebugger.IBreakpoint[] {
+ return this._breakpoints.get(id) ?? [];
+ }
+
+ /**
+ * Restore a map of breakpoints.
+ * @param breakpoints The map of breakpoints
+ */
+ restoreBreakpoints(breakpoints: Map) {
+ this._breakpoints = breakpoints;
+ this._restored.emit();
+ }
+
+ private _isDisposed = false;
+ private _breakpoints = new Map();
+ private _changed = new Signal(this);
+ private _restored = new Signal(this);
+ private _clicked = new Signal(this);
+}
diff --git a/src/callstack/body.tsx b/src/callstack/body.tsx
index 63347225..0d98b8b7 100644
--- a/src/callstack/body.tsx
+++ b/src/callstack/body.tsx
@@ -1,27 +1,41 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-import React, { useEffect, useState } from 'react';
+import { ReactWidget } from '@jupyterlab/apputils';
-import { Callstack } from '.';
+import React, { useEffect, useState } from 'react';
-import { ReactWidget } from '@jupyterlab/apputils';
+import { CallstackModel } from './model';
-export class Body extends ReactWidget {
- constructor(model: Callstack.Model) {
+/**
+ * The body for a Callstack Panel.
+ */
+export class CallstackBody extends ReactWidget {
+ /**
+ * Instantiate a new Body for the Callstack Panel.
+ * @param model The model for the callstack.
+ */
+ constructor(model: CallstackModel) {
super();
- this.model = model;
+ this._model = model;
this.addClass('jp-DebuggerCallstack-body');
}
+ /**
+ * Render the FramesComponent.
+ */
render() {
- return ;
+ return ;
}
- readonly model: Callstack.Model;
+ private _model: CallstackModel;
}
-const FramesComponent = ({ model }: { model: Callstack.Model }) => {
+/**
+ * A React component to display a list of frames in a callstack.
+ * @param model The model for the callstack.
+ */
+const FramesComponent = ({ model }: { model: CallstackModel }) => {
const [frames, setFrames] = useState(model.frames);
const [selected, setSelected] = useState(model.frame);
diff --git a/src/callstack/header.ts b/src/callstack/header.ts
new file mode 100644
index 00000000..c47da503
--- /dev/null
+++ b/src/callstack/header.ts
@@ -0,0 +1,31 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { Toolbar } from '@jupyterlab/apputils';
+
+import { PanelLayout, Widget } from '@phosphor/widgets';
+
+/**
+ * The header for a Callstack Panel.
+ */
+export class CallstackHeader extends Widget {
+ /**
+ * Instantiate a new CallstackHeader.
+ */
+ constructor() {
+ super({ node: document.createElement('header') });
+
+ const title = new Widget({ node: document.createElement('h2') });
+ title.node.textContent = 'Callstack';
+
+ const layout = new PanelLayout();
+ layout.addWidget(title);
+ layout.addWidget(this.toolbar);
+ this.layout = layout;
+ }
+
+ /**
+ * The toolbar for the callstack header.
+ */
+ readonly toolbar = new Toolbar();
+}
diff --git a/src/callstack/index.ts b/src/callstack/index.ts
index 70ec802b..815ebc49 100644
--- a/src/callstack/index.ts
+++ b/src/callstack/index.ts
@@ -1,28 +1,32 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-import { CommandToolbarButton, Toolbar } from '@jupyterlab/apputils';
+import { CommandToolbarButton } from '@jupyterlab/apputils';
import { CommandRegistry } from '@phosphor/commands';
-import { ISignal, Signal } from '@phosphor/signaling';
-import { Panel, PanelLayout, Widget } from '@phosphor/widgets';
-import { DebugProtocol } from 'vscode-debugprotocol';
-import { Body } from './body';
+import { Panel } from '@phosphor/widgets';
+
+import { CallstackBody } from './body';
+
+import { CallstackHeader } from './header';
+
+import { CallstackModel } from './model';
+
+/**
+ * A Panel to show a callstack.
+ */
export class Callstack extends Panel {
+ /**
+ * Instantiate a new Callstack Panel.
+ * @param options The instantiation options for a Callstack Panel.
+ */
constructor(options: Callstack.IOptions) {
super();
-
const { commands, model } = options;
- this.model = model;
- this.addClass('jp-DebuggerCallstack');
-
const header = new CallstackHeader();
- const body = new Body(this.model);
-
- this.addWidget(header);
- this.addWidget(body);
+ const body = new CallstackBody(model);
header.toolbar.addItem(
'continue',
@@ -63,67 +67,21 @@ export class Callstack extends Panel {
id: commands.stepOut
})
);
- }
- readonly model: Callstack.Model;
-}
-
-class CallstackHeader extends Widget {
- constructor() {
- super({ node: document.createElement('header') });
-
- const layout = new PanelLayout();
- const title = new Widget({ node: document.createElement('h2') });
+ this.addWidget(header);
+ this.addWidget(body);
- this.layout = layout;
- title.node.textContent = 'Callstack';
- layout.addWidget(title);
- layout.addWidget(this.toolbar);
+ this.addClass('jp-DebuggerCallstack');
}
-
- readonly toolbar = new Toolbar();
}
+/**
+ * A namespace for Callstack `statics`.
+ */
export namespace Callstack {
- export interface IFrame extends DebugProtocol.StackFrame {}
-
- export class Model {
- set frames(newFrames: IFrame[]) {
- this._state = newFrames;
- // default to the new frame is the previous one can't be found
- if (!this.frame || !newFrames.find(frame => frame.id === this.frame.id)) {
- this.frame = newFrames[0];
- }
- this._framesChanged.emit(newFrames);
- }
-
- get frames(): IFrame[] {
- return this._state;
- }
-
- set frame(frame: IFrame) {
- this._currentFrame = frame;
- this._currentFrameChanged.emit(frame);
- }
-
- get frame(): IFrame {
- return this._currentFrame;
- }
-
- get framesChanged(): ISignal {
- return this._framesChanged;
- }
-
- get currentFrameChanged(): ISignal {
- return this._currentFrameChanged;
- }
-
- private _state: IFrame[] = [];
- private _currentFrame: IFrame;
- private _framesChanged = new Signal(this);
- private _currentFrameChanged = new Signal(this);
- }
-
+ /**
+ * The toolbar commands and registry for the callstack.
+ */
export interface ICommands {
/**
* The command registry.
@@ -156,12 +114,18 @@ export namespace Callstack {
stepOut: string;
}
+ /**
+ * Instantiation options for `Callstack`.
+ */
export interface IOptions extends Panel.IOptions {
/**
* The toolbar commands interface for the callstack.
*/
commands: ICommands;
- model: Model;
+ /**
+ * The model for the callstack.
+ */
+ model: CallstackModel;
}
}
diff --git a/src/callstack/model.ts b/src/callstack/model.ts
new file mode 100644
index 00000000..9f0830f3
--- /dev/null
+++ b/src/callstack/model.ts
@@ -0,0 +1,74 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { ISignal, Signal } from '@phosphor/signaling';
+
+import { DebugProtocol } from 'vscode-debugprotocol';
+
+/**
+ * A model for a callstack.
+ */
+export class CallstackModel {
+ /**
+ * Get all the frames.
+ */
+ get frames(): CallstackModel.IFrame[] {
+ return this._state;
+ }
+
+ /**
+ * Set the frames.
+ */
+ set frames(newFrames: CallstackModel.IFrame[]) {
+ this._state = newFrames;
+ // default to the new frame is the previous one can't be found
+ if (!this.frame || !newFrames.find(frame => frame.id === this.frame.id)) {
+ this.frame = newFrames[0];
+ }
+ this._framesChanged.emit(newFrames);
+ }
+
+ /**
+ * Get the current frame.
+ */
+ get frame(): CallstackModel.IFrame {
+ return this._currentFrame;
+ }
+
+ /**
+ * Set the current frame.
+ */
+ set frame(frame: CallstackModel.IFrame) {
+ this._currentFrame = frame;
+ this._currentFrameChanged.emit(frame);
+ }
+
+ /**
+ * Signal emitted when the frames have changed.
+ */
+ get framesChanged(): ISignal {
+ return this._framesChanged;
+ }
+
+ /**
+ * Signal emitted when the current frame has changed.
+ */
+ get currentFrameChanged(): ISignal {
+ return this._currentFrameChanged;
+ }
+
+ private _state: CallstackModel.IFrame[] = [];
+ private _currentFrame: CallstackModel.IFrame;
+ private _framesChanged = new Signal(this);
+ private _currentFrameChanged = new Signal(this);
+}
+
+/**
+ * A namespace for CallstackModel `statics`.
+ */
+export namespace CallstackModel {
+ /**
+ * An interface for a frame.
+ */
+ export interface IFrame extends DebugProtocol.StackFrame {}
+}
diff --git a/src/debugger.ts b/src/debugger.ts
index ed407269..e5bba534 100644
--- a/src/debugger.ts
+++ b/src/debugger.ts
@@ -3,17 +3,17 @@
import { IEditorServices } from '@jupyterlab/codeeditor';
-import { ISignal, Signal } from '@phosphor/signaling';
-
import { SplitPanel } from '@phosphor/widgets';
import { Breakpoints } from './breakpoints';
import { Callstack } from './callstack';
-import { Sources } from './sources';
+import { DebuggerModel } from './model';
+
+import { DebuggerService } from './service';
-import { DebugService } from './service';
+import { Sources } from './sources';
import { IDebugger } from './tokens';
@@ -23,7 +23,14 @@ import { Variables } from './variables';
* A namespace for `Debugger` statics.
*/
export namespace Debugger {
+ /**
+ * A debugger sidebar.
+ */
export class Sidebar extends SplitPanel {
+ /**
+ * Instantiate a new Debugger.Sidebar
+ * @param options The instantiation options for a Debugger.Sidebar
+ */
constructor(options: Sidebar.IOptions) {
super();
this.id = 'jp-debugger-sidebar';
@@ -32,8 +39,8 @@ export namespace Debugger {
const { callstackCommands, editorServices, service } = options;
- this.model = new Debugger.Model();
- this.service = service as DebugService;
+ this.model = new DebuggerModel();
+ this.service = service as DebuggerService;
this.service.model = this.model;
this.variables = new Variables({ model: this.model.variables });
@@ -62,8 +69,14 @@ export namespace Debugger {
this.addClass('jp-DebuggerSidebar');
}
+ /**
+ * Whether the sidebar is disposed.
+ */
isDisposed: boolean;
+ /**
+ * Dispose the sidebar.
+ */
dispose(): void {
if (this.isDisposed) {
return;
@@ -72,83 +85,58 @@ export namespace Debugger {
this.service.model = null;
}
- readonly model: Debugger.Model;
- readonly service: DebugService;
- readonly variables: Variables;
- readonly callstack: Callstack;
- readonly breakpoints: Breakpoints;
- readonly sources: Sources;
- }
-
- export class Model implements IDebugger.IModel {
- constructor() {
- this.breakpoints = new Breakpoints.Model();
- this.callstack = new Callstack.Model();
- this.variables = new Variables.Model();
- this.sources = new Sources.Model({
- currentFrameChanged: this.callstack.currentFrameChanged
- });
- }
-
- readonly breakpoints: Breakpoints.Model;
- readonly callstack: Callstack.Model;
- readonly variables: Variables.Model;
- readonly sources: Sources.Model;
-
- dispose(): void {
- if (this._isDisposed) {
- return;
- }
- this._isDisposed = true;
- this._disposed.emit();
- }
-
/**
- * A signal emitted when the debugger widget is disposed.
+ * The debugger model.
*/
- get disposed(): ISignal {
- return this._disposed;
- }
+ readonly model: DebuggerModel;
- get isDisposed(): boolean {
- return this._isDisposed;
- }
+ /**
+ * The debugger service.
+ */
+ readonly service: DebuggerService;
/**
- * The set of threads in stopped state.
+ * The variables widget.
*/
- get stoppedThreads(): Set {
- return this._stoppedThreads;
- }
+ readonly variables: Variables;
/**
- * Assigns the parameters to the set of threads in stopped state.
+ * The callstack widget.
*/
- set stoppedThreads(threads: Set) {
- this._stoppedThreads = threads;
- }
+ readonly callstack: Callstack;
/**
- * Clear the model.
+ * The breakpoints widget.
*/
- clear() {
- this._stoppedThreads.clear();
- const breakpoints = new Map();
- this.breakpoints.restoreBreakpoints(breakpoints);
- this.callstack.frames = [];
- this.variables.scopes = [];
- this.sources.currentSource = null;
- }
+ readonly breakpoints: Breakpoints;
- private _isDisposed = false;
- private _stoppedThreads = new Set();
- private _disposed = new Signal(this);
+ /**
+ * The sources widget.
+ */
+ readonly sources: Sources;
}
+ /**
+ * A namespace for Sidebar `statics`
+ */
export namespace Sidebar {
+ /**
+ * Instantiation options for `Sidebar`.
+ */
export interface IOptions {
+ /**
+ * The debug service.
+ */
service: IDebugger;
+
+ /**
+ * The callstack toolbar commands.
+ */
callstackCommands: Callstack.ICommands;
+
+ /**
+ * The editor services.
+ */
editorServices: IEditorServices;
}
}
diff --git a/src/handlers/editor.ts b/src/handlers/editor.ts
index 1dec34b5..94e394bf 100644
--- a/src/handlers/editor.ts
+++ b/src/handlers/editor.ts
@@ -15,14 +15,20 @@ import { Signal } from '@phosphor/signaling';
import { Editor } from 'codemirror';
-import { Breakpoints } from '../breakpoints';
+import { IDebugger } from '../tokens';
-import { Debugger } from '../debugger';
+import { BreakpointsModel } from '../breakpoints/model';
-import { IDebugger } from '../tokens';
+import { DebuggerModel } from '../model';
+/**
+ * The class name added to the current line.
+ */
const LINE_HIGHLIGHT_CLASS = 'jp-DebuggerEditor-highlight';
+/**
+ * The timeout for listening to editor content changes.
+ */
const EDITOR_CHANGED_TIMEOUT = 1000;
/**
@@ -76,7 +82,7 @@ export class EditorHandler implements IDisposable {
* Handle when the debug model changes.
*/
private _onModelChanged() {
- this._debuggerModel = this._debuggerService.model as Debugger.Model;
+ this._debuggerModel = this._debuggerService.model as DebuggerModel;
if (!this._debuggerModel) {
return;
}
@@ -117,7 +123,7 @@ export class EditorHandler implements IDisposable {
'CodeMirror-linenumbers',
'breakpoints'
]);
- editor.editor.on('gutterClick', this.onGutterClick);
+ editor.editor.on('gutterClick', this._onGutterClick);
}
/**
@@ -132,7 +138,7 @@ export class EditorHandler implements IDisposable {
EditorHandler.clearGutter(editor);
editor.setOption('lineNumbers', false);
editor.editor.setOption('gutters', []);
- editor.editor.off('gutterClick', this.onGutterClick);
+ editor.editor.off('gutterClick', this._onGutterClick);
}
/**
@@ -162,7 +168,7 @@ export class EditorHandler implements IDisposable {
* @param editor The editor from where the click originated.
* @param lineNumber The line corresponding to the click event.
*/
- private onGutterClick = (editor: Editor, lineNumber: number) => {
+ private _onGutterClick = (editor: Editor, lineNumber: number) => {
const info = editor.lineInfo(lineNumber);
if (!info || this._id !== this._debuggerService.session.client.path) {
@@ -237,8 +243,8 @@ export class EditorHandler implements IDisposable {
private _id: string;
private _path: string;
private _editor: CodeEditor.IEditor;
- private _debuggerModel: Debugger.Model;
- private _breakpointsModel: Breakpoints.Model;
+ private _debuggerModel: DebuggerModel;
+ private _breakpointsModel: BreakpointsModel;
private _debuggerService: IDebugger;
private _editorMonitor: ActivityMonitor<
IObservableString,
diff --git a/src/handlers/tracker.ts b/src/handlers/tracker.ts
index 07a258a7..32b0cf62 100644
--- a/src/handlers/tracker.ts
+++ b/src/handlers/tracker.ts
@@ -34,16 +34,18 @@ import { IDisposable } from '@phosphor/disposable';
import { Signal } from '@phosphor/signaling';
-import { Callstack } from '../callstack';
-
import { EditorHandler } from './editor';
-import { Debugger } from '../debugger';
+import { CallstackModel } from '../callstack/model';
+
+import { ReadOnlyEditorFactory } from '../sources/factory';
-import { ReadOnlyEditorFactory, Sources } from '../sources';
+import { SourcesModel } from '../sources/model';
import { IDebugger } from '../tokens';
+import { DebuggerModel } from '../model';
+
/**
* A class which handles notebook, console and editor trackers.
*/
@@ -93,7 +95,7 @@ export class TrackerHandler implements IDisposable {
* Handle when the debug model changes.
*/
private _onModelChanged() {
- this._debuggerModel = this._debuggerService.model as Debugger.Model;
+ this._debuggerModel = this._debuggerService.model as DebuggerModel;
if (!this._debuggerModel) {
return;
}
@@ -123,7 +125,10 @@ export class TrackerHandler implements IDisposable {
* @param _ The sender.
* @param frame The current frame.
*/
- private _onCurrentFrameChanged(_: Callstack.Model, frame: Callstack.IFrame) {
+ private _onCurrentFrameChanged(
+ _: CallstackModel,
+ frame: CallstackModel.IFrame
+ ) {
const debugSessionPath = this._debuggerService.session?.client?.path;
const source = frame?.source.path ?? null;
each(this._find(debugSessionPath, source), editor => {
@@ -138,7 +143,7 @@ export class TrackerHandler implements IDisposable {
* @param _ The sender.
* @param source The source to open.
*/
- private _onCurrentSourceOpened(_: Sources.Model, source: IDebugger.ISource) {
+ private _onCurrentSourceOpened(_: SourcesModel, source: IDebugger.ISource) {
if (!source) {
return;
}
@@ -320,7 +325,7 @@ export class TrackerHandler implements IDisposable {
}
private _debuggerService: IDebugger;
- private _debuggerModel: Debugger.Model;
+ private _debuggerModel: DebuggerModel;
private _shell: JupyterFrontEnd.IShell;
private _readOnlyEditorFactory: ReadOnlyEditorFactory;
private _readOnlyEditorTracker: WidgetTracker<
diff --git a/src/index.ts b/src/index.ts
index 8e598a69..5208aed5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -39,7 +39,9 @@ import { NotebookHandler } from './handlers/notebook';
import { TrackerHandler } from './handlers/tracker';
-import { DebugService } from './service';
+import { DebuggerModel } from './model';
+
+import { DebuggerService } from './service';
import { DebugSession } from './session';
@@ -62,8 +64,6 @@ export namespace CommandIDs {
export const stepIn = 'debugger:stepIn';
export const stepOut = 'debugger:stepOut';
-
- export const debugConsole = 'debugger:debug-console';
}
/**
@@ -225,7 +225,7 @@ class DebuggerHandler<
// clear the model if the handler being removed corresponds
// to the current active debug session
if (debug.session?.client?.path === client.path) {
- const model = debug.model as Debugger.Model;
+ const model = debug.model as DebuggerModel;
model.clear();
}
@@ -442,7 +442,7 @@ const main: JupyterFrontEndPlugin = {
): IDebugger => {
const { commands, shell } = app;
- const service = new DebugService();
+ const service = new DebuggerService();
commands.addCommand(CommandIDs.debugContinue, {
label: 'Continue',
diff --git a/src/model.ts b/src/model.ts
new file mode 100644
index 00000000..aa2a8fde
--- /dev/null
+++ b/src/model.ts
@@ -0,0 +1,106 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { IDebugger } from './tokens';
+
+import { ISignal, Signal } from '@phosphor/signaling';
+
+import { BreakpointsModel } from './breakpoints/model';
+
+import { CallstackModel } from './callstack/model';
+
+import { SourcesModel } from './sources/model';
+
+import { VariablesModel } from './variables/model';
+
+/**
+ * A model for a debugger.
+ */
+export class DebuggerModel implements IDebugger.IModel {
+ /**
+ * Instantiate a new DebuggerModel
+ */
+ constructor() {
+ this.breakpoints = new BreakpointsModel();
+ this.callstack = new CallstackModel();
+ this.variables = new VariablesModel();
+ this.sources = new SourcesModel({
+ currentFrameChanged: this.callstack.currentFrameChanged
+ });
+ }
+
+ /**
+ * The breakpoints model.
+ */
+ readonly breakpoints: BreakpointsModel;
+
+ /**
+ * The callstack model.
+ */
+ readonly callstack: CallstackModel;
+
+ /**
+ * The variables model.
+ */
+ readonly variables: VariablesModel;
+
+ /**
+ * The sources model.
+ */
+ readonly sources: SourcesModel;
+
+ /**
+ * A signal emitted when the debugger widget is disposed.
+ */
+ get disposed(): ISignal {
+ return this._disposed;
+ }
+
+ /**
+ * Whether the model is disposed.
+ */
+ get isDisposed(): boolean {
+ return this._isDisposed;
+ }
+
+ /**
+ * The set of threads in stopped state.
+ */
+ get stoppedThreads(): Set {
+ return this._stoppedThreads;
+ }
+
+ /**
+ * Assigns the parameters to the set of threads in stopped state.
+ */
+ set stoppedThreads(threads: Set) {
+ this._stoppedThreads = threads;
+ }
+
+ /**
+ * Dispose the model.
+ */
+ dispose(): void {
+ if (this._isDisposed) {
+ return;
+ }
+ this._isDisposed = true;
+ this._disposed.emit();
+ }
+
+ /**
+ * Clear the model.
+ */
+ clear() {
+ this._stoppedThreads.clear();
+ const breakpoints = new Map();
+ this.breakpoints.restoreBreakpoints(breakpoints);
+ this.callstack.frames = [];
+ this.variables.scopes = [];
+ this.sources.currentSource = null;
+ }
+
+ private _isDisposed = false;
+ private _stoppedThreads = new Set();
+ private _disposed = new Signal(this);
+}
diff --git a/src/service.ts b/src/service.ts
index d1cc0730..6cce9ad7 100644
--- a/src/service.ts
+++ b/src/service.ts
@@ -13,18 +13,21 @@ import { murmur2 } from 'murmurhash-js';
import { DebugProtocol } from 'vscode-debugprotocol';
-import { Debugger } from './debugger';
+import { CallstackModel } from './callstack/model';
-import { IDebugger } from './tokens';
+import { DebuggerModel } from './model';
-import { Variables } from './variables';
+import { IDebugger } from './tokens';
-import { Callstack } from './callstack';
+import { VariablesModel } from './variables/model';
/**
* A concrete implementation of IDebugger.
*/
-export class DebugService implements IDebugger, IDisposable {
+export class DebuggerService implements IDebugger, IDisposable {
+ /**
+ * Instantiate a new DebuggerService.
+ */
constructor() {
// Avoids setting session with invalid client
// session should be set only when a notebook or
@@ -97,7 +100,7 @@ export class DebugService implements IDebugger, IDisposable {
* @param model - The new debugger model.
*/
set model(model: IDebugger.IModel) {
- this._model = model as Debugger.Model;
+ this._model = model as DebuggerModel;
this._modelChanged.emit(model);
}
@@ -402,7 +405,10 @@ export class DebugService implements IDebugger, IDisposable {
/**
* Handle a change of the current active frame.
*/
- private async _onChangeFrame(_: Callstack.Model, frame: Callstack.IFrame) {
+ private async _onChangeFrame(
+ _: CallstackModel,
+ frame: CallstackModel.IFrame
+ ) {
if (!frame) {
return;
}
@@ -502,7 +508,7 @@ export class DebugService implements IDebugger, IDisposable {
private _convertScopes(
scopes: DebugProtocol.Scope[],
variables: DebugProtocol.Variable[]
- ): Variables.IScope[] {
+ ): VariablesModel.IScope[] {
if (!variables || !scopes) {
return;
}
@@ -573,7 +579,7 @@ export class DebugService implements IDebugger, IDisposable {
private _isDisposed: boolean = false;
private _session: IDebugger.ISession;
- private _model: Debugger.Model;
+ private _model: DebuggerModel;
private _sessionChanged = new Signal(this);
private _modelChanged = new Signal(this);
private _eventMessage = new Signal(this);
diff --git a/src/sources/body.ts b/src/sources/body.ts
new file mode 100644
index 00000000..3dc9f927
--- /dev/null
+++ b/src/sources/body.ts
@@ -0,0 +1,160 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import {
+ CodeEditorWrapper,
+ IEditorMimeTypeService,
+ IEditorServices
+} from '@jupyterlab/codeeditor';
+
+import { Signal } from '@phosphor/signaling';
+
+import { PanelLayout, Widget } from '@phosphor/widgets';
+
+import { CallstackModel } from '../callstack/model';
+
+import { EditorHandler } from '../handlers/editor';
+
+import { IDebugger } from '../tokens';
+
+import { ReadOnlyEditorFactory } from './factory';
+
+import { SourcesModel } from './model';
+
+/**
+ * The body for a Sources Panel.
+ */
+export class SourcesBody extends Widget {
+ /**
+ * Instantiate a new Body for the Sources Panel.
+ * @param model The model for the sources.
+ */
+ constructor(options: SourcesBody.IOptions) {
+ super();
+ this._model = options.model;
+ this._debuggerService = options.service;
+ this._mimeTypeService = options.editorServices.mimeTypeService;
+
+ const factory = new ReadOnlyEditorFactory({
+ editorServices: options.editorServices
+ });
+
+ this._editor = factory.createNewEditor({
+ content: '',
+ mimeType: '',
+ path: ''
+ });
+ this._editor.hide();
+
+ this._model.currentFrameChanged.connect(async (_, frame) => {
+ if (!frame) {
+ this._clearEditor();
+ return;
+ }
+
+ void this._showSource(frame);
+ });
+
+ const layout = new PanelLayout();
+ layout.addWidget(this._editor);
+ this.layout = layout;
+
+ this.addClass('jp-DebuggerSources-body');
+ }
+
+ /**
+ * Dispose the sources body widget.
+ */
+ dispose(): void {
+ if (this.isDisposed) {
+ return;
+ }
+ this._editorHandler.dispose();
+ Signal.clearData(this);
+ }
+
+ /**
+ * Clear the content of the source read-only editor.
+ */
+ private _clearEditor() {
+ this._model.currentSource = null;
+ this._editor.hide();
+ }
+
+ /**
+ * Show the content of the source for the given frame.
+ * @param frame The current frame.
+ */
+ private async _showSource(frame: CallstackModel.IFrame) {
+ const path = frame.source.path;
+ const source = await this._debuggerService.getSource({
+ sourceReference: 0,
+ path
+ });
+
+ if (!source?.content) {
+ this._clearEditor();
+ return;
+ }
+
+ if (this._editorHandler) {
+ this._editorHandler.dispose();
+ }
+
+ const { content, mimeType } = source;
+ const editorMimeType =
+ mimeType || this._mimeTypeService.getMimeTypeByFilePath(path);
+
+ this._editor.model.value.text = content;
+ this._editor.model.mimeType = editorMimeType;
+
+ this._editorHandler = new EditorHandler({
+ debuggerService: this._debuggerService,
+ editor: this._editor.editor,
+ path
+ });
+
+ this._model.currentSource = {
+ content,
+ mimeType: editorMimeType,
+ path
+ };
+
+ requestAnimationFrame(() => {
+ EditorHandler.showCurrentLine(this._editor.editor, frame.line);
+ });
+
+ this._editor.show();
+ }
+
+ private _model: SourcesModel;
+ private _editor: CodeEditorWrapper;
+ private _editorHandler: EditorHandler;
+ private _debuggerService: IDebugger;
+ private _mimeTypeService: IEditorMimeTypeService;
+}
+
+/**
+ * A namespace for SourcesBody `statics`.
+ */
+export namespace SourcesBody {
+ /**
+ * Instantiation options for `Breakpoints`.
+ */
+ export interface IOptions {
+ /**
+ * The debug service.
+ */
+ service: IDebugger;
+
+ /**
+ * The sources model.
+ */
+ model: SourcesModel;
+
+ /**
+ * The editor services used to create new read-only editors.
+ */
+ editorServices: IEditorServices;
+ }
+}
diff --git a/src/sources/factory.ts b/src/sources/factory.ts
new file mode 100644
index 00000000..73151580
--- /dev/null
+++ b/src/sources/factory.ts
@@ -0,0 +1,63 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import {
+ CodeEditor,
+ CodeEditorWrapper,
+ IEditorServices
+} from '@jupyterlab/codeeditor';
+
+import { IDebugger } from '../tokens';
+
+/**
+ * A widget factory for read only editors.
+ */
+export class ReadOnlyEditorFactory {
+ /**
+ * Construct a new editor widget factory.
+ * @param options The instantiation options for a ReadOnlyEditorFactory.
+ */
+ constructor(options: ReadOnlyEditorFactory.IOptions) {
+ this._services = options.editorServices;
+ }
+
+ /**
+ * Create a new CodeEditorWrapper given a Source.
+ * @param source The source to create a new editor for.
+ */
+ createNewEditor(source: IDebugger.ISource): CodeEditorWrapper {
+ const { content, mimeType, path } = source;
+ const factory = this._services.factoryService.newInlineEditor;
+ const mimeTypeService = this._services.mimeTypeService;
+ const editor = new CodeEditorWrapper({
+ model: new CodeEditor.Model({
+ value: content,
+ mimeType: mimeType || mimeTypeService.getMimeTypeByFilePath(path)
+ }),
+ factory,
+ config: {
+ readOnly: true,
+ lineNumbers: true
+ }
+ });
+ editor.node.setAttribute('data-jp-debugger', 'true');
+ return editor;
+ }
+
+ private _services: IEditorServices;
+}
+
+/**
+ * The namespace for `ReadOnlyEditorFactory` class statics.
+ */
+export namespace ReadOnlyEditorFactory {
+ /**
+ * The options used to create a read only editor widget factory.
+ */
+ export interface IOptions {
+ /**
+ * The editor services used by the factory.
+ */
+ editorServices: IEditorServices;
+ }
+}
diff --git a/src/sources/header.ts b/src/sources/header.ts
new file mode 100644
index 00000000..1840948e
--- /dev/null
+++ b/src/sources/header.ts
@@ -0,0 +1,45 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { Toolbar } from '@jupyterlab/apputils';
+
+import { PanelLayout, Widget } from '@phosphor/widgets';
+
+import { SourcesModel } from './model';
+
+/**
+ * The header for a Source Panel.
+ */
+export class SourcesHeader extends Widget {
+ /**
+ * Instantiate a new SourcesHeader.
+ * @param model The model for the Sources.
+ */
+ constructor(model: SourcesModel) {
+ super({ node: document.createElement('header') });
+
+ const layout = new PanelLayout();
+ this.layout = layout;
+
+ const title = new Widget({ node: document.createElement('h2') });
+ title.node.textContent = 'Source';
+
+ const sourcePath = new Widget({ node: document.createElement('span') });
+ model.currentSourceChanged.connect((_, source) => {
+ const path = source?.path ?? '';
+ sourcePath.node.textContent = path;
+ sourcePath.node.title = path;
+ });
+
+ layout.addWidget(title);
+ layout.addWidget(this.toolbar);
+ layout.addWidget(sourcePath);
+
+ this.addClass('jp-DebuggerSources-header');
+ }
+
+ /**
+ * The toolbar for the sources header.
+ */
+ readonly toolbar = new Toolbar();
+}
diff --git a/src/sources/index.ts b/src/sources/index.ts
index 7b9f13c2..a8983b75 100644
--- a/src/sources/index.ts
+++ b/src/sources/index.ts
@@ -3,24 +3,19 @@
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
-import { Toolbar, ToolbarButton } from '@jupyterlab/apputils';
+import { ToolbarButton } from '@jupyterlab/apputils';
-import {
- CodeEditor,
- CodeEditorWrapper,
- IEditorMimeTypeService,
- IEditorServices
-} from '@jupyterlab/codeeditor';
+import { IEditorServices } from '@jupyterlab/codeeditor';
-import { ISignal, Signal } from '@phosphor/signaling';
+import { Panel } from '@phosphor/widgets';
-import { Panel, PanelLayout, Widget } from '@phosphor/widgets';
+import { IDebugger } from '../tokens';
-import { Callstack } from '../callstack';
+import { SourcesBody } from './body';
-import { EditorHandler } from '../handlers/editor';
+import { SourcesHeader } from './header';
-import { IDebugger } from '../tokens';
+import { SourcesModel } from './model';
/**
* A Panel that shows a preview of the source code while debugging.
@@ -32,154 +27,25 @@ export class Sources extends Panel {
*/
constructor(options: Sources.IOptions) {
super();
- this.model = options.model;
- this._debuggerService = options.service;
- this._mimeTypeService = options.editorServices.mimeTypeService;
+ const { model, service, editorServices } = options;
- const header = new SourcesHeader(this.model);
+ const header = new SourcesHeader(model);
+ const body = new SourcesBody({
+ service,
+ model,
+ editorServices
+ });
header.toolbar.addItem(
'open',
new ToolbarButton({
iconClassName: 'jp-ViewBreakpointIcon',
- onClick: () => this.model.open(),
+ onClick: () => model.open(),
tooltip: 'Open in the Main Area'
})
);
-
- const factory = new ReadOnlyEditorFactory({
- editorServices: options.editorServices
- });
-
- this._editor = factory.createNewEditor({
- content: '',
- mimeType: '',
- path: ''
- });
- this._editor.hide();
-
- this.model.currentFrameChanged.connect(async (_, frame) => {
- if (!frame) {
- this._clearEditor();
- return;
- }
-
- void this._showSource(frame);
- });
-
this.addWidget(header);
- this.addWidget(this._editor);
- }
-
- /**
- * The debugger sources model.
- */
- readonly model: Sources.Model;
-
- /**
- * Dispose the debug sources.
- */
- dispose(): void {
- if (this.isDisposed) {
- return;
- }
- this._editorHandler.dispose();
- Signal.clearData(this);
- }
-
- /**
- * Clear the content of the source read-only editor.
- */
- private _clearEditor() {
- this.model.currentSource = null;
- this._editor.hide();
+ this.addWidget(body);
}
-
- /**
- * Show the content of the source for the given frame.
- * @param frame The current frame.
- */
- private async _showSource(frame: Callstack.IFrame) {
- const path = frame.source.path;
- const source = await this._debuggerService.getSource({
- sourceReference: 0,
- path
- });
-
- if (!source?.content) {
- this._clearEditor();
- return;
- }
-
- if (this._editorHandler) {
- this._editorHandler.dispose();
- }
-
- const { content, mimeType } = source;
- const editorMimeType =
- mimeType || this._mimeTypeService.getMimeTypeByFilePath(path);
-
- this._editor.model.value.text = content;
- this._editor.model.mimeType = editorMimeType;
-
- this._editorHandler = new EditorHandler({
- debuggerService: this._debuggerService,
- editor: this._editor.editor,
- path
- });
-
- this.model.currentSource = {
- content,
- mimeType: editorMimeType,
- path
- };
-
- requestAnimationFrame(() => {
- EditorHandler.showCurrentLine(this._editor.editor, frame.line);
- });
-
- this._editor.show();
- }
-
- private _debuggerService: IDebugger;
- private _mimeTypeService: IEditorMimeTypeService;
- private _editor: CodeEditorWrapper;
- private _editorHandler: EditorHandler;
-}
-
-/**
- * The header for a Source Panel.
- */
-class SourcesHeader extends Widget {
- /**
- * Instantiate a new SourcesHeader.
- * @param model The model for the Sources.
- */
- constructor(model: Sources.Model) {
- super({ node: document.createElement('header') });
- this.addClass('jp-DebuggerSources');
-
- const layout = new PanelLayout();
- this.layout = layout;
-
- const title = new Widget({ node: document.createElement('h2') });
- title.node.textContent = 'Source';
-
- const sourcePath = new Widget({ node: document.createElement('span') });
- model.currentSourceChanged.connect((_, source) => {
- const path = source?.path ?? '';
- sourcePath.node.textContent = path;
- sourcePath.node.title = path;
- });
-
- layout.addWidget(title);
- layout.addWidget(this.toolbar);
- layout.addWidget(sourcePath);
- }
-
- /**
- * The toolbar for the sources header.
- */
- readonly toolbar = new Toolbar();
}
/**
@@ -198,143 +64,11 @@ export namespace Sources {
/**
* The model for the sources.
*/
- model: Sources.Model;
+ model: SourcesModel;
/**
* The editor services used to create new read-only editors.
*/
editorServices: IEditorServices;
}
-
- /**
- * The model to keep track of the current source being displayed.
- */
- export class Model {
- /**
- * Instantiate a new Sources.Model
- * @param options The Sources.Model instantiation options.
- */
- constructor(options: Sources.Model.IOptions) {
- this.currentFrameChanged = options.currentFrameChanged;
- }
-
- /**
- * Signal emitted when the current frame changes.
- */
- currentFrameChanged: ISignal;
-
- /**
- * Signal emitted when a source should be open in the main area.
- */
- get currentSourceOpened(): ISignal {
- return this._currentSourceOpened;
- }
-
- /**
- * Signal emitted when the current source changes.
- */
- get currentSourceChanged(): ISignal {
- return this._currentSourceChanged;
- }
-
- /**
- * Return the current source.
- */
- get currentSource() {
- return this._currentSource;
- }
-
- /**
- * Set the current source.
- * @param source The source to set as the current source.
- */
- set currentSource(source: IDebugger.ISource | null) {
- this._currentSource = source;
- this._currentSourceChanged.emit(source);
- }
-
- /**
- * Open a source in the main area.
- */
- open() {
- this._currentSourceOpened.emit(this._currentSource);
- }
-
- private _currentSource: IDebugger.ISource | null;
- private _currentSourceOpened = new Signal(
- this
- );
- private _currentSourceChanged = new Signal<
- Sources.Model,
- IDebugger.ISource
- >(this);
- }
-
- /**
- * A namespace for the Model `statics`.
- */
- export namespace Model {
- /**
- * The options used to initialize a Sources.Model object.
- */
- export interface IOptions {
- /**
- * Signal emitted when the current frame changes.
- */
- currentFrameChanged: ISignal;
- }
- }
-}
-
-/**
- * A widget factory for read only editors.
- */
-export class ReadOnlyEditorFactory {
- /**
- * Construct a new editor widget factory.
- * @param options The instantiation options for a ReadOnlyEditorFactory.
- */
- constructor(options: ReadOnlyEditorFactory.IOptions) {
- this._services = options.editorServices;
- }
-
- /**
- * Create a new CodeEditorWrapper given a Source.
- * @param source The source to create a new editor for.
- */
- createNewEditor(source: IDebugger.ISource): CodeEditorWrapper {
- const { content, mimeType, path } = source;
- const factory = this._services.factoryService.newInlineEditor;
- const mimeTypeService = this._services.mimeTypeService;
- const editor = new CodeEditorWrapper({
- model: new CodeEditor.Model({
- value: content,
- mimeType: mimeType || mimeTypeService.getMimeTypeByFilePath(path)
- }),
- factory,
- config: {
- readOnly: true,
- lineNumbers: true
- }
- });
- editor.node.setAttribute('data-jp-debugger', 'true');
- return editor;
- }
-
- private _services: IEditorServices;
-}
-
-/**
- * The namespace for `ReadOnlyEditorFactory` class statics.
- */
-export namespace ReadOnlyEditorFactory {
- /**
- * The options used to create a read only editor widget factory.
- */
- export interface IOptions {
- /**
- * The editor services used by the factory.
- */
- editorServices: IEditorServices;
- }
}
diff --git a/src/sources/model.ts b/src/sources/model.ts
new file mode 100644
index 00000000..f26130e0
--- /dev/null
+++ b/src/sources/model.ts
@@ -0,0 +1,86 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { ISignal, Signal } from '@phosphor/signaling';
+
+import { CallstackModel } from '../callstack/model';
+
+import { IDebugger } from '../tokens';
+
+/**
+ * The model to keep track of the current source being displayed.
+ */
+export class SourcesModel {
+ /**
+ * Instantiate a new Sources.Model
+ * @param options The Sources.Model instantiation options.
+ */
+ constructor(options: SourcesModel.IOptions) {
+ this.currentFrameChanged = options.currentFrameChanged;
+ }
+
+ /**
+ * Signal emitted when the current frame changes.
+ */
+ currentFrameChanged: ISignal;
+
+ /**
+ * Signal emitted when a source should be open in the main area.
+ */
+ get currentSourceOpened(): ISignal {
+ return this._currentSourceOpened;
+ }
+
+ /**
+ * Signal emitted when the current source changes.
+ */
+ get currentSourceChanged(): ISignal {
+ return this._currentSourceChanged;
+ }
+
+ /**
+ * Return the current source.
+ */
+ get currentSource() {
+ return this._currentSource;
+ }
+
+ /**
+ * Set the current source.
+ * @param source The source to set as the current source.
+ */
+ set currentSource(source: IDebugger.ISource | null) {
+ this._currentSource = source;
+ this._currentSourceChanged.emit(source);
+ }
+
+ /**
+ * Open a source in the main area.
+ */
+ open() {
+ this._currentSourceOpened.emit(this._currentSource);
+ }
+
+ private _currentSource: IDebugger.ISource | null;
+ private _currentSourceOpened = new Signal(
+ this
+ );
+ private _currentSourceChanged = new Signal(
+ this
+ );
+}
+
+/**
+ * A namespace for SourcesModel `statics`.
+ */
+export namespace SourcesModel {
+ /**
+ * The options used to initialize a SourcesModel object.
+ */
+ export interface IOptions {
+ /**
+ * Signal emitted when the current frame changes.
+ */
+ currentFrameChanged: ISignal;
+ }
+}
diff --git a/src/variables/body/index.tsx b/src/variables/body.tsx
similarity index 83%
rename from src/variables/body/index.tsx
rename to src/variables/body.tsx
index 6ed77350..06ff0745 100644
--- a/src/variables/body/index.tsx
+++ b/src/variables/body.tsx
@@ -1,36 +1,50 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-import { Variables } from '../index';
-
-import { ITheme, ObjectInspector, ObjectRootLabel } from 'react-inspector';
-
import { ReactWidget } from '@jupyterlab/apputils';
import { ArrayExt } from '@phosphor/algorithm';
+import { ITheme, ObjectInspector, ObjectRootLabel } from 'react-inspector';
+
import React, { useEffect, useState } from 'react';
-export class Body extends ReactWidget {
- constructor(model: Variables.Model) {
+import { VariablesModel } from './model';
+
+/**
+ * The body for a Variables Panel.
+ */
+export class VariablesBody extends ReactWidget {
+ /**
+ * Instantiate a new Body for the Variables Panel.
+ * @param model The model for the variables.
+ */
+ constructor(model: VariablesModel) {
super();
- this.model = model;
+ this._model = model;
this.addClass('jp-DebuggerVariables-body');
}
- model: Variables.Model;
-
+ /**
+ * Render the VariablesComponent.
+ */
render() {
- return ;
+ return ;
}
+
+ private _model: VariablesModel;
}
-const VariableComponent = ({ model }: { model: Variables.Model }) => {
+/**
+ * A React component to display a list of variables.
+ * @param model The model for the variables.
+ */
+const VariablesComponent = ({ model }: { model: VariablesModel }) => {
const [data, setData] = useState(model.scopes);
// TODO: this should be simplified and extracted from the component
const filterVariable = (
- variable: Variables.IVariable,
+ variable: VariablesModel.IVariable,
isObject?: boolean,
keyObj?: string
): Object => {
@@ -70,7 +84,7 @@ const VariableComponent = ({ model }: { model: Variables.Model }) => {
return filteredObj;
};
- const convertForObjectInspector = (scopes: Variables.IScope[]) => {
+ const convertForObjectInspector = (scopes: VariablesModel.IScope[]) => {
return scopes.map(scope => {
const newVariable = scope.variables.map(variable => {
if (variable.expanded || variable.variablesReference === 0) {
@@ -87,7 +101,7 @@ const VariableComponent = ({ model }: { model: Variables.Model }) => {
};
useEffect(() => {
- const updateScopes = (self: Variables.Model) => {
+ const updateScopes = (self: VariablesModel) => {
if (ArrayExt.shallowEqual(data, self.scopes)) {
return;
}
@@ -119,7 +133,11 @@ const VariableComponent = ({ model }: { model: Variables.Model }) => {
return <>{List()}>;
};
-const convertType = (variable: Variables.IVariable) => {
+/**
+ * Convert a variable to a primitive type.
+ * @param variable The variable.
+ */
+const convertType = (variable: VariablesModel.IVariable) => {
const { type, value } = variable;
switch (type) {
case 'int':
@@ -135,6 +153,9 @@ const convertType = (variable: Variables.IVariable) => {
}
};
+/**
+ * Default renderer for the variable tree view.
+ */
const defaultNodeRenderer = ({
depth,
name,
@@ -178,6 +199,9 @@ const defaultNodeRenderer = ({
);
};
+/**
+ * Default theme for the variable tree view.
+ */
const THEME = {
BASE_FONT_FAMILY: 'var(--jp-code-font-family)',
BASE_FONT_SIZE: 'var(--jp-code-font-size)',
diff --git a/src/variables/header.ts b/src/variables/header.ts
new file mode 100644
index 00000000..d7c4199b
--- /dev/null
+++ b/src/variables/header.ts
@@ -0,0 +1,23 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { PanelLayout, Widget } from '@phosphor/widgets';
+
+/**
+ * The header for a Variables Panel.
+ */
+export class VariablesHeader extends Widget {
+ /**
+ * Instantiate a new VariablesHeader.
+ */
+ constructor() {
+ super({ node: document.createElement('header') });
+
+ const title = new Widget({ node: document.createElement('h2') });
+ title.node.textContent = 'Variables';
+
+ const layout = new PanelLayout();
+ this.layout = layout;
+ layout.addWidget(title);
+ }
+}
diff --git a/src/variables/index.ts b/src/variables/index.ts
index aca76a9c..0f4d13d7 100644
--- a/src/variables/index.ts
+++ b/src/variables/index.ts
@@ -1,93 +1,65 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-import { Panel, PanelLayout, Widget } from '@phosphor/widgets';
+import { Panel, Widget } from '@phosphor/widgets';
-import { Body } from './body';
+import { VariablesBody } from './body';
-import { ISignal, Signal } from '@phosphor/signaling';
+import { VariablesHeader } from './header';
-import { DebugProtocol } from 'vscode-debugprotocol';
+import { VariablesModel } from './model';
+/**
+ * A Panel to show a variable explorer.
+ */
export class Variables extends Panel {
+ /**
+ * Instantiate a new Variables Panel.
+ * @param options The instantiation options for a Variables Panel.
+ */
constructor(options: Variables.IOptions) {
super();
+ this._header = new VariablesHeader();
+ this._body = new VariablesBody(options.model);
- this.model = options.model;
- this.addClass('jp-DebuggerVariables');
-
- this.header = new VariablesHeader();
- this.body = new Body(this.model);
+ this.addWidget(this._header);
+ this.addWidget(this._body);
- this.addWidget(this.header);
- this.addWidget(this.body);
+ this.addClass('jp-DebuggerVariables');
}
- readonly model: Variables.Model;
- readonly header: VariablesHeader;
- readonly body: Widget;
+ private _header: VariablesHeader;
+ private _body: Widget;
+ /**
+ * A message handler invoked on a `'resize'` message.
+ */
protected onResize(msg: Widget.ResizeMessage): void {
super.onResize(msg);
- this.resizeBody(msg);
+ this._resizeBody(msg);
}
- private resizeBody(msg: Widget.ResizeMessage) {
- const height = msg.height - this.header.node.offsetHeight;
- this.body.node.style.height = `${height}px`;
- }
-}
-
-class VariablesHeader extends Widget {
- constructor() {
- super({ node: document.createElement('header') });
- const layout = new PanelLayout();
- const title = new Widget({ node: document.createElement('h2') });
-
- this.layout = layout;
- title.node.textContent = 'Variables';
- layout.addWidget(title);
+ /**
+ * Resize the body.
+ * @param msg The resize message.
+ */
+ private _resizeBody(msg: Widget.ResizeMessage) {
+ const height = msg.height - this._header.node.offsetHeight;
+ this._body.node.style.height = `${height}px`;
}
}
+/**
+ * A namespace for Variables `statics`.
+ */
export namespace Variables {
- export interface IVariable extends DebugProtocol.Variable {
- expanded?: boolean;
- }
-
- export interface IScope {
- name: string;
- variables: IVariable[];
- }
-
- export class Model {
- get changed(): ISignal {
- return this._changed;
- }
-
- get scopes(): IScope[] {
- return this._state;
- }
-
- set scopes(scopes: IScope[]) {
- this._state = scopes;
- this._changed.emit();
- }
-
- get variableExpanded(): ISignal {
- return this._variableExpanded;
- }
-
- expandVariable(variable: IVariable) {
- this._variableExpanded.emit(variable);
- }
-
- protected _state: IScope[] = [];
- private _variableExpanded = new Signal(this);
- private _changed = new Signal(this);
- }
-
+ /**
+ * Instantiation options for `Variables`.
+ */
export interface IOptions extends Panel.IOptions {
- model: Model;
+ /**
+ * The variables model.
+ */
+ model: VariablesModel;
}
}
diff --git a/src/variables/model.ts b/src/variables/model.ts
new file mode 100644
index 00000000..4d1f044f
--- /dev/null
+++ b/src/variables/model.ts
@@ -0,0 +1,82 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { ISignal, Signal } from '@phosphor/signaling';
+
+import { DebugProtocol } from 'vscode-debugprotocol';
+
+/**
+ * A model for a variable explorer.
+ */
+export class VariablesModel {
+ /**
+ * Get all the scopes.
+ */
+ get scopes(): VariablesModel.IScope[] {
+ return this._state;
+ }
+
+ /**
+ * Set the scopes.
+ */
+ set scopes(scopes: VariablesModel.IScope[]) {
+ this._state = scopes;
+ this._changed.emit();
+ }
+
+ /**
+ * Signal emitted when the current variable has changed.
+ */
+ get changed(): ISignal {
+ return this._changed;
+ }
+
+ /**
+ * Signal emitted when the current variable has been expanded.
+ */
+ get variableExpanded(): ISignal {
+ return this._variableExpanded;
+ }
+
+ /**
+ * Expand a variable.
+ * @param variable The variable to expand.
+ */
+ expandVariable(variable: VariablesModel.IVariable) {
+ this._variableExpanded.emit(variable);
+ }
+
+ private _state: VariablesModel.IScope[] = [];
+ private _variableExpanded = new Signal(this);
+ private _changed = new Signal(this);
+}
+
+/**
+ * A namespace for VariablesModel `statics`.
+ */
+export namespace VariablesModel {
+ /**
+ * An interface for a variable.
+ */
+ export interface IVariable extends DebugProtocol.Variable {
+ /**
+ * Whether the variable is expanded.
+ */
+ expanded?: boolean;
+ }
+
+ /**
+ * An interface for a scope.
+ */
+ export interface IScope {
+ /**
+ * The name of the scope.
+ */
+ name: string;
+
+ /**
+ * The list of variables.
+ */
+ variables: IVariable[];
+ }
+}
diff --git a/src/variables/body/typings.d.ts b/src/variables/typings.d.ts
similarity index 56%
rename from src/variables/body/typings.d.ts
rename to src/variables/typings.d.ts
index 1e1bc27d..1ee45a07 100644
--- a/src/variables/body/typings.d.ts
+++ b/src/variables/typings.d.ts
@@ -1,3 +1,3 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-///
+///
diff --git a/style/sources.css b/style/sources.css
index 5ed06b09..48db624b 100644
--- a/style/sources.css
+++ b/style/sources.css
@@ -1,13 +1,17 @@
-[data-jp-debugger='true'].jp-Editor .jp-mod-readOnly {
+.jp-DebuggerSources-body [data-jp-debugger='true'].jp-Editor .jp-mod-readOnly {
background: var(--jp-layout-color2);
height: 100%;
}
-[data-jp-debugger='true'].jp-Editor {
+.jp-DebuggerSources-body [data-jp-debugger='true'].jp-Editor {
height: 100%;
}
-.jp-DebuggerSources > span {
+.jp-DebuggerSources-body {
+ height: 100%;
+}
+
+.jp-DebuggerSources-header > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
diff --git a/tests/src/debugger.spec.ts b/tests/src/debugger.spec.ts
index 181a3bf2..09bd2cb8 100644
--- a/tests/src/debugger.spec.ts
+++ b/tests/src/debugger.spec.ts
@@ -9,12 +9,12 @@ import { CommandRegistry } from '@phosphor/commands';
import { Debugger } from '../../lib/debugger';
-import { DebugService } from '../../lib/service';
+import { DebuggerService } from '../../lib/service';
class TestSidebar extends Debugger.Sidebar {}
describe('Debugger', () => {
- const service = new DebugService();
+ const service = new DebuggerService();
const registry = new CommandRegistry();
const factoryService = new CodeMirrorEditorFactory();
const mimeTypeService = new CodeMirrorMimeTypeService();
diff --git a/tests/src/service.spec.ts b/tests/src/service.spec.ts
index 0ef38542..a40e0fc6 100644
--- a/tests/src/service.spec.ts
+++ b/tests/src/service.spec.ts
@@ -6,16 +6,16 @@ import { createClientSession } from '@jupyterlab/testutils';
import { PromiseDelegate } from '@phosphor/coreutils';
-import { Debugger } from '../../lib/debugger';
+import { DebuggerModel } from '../../lib/model';
-import { DebugService } from '../../lib/service';
+import { DebuggerService } from '../../lib/service';
import { DebugSession } from '../../lib/session';
import { IDebugger } from '../../lib/tokens';
describe('Debugging support', () => {
- const service = new DebugService();
+ const service = new DebuggerService();
let xpythonClient: IClientSession;
let ipykernelClient: IClientSession;
@@ -57,9 +57,9 @@ describe('Debugging support', () => {
});
});
-describe('DebugService', () => {
+describe('DebuggerService', () => {
let client: IClientSession;
- let model: Debugger.Model;
+ let model: DebuggerModel;
let session: IDebugger.ISession;
let service: IDebugger;
@@ -72,19 +72,19 @@ describe('DebugService', () => {
await (client as ClientSession).initialize();
await client.kernel.ready;
session = new DebugSession({ client });
- model = new Debugger.Model();
- service = new DebugService();
+ model = new DebuggerModel();
+ service = new DebuggerService();
});
afterEach(async () => {
await client.shutdown();
session.dispose();
- (service as DebugService).dispose();
+ (service as DebuggerService).dispose();
});
describe('#constructor()', () => {
it('should create a new instance', () => {
- expect(service).to.be.an.instanceOf(DebugService);
+ expect(service).to.be.an.instanceOf(DebuggerService);
});
});
@@ -115,7 +115,7 @@ describe('DebugService', () => {
describe('#session', () => {
it('should emit the sessionChanged signal when setting the session', () => {
- let sessionChangedEvents: IDebugger.ISession[] = [];
+ const sessionChangedEvents: IDebugger.ISession[] = [];
service.sessionChanged.connect((_, newSession) => {
sessionChangedEvents.push(newSession);
});
@@ -127,9 +127,9 @@ describe('DebugService', () => {
describe('#model', () => {
it('should emit the modelChanged signal when setting the model', () => {
- let modelChangedEvents: Debugger.Model[] = [];
+ const modelChangedEvents: DebuggerModel[] = [];
service.modelChanged.connect((_, newModel) => {
- modelChangedEvents.push(newModel as Debugger.Model);
+ modelChangedEvents.push(newModel as DebuggerModel);
});
service.model = model;
expect(modelChangedEvents.length).to.equal(1);