Browse Source

Continue, Next and StepIn commands

Johan Mabille 5 years ago
parent
commit
fd0510b08d
3 changed files with 144 additions and 1 deletions
  1. 51 0
      src/index.ts
  2. 62 1
      src/service.ts
  3. 31 0
      src/tokens.ts

+ 51 - 0
src/index.ts

@@ -45,6 +45,12 @@ export namespace CommandIDs {
 
   export const stop = 'debugger:stop';
 
+  export const debugContinue = 'debugger:continue';
+
+  export const next = 'debugger:next';
+
+  export const stepIn = 'debugger:stepIn';
+
   export const debugConsole = 'debugger:debug-console';
 
   export const debugFile = 'debugger:debug-file';
@@ -303,6 +309,40 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       }
     });
 
+    commands.addCommand(CommandIDs.debugContinue, {
+      label: 'Continue',
+      isEnabled: () => {
+        const service = getService();
+        return service && service.isThreadStopped();
+      },
+      execute: async () => {
+        await getService().continue();
+        commands.notifyCommandChanged();
+      }
+    });
+
+    commands.addCommand(CommandIDs.next, {
+      label: 'Next',
+      isEnabled: () => {
+        const service = getService();
+        return service && service.isThreadStopped();
+      },
+      execute: async () => {
+        await getService().next();
+      }
+    });
+
+    commands.addCommand(CommandIDs.stepIn, {
+      label: 'StepIn',
+      isEnabled: () => {
+        const service = getService();
+        return service && service.isThreadStopped();
+      },
+      execute: async () => {
+        await getService().stepIn();
+      }
+    });
+
     commands.addCommand(CommandIDs.debugNotebook, {
       label: 'Launch',
       isEnabled: () => {
@@ -360,6 +400,14 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
           });
         }
 
+        widget.content.service.eventMessage.connect(_ => {
+          commands.notifyCommandChanged();
+        });
+
+        widget.content.service.sessionChanged.connect(_ => {
+          commands.notifyCommandChanged();
+        });
+
         await commands.execute(CommandIDs.mount, { mode });
         return widget;
       }
@@ -371,6 +419,9 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       palette.addItem({ command: CommandIDs.create, category });
       palette.addItem({ command: CommandIDs.start, category });
       palette.addItem({ command: CommandIDs.stop, category });
+      palette.addItem({ command: CommandIDs.debugContinue, category });
+      palette.addItem({ command: CommandIDs.next, category });
+      palette.addItem({ command: CommandIDs.stepIn, category });
       palette.addItem({ command: CommandIDs.debugNotebook, category });
     }
 

+ 62 - 1
src/service.ts

@@ -29,6 +29,15 @@ export class DebugService implements IDebugger.IService {
       this._session.dispose();
     }
     this._session = session;
+
+    this._session.eventMessage.connect((_, event) => {
+      if (event.event === 'stopped') {
+        this._threadStopped.add(event.body.threadId);
+      } else if (event.event === 'continued') {
+        this._threadStopped.delete(event.body.threadId);
+      }
+      this._eventMessage.emit(event);
+    });
     this._sessionChanged.emit(session);
   }
 
@@ -44,10 +53,49 @@ export class DebugService implements IDebugger.IService {
     return this._session !== null && this._session.isStarted;
   }
 
+  isThreadStopped(): boolean {
+    return this._threadStopped.has(this.currentThread());
+  }
+
+  async continue(): Promise<void> {
+    try {
+      await this.session.sendRequest('continue', {
+        threadId: this.currentThread()
+      });
+      this._threadStopped.delete(this.currentThread());
+    } catch (err) {
+      console.error('Error:', err.message);
+    }
+  }
+
+  async next(): Promise<void> {
+    try {
+      await this.session.sendRequest('next', {
+        threadId: this.currentThread()
+      });
+    } catch (err) {
+      console.error('Error:', err.message);
+    }
+  }
+
+  async stepIn(): Promise<void> {
+    try {
+      await this.session.sendRequest('stepIn', {
+        threadId: this.currentThread()
+      });
+    } catch (err) {
+      console.error('Error:', err.message);
+    }
+  }
+
   get sessionChanged(): ISignal<IDebugger.IService, IDebugger.ISession> {
     return this._sessionChanged;
   }
 
+  get eventMessage(): ISignal<IDebugger.IService, IDebugger.ISession.Event> {
+    return this._eventMessage;
+  }
+
   // this will change for after execute cell
   async launch(code: string): Promise<void> {
     let threadId: number = 1;
@@ -158,10 +206,23 @@ export class DebugService implements IDebugger.IService {
     });
   };
 
+  private currentThread(): number {
+    // TODO: ask the model for the current thread ID
+    return 1;
+  }
+
   private _session: IDebugger.ISession;
-  private _sessionChanged = new Signal<this, IDebugger.ISession>(this);
+  private _sessionChanged = new Signal<IDebugger.IService, IDebugger.ISession>(
+    this
+  );
+  private _eventMessage = new Signal<
+    IDebugger.IService,
+    IDebugger.ISession.Event
+  >(this);
   private _model: Debugger.Model;
   private frames: Frame[];
+  // TODO: move this in model
+  private _threadStopped = new Set();
 }
 
 export type Frame = {

+ 31 - 0
src/tokens.ts

@@ -96,6 +96,9 @@ export namespace IDebugger {
       args: IDebugger.ISession.Request[K]
     ): Promise<IDebugger.ISession.Response[K]>;
 
+    /**
+     * Signal emitted for debug event messages.
+     */
     eventMessage: ISignal<IDebugger.ISession, IDebugger.ISession.Event>;
   }
 
@@ -115,12 +118,40 @@ export namespace IDebugger {
      */
     isStarted(): boolean;
 
+    /**
+     * Whether the current thread is stopped.
+     */
+    isThreadStopped(): boolean;
+
+    /**
+     * Continues the execution of the current thread.
+     */
+    continue(): Promise<void>;
+
+    /**
+     * Makes the current thread run again for one step.
+     */
+    next(): Promise<void>;
+
+    /**
+     * Makes the current thread step in a function / method if possible.
+     */
+    stepIn(): Promise<void>;
+
     /**
      * For testing purpose only, to be removed.
      */
     launch(code: string): Promise<void>;
 
+    /**
+     * Signal emitted upon session changed.
+     */
     sessionChanged: ISignal<IDebugger.IService, IDebugger.ISession>;
+
+    /**
+     * Signal emitted for debug event messages.
+     */
+    eventMessage: ISignal<IDebugger.IService, IDebugger.ISession.Event>;
   }
 
   export namespace ISession {