Browse Source

Debuger service is now always available

Johan Mabille 5 years ago
parent
commit
154be2e56b
8 changed files with 84 additions and 118 deletions
  1. 7 4
      src/debugger.ts
  2. 1 1
      src/handlers/cell.ts
  3. 2 2
      src/handlers/console.ts
  4. 1 2
      src/handlers/notebook.ts
  5. 38 78
      src/index.ts
  6. 16 21
      src/service.ts
  7. 12 9
      src/tokens.ts
  8. 7 1
      tests/src/debugger.spec.ts

+ 7 - 4
src/debugger.ts

@@ -40,10 +40,12 @@ export class Debugger extends SplitPanel {
     });
 
     this.sidebar = new Debugger.Sidebar(this.model);
-    this.service = new DebugService(this.model);
+    this.service = options.debugService;
+    this.service.model = this.model;
 
-    const { editorFactory } = options;
-    this.editors = new DebuggerEditors({ editorFactory });
+    this.editors = new DebuggerEditors({
+      editorFactory: options.editorFactory
+    });
     this.addWidget(this.editors);
 
     this.addClass('jp-Debugger');
@@ -58,8 +60,8 @@ export class Debugger extends SplitPanel {
     if (this.isDisposed) {
       return;
     }
+    this.service.model = null;
     this.model.dispose();
-    this.service.dispose();
     super.dispose();
   }
 
@@ -74,6 +76,7 @@ export class Debugger extends SplitPanel {
  */
 export namespace Debugger {
   export interface IOptions {
+    debugService: DebugService;
     editorFactory: CodeEditor.Factory;
     connector?: IDataConnector<ReadonlyJSONValue>;
   }

+ 1 - 1
src/handlers/cell.ts

@@ -213,7 +213,7 @@ export class CellManager implements IDisposable {
   private _type: SessionTypes;
   private breakpointsModel: Breakpoints.Model;
   private _activeCell: CodeCell;
-  private _debuggerService: IDebugger.IService;
+  private _debuggerService: IDebugger;
 }
 
 export namespace CellManager {

+ 2 - 2
src/handlers/console.ts

@@ -17,7 +17,8 @@ import { Signal } from '@phosphor/signaling';
 
 export class DebuggerConsoleHandler implements IDisposable {
   constructor(options: DebuggerConsoleHandler.IOptions) {
-    this.debuggerModel = options.debuggerModel;
+    this.debuggerModel = options.debuggerService.model;
+    this.debuggerService = options.debuggerService;
     this.consoleTracker = options.tracker;
     this.breakpoints = this.debuggerModel.breakpointsModel;
     this.cellManager = new CellManager({
@@ -67,7 +68,6 @@ export class DebuggerConsoleHandler implements IDisposable {
 
 export namespace DebuggerConsoleHandler {
   export interface IOptions {
-    debuggerModel: Debugger.Model;
     debuggerService: IDebugger;
     tracker: IConsoleTracker;
   }

+ 1 - 2
src/handlers/notebook.ts

@@ -19,7 +19,7 @@ import { Signal } from '@phosphor/signaling';
 
 export class DebuggerNotebookHandler implements IDisposable {
   constructor(options: DebuggerNotebookHandler.IOptions) {
-    this.debuggerModel = options.debuggerModel;
+    this.debuggerModel = options.debuggerService.model;
     this.debuggerService = options.debuggerService;
     this.notebookTracker = options.tracker;
     this.breakpoints = this.debuggerModel.breakpointsModel;
@@ -67,7 +67,6 @@ export class DebuggerNotebookHandler implements IDisposable {
 
 export namespace DebuggerNotebookHandler {
   export interface IOptions {
-    debuggerModel: Debugger.Model;
     debuggerService: IDebugger;
     tracker: INotebookTracker;
   }

+ 38 - 78
src/index.ts

@@ -8,33 +8,38 @@ import {
   ILabShell
 } from '@jupyterlab/application';
 
-import { IClientSession, ICommandPalette } from '@jupyterlab/apputils';
+import {
+  IClientSession,
+  ICommandPalette,
+  MainAreaWidget,
+  WidgetTracker
+} from '@jupyterlab/apputils';
 
-import { WidgetTracker, MainAreaWidget } from '@jupyterlab/apputils';
+import { IEditorServices } from '@jupyterlab/codeeditor';
 
 import { IConsoleTracker, ConsolePanel } from '@jupyterlab/console';
 
-import { IDebugger } from './tokens';
-
 import { IStateDB } from '@jupyterlab/coreutils';
 
 import { IEditorTracker, FileEditor } from '@jupyterlab/fileeditor';
 
 import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
 
+import { Kernel } from '@jupyterlab/services';
+
 import { UUID } from '@phosphor/coreutils';
 
 import { Debugger } from './debugger';
 
-import { DebugSession } from './session';
-
 import { DebuggerNotebookHandler } from './handlers/notebook';
 
 import { DebuggerConsoleHandler } from './handlers/console';
 
-import { Kernel } from '@jupyterlab/services';
+import { DebugService } from './service';
 
-import { IEditorServices } from '@jupyterlab/codeeditor';
+import { DebugSession } from './session';
+
+import { IDebugger } from './tokens';
 
 /**
  * The command IDs used by the debugger plugin.
@@ -73,10 +78,8 @@ async function setDebugSession(
   } else {
     debug.session.client = client;
   }
-  if (debug.session) {
-    await debug.session.restoreState();
-    app.commands.notifyCommandChanged();
-  }
+  await debug.session.restoreState();
+  app.commands.notifyCommandChanged();
 }
 
 class HandlerTracker<
@@ -90,11 +93,10 @@ class HandlerTracker<
     T extends IConsoleTracker | INotebookTracker,
     W extends ConsolePanel | NotebookPanel
   >(debug: IDebugger, tracker: T, widget: W): void {
-    if (debug.tracker.currentWidget && !this.handlers[widget.id]) {
+    if (debug.model && !this.handlers[widget.id]) {
       const handler = new this.builder({
         tracker: tracker,
-        debuggerModel: debug.tracker.currentWidget.content.model,
-        debuggerService: debug.tracker.currentWidget.content.service
+        debuggerService: debug
       });
       this.handlers[widget.id] = handler;
       widget.disposed.connect(() => {
@@ -229,25 +231,21 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
     restorer: ILayoutRestorer | null,
     palette: ICommandPalette | null
   ): IDebugger => {
+    const { commands, shell } = app;
+    const editorFactory = editorServices.factoryService.newInlineEditor;
+
+    const service = new DebugService();
+
     const tracker = new WidgetTracker<MainAreaWidget<Debugger>>({
       namespace: 'debugger'
     });
 
-    const { commands, shell } = app;
-    const editorFactory = editorServices.factoryService.newInlineEditor;
-
     let widget: MainAreaWidget<Debugger>;
 
     const getModel = () => {
       return tracker.currentWidget ? tracker.currentWidget.content.model : null;
     };
 
-    const getService = () => {
-      return tracker.currentWidget
-        ? tracker.currentWidget.content.service
-        : null;
-    };
-
     commands.addCommand(CommandIDs.mount, {
       execute: args => {
         if (!widget) {
@@ -269,6 +267,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
             sidebar.parent = null;
           }
 
+          // edge case when reload page after set condensed mode
           widget.title.label = 'Debugger';
           shell.add(widget, 'main');
           return;
@@ -288,11 +287,10 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
     commands.addCommand(CommandIDs.stop, {
       label: 'Stop',
       isEnabled: () => {
-        const service = getService();
-        return service && service.isStarted();
+        return service.isStarted();
       },
       execute: async () => {
-        await getService().session.stop();
+        await service.session.stop();
         commands.notifyCommandChanged();
       }
     });
@@ -300,11 +298,10 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
     commands.addCommand(CommandIDs.start, {
       label: 'Start',
       isEnabled: () => {
-        const service = getService();
-        return service && service.canStart();
+        return widget && service.canStart();
       },
       execute: async () => {
-        await getService().session.start();
+        await service.session.start();
         commands.notifyCommandChanged();
       }
     });
@@ -312,11 +309,10 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
     commands.addCommand(CommandIDs.debugContinue, {
       label: 'Continue',
       isEnabled: () => {
-        const service = getService();
-        return service && service.isThreadStopped();
+        return service.isThreadStopped();
       },
       execute: async () => {
-        await getService().continue();
+        await service.continue();
         commands.notifyCommandChanged();
       }
     });
@@ -324,30 +320,27 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
     commands.addCommand(CommandIDs.next, {
       label: 'Next',
       isEnabled: () => {
-        const service = getService();
-        return service && service.isThreadStopped();
+        return service.isThreadStopped();
       },
       execute: async () => {
-        await getService().next();
+        await service.next();
       }
     });
 
     commands.addCommand(CommandIDs.stepIn, {
       label: 'StepIn',
       isEnabled: () => {
-        const service = getService();
-        return service && service.isThreadStopped();
+        return service.isThreadStopped();
       },
       execute: async () => {
-        await getService().stepIn();
+        await service.stepIn();
       }
     });
 
     commands.addCommand(CommandIDs.debugNotebook, {
       label: 'Launch',
       isEnabled: () => {
-        const service = getService();
-        return service && service.isStarted();
+        return service.isStarted();
       },
       execute: async () => {
         await tracker.currentWidget.content.service.launch(
@@ -376,15 +369,12 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
         const savedMode = (await state.fetch('mode')) as IDebugger.Mode;
         const mode = savedMode ? savedMode : 'expanded';
 
-        if (id) {
-          console.log('Debugger ID: ', id);
-        }
-
         if (tracker.currentWidget) {
           widget = tracker.currentWidget;
         } else {
           widget = new MainAreaWidget({
             content: new Debugger({
+              debugService: service,
               connector: state,
               editorFactory
             })
@@ -400,6 +390,8 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
           });
         }
 
+        console.log('Debugger ID: ', widget.id);
+
         widget.content.service.eventMessage.connect(_ => {
           commands.notifyCommandChanged();
         });
@@ -436,39 +428,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       });
     }
 
-    // Create a proxy to pass the `session` and `mode` to the debugger.
-
-    const proxy: IDebugger = Object.defineProperties(
-      {},
-      {
-        mode: {
-          get: (): IDebugger.Mode => {
-            return widget.content.model.mode;
-          },
-          set: (mode: IDebugger.Mode) => {
-            if (widget) {
-              widget.content.model.mode = mode;
-            }
-          }
-        },
-        session: {
-          get: (): IDebugger.ISession | null => {
-            return widget ? widget.content.service.session : null;
-          },
-          set: (src: IDebugger.ISession | null) => {
-            if (widget) {
-              widget.content.service.session = src;
-            }
-          }
-        },
-        tracker: {
-          get: (): WidgetTracker<MainAreaWidget<Debugger>> => {
-            return tracker;
-          }
-        }
-      }
-    );
-    return proxy;
+    return service;
   }
 };
 

+ 16 - 21
src/service.ts

@@ -1,6 +1,3 @@
-// temporary
-import { MainAreaWidget, WidgetTracker } from '@jupyterlab/apputils';
-
 import { ISignal, Signal } from '@phosphor/signaling';
 
 import { DebugProtocol } from 'vscode-debugprotocol';
@@ -14,18 +11,16 @@ import { Variables } from './variables';
 import { Callstack } from './callstack';
 
 export class DebugService implements IDebugger {
-  constructor(debuggerModel: Debugger.Model) {
+  constructor() {
     // Avoids setting session with invalid client
     // session should be set only when a notebook or
     // a console get the focus.
     // TODO: also checks that the notebook or console
     // runs a kernel with debugging ability
     this._session = null;
-    this._model = debuggerModel;
-  }
-
-  get tracker(): WidgetTracker<MainAreaWidget<Debugger>> {
-    return null;
+    // The model will be set by the UI which can be built
+    // after the service.
+    this._model = null;
   }
 
   dispose(): void {
@@ -48,6 +43,18 @@ export class DebugService implements IDebugger {
     this._model.mode = mode;
   }
 
+  get model(): Debugger.Model {
+    return this._model;
+  }
+
+  set model(model: Debugger.Model) {
+    this._model = model;
+  }
+
+  get session(): IDebugger.ISession {
+    return this._session;
+  }
+
   set session(session: IDebugger.ISession) {
     if (this._session === session) {
       return;
@@ -70,10 +77,6 @@ export class DebugService implements IDebugger {
     this._sessionChanged.emit(session);
   }
 
-  get session() {
-    return this._session;
-  }
-
   canStart(): boolean {
     return this._session !== null && !this._session.isStarted;
   }
@@ -127,15 +130,7 @@ export class DebugService implements IDebugger {
 
   // this will change for after execute cell
   async launch(code: string): Promise<void> {
-    let threadId: number = 1;
     this.frames = [];
-    this.session.eventMessage.connect((_, event: IDebugger.ISession.Event) => {
-      const eventName = event.event;
-      if (eventName === 'thread') {
-        const msg = event as DebugProtocol.ThreadEvent;
-        threadId = msg.body.threadId;
-      }
-    });
 
     const breakpoints: DebugProtocol.SourceBreakpoint[] = this.getBreakpoints();
     const dumpedCell = await this.session.sendRequest('dumpCell', {

+ 12 - 9
src/tokens.ts

@@ -1,11 +1,7 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
-import {
-  IClientSession,
-  MainAreaWidget,
-  WidgetTracker
-} from '@jupyterlab/apputils';
+import { IClientSession } from '@jupyterlab/apputils';
 
 import { CodeEditor } from '@jupyterlab/codeeditor';
 
@@ -19,6 +15,8 @@ import { ISignal } from '@phosphor/signaling';
 
 import { DebugProtocol } from 'vscode-debugprotocol';
 
+// TODO: remove that import when an interface has
+// been created for Model class
 import { Debugger } from './debugger';
 
 /**
@@ -35,14 +33,14 @@ export interface IDebugger {
   mode: IDebugger.Mode;
 
   /**
-   * The current debugger session.
+   * The model of the debugger.
    */
-  session: IDebugger.ISession;
+  readonly model: Debugger.Model;
 
   /**
-   * tracker for get instance of debugger.
+   * The current debugger session.
    */
-  tracker: WidgetTracker<MainAreaWidget<Debugger>>;
+  session: IDebugger.ISession;
 
   /**
    * Whether the debugger can start.
@@ -74,6 +72,11 @@ export interface IDebugger {
    */
   stepIn(): Promise<void>;
 
+  /**
+   * Update all breakpoints at once.
+   */
+  updateBreakpoints(): Promise<void>;
+
   /**
    * Signal emitted upon session changed.
    */

+ 7 - 1
tests/src/debugger.spec.ts

@@ -4,16 +4,22 @@ import { CodeMirrorEditorFactory } from '@jupyterlab/codemirror';
 
 import { Debugger } from '../../lib/debugger';
 
+import { DebugService } from '../../lib/service';
+
 class TestPanel extends Debugger {}
 
 describe('Debugger', () => {
+  const service = new DebugService();
   const editorServices = new CodeMirrorEditorFactory();
   const editorFactory = editorServices.newInlineEditor;
 
   let panel: TestPanel;
 
   beforeEach(() => {
-    panel = new TestPanel({ editorFactory });
+    panel = new TestPanel({
+      debugService: service,
+      editorFactory: editorFactory
+    });
   });
 
   afterEach(() => {