浏览代码

Merge branch 'notebook_lines' into debugger-ui

Borys Palka 5 年之前
父节点
当前提交
e6bd0dc23f
共有 7 个文件被更改,包括 197 次插入44 次删除
  1. 1 0
      package.json
  2. 2 1
      src/breakpoints/body.tsx
  3. 146 23
      src/breakpoints/index.ts
  4. 21 10
      src/debugger.ts
  5. 17 9
      src/index.ts
  6. 3 1
      src/sidebar.ts
  7. 7 0
      style/breakpoints.css

+ 1 - 0
package.json

@@ -52,6 +52,7 @@
     "@phosphor/coreutils": "^1.3.1",
     "@phosphor/disposable": "^1.2.0",
     "@phosphor/widgets": "^1.8.0",
+    "@types/codemirror": "0.0.76",
     "vscode-debugprotocol": "1.35.0"
   },
   "devDependencies": {

+ 2 - 1
src/breakpoints/body.tsx

@@ -29,13 +29,14 @@ const BreakpointsComponent = ({ model }: { model: Breakpoints.IModel }) => {
       if (ArrayExt.shallowEqual(breakpoints, updates)) {
         return;
       }
+      console.log(updates);
       setBreakpoints(updates);
     }
   );
 
   return (
     <div>
-      {breakpoints.map((breakpoint: any) => (
+      {breakpoints.map((breakpoint: Breakpoints.IBreakpoint) => (
         <BreakpointComponent
           key={breakpoint.id}
           breakpoint={breakpoint}

+ 146 - 23
src/breakpoints/index.ts

@@ -7,12 +7,16 @@ import { Widget, Panel, PanelLayout } from '@phosphor/widgets';
 import { DebugProtocol } from 'vscode-debugprotocol';
 import { Body } from './body';
 import { Signal, ISignal } from '@phosphor/signaling';
+import { INotebookTracker } from '@jupyterlab/notebook';
+import { CodeMirrorEditor } from '@jupyterlab/codemirror';
+import { Editor } from 'codemirror';
+import { CodeCell, Cell } from '@jupyterlab/cells';
 
 export class Breakpoints extends Panel {
-  constructor(options: Breakpoints.IOptions = {}) {
+  constructor(options: Breakpoints.IOptions) {
     super();
 
-    this.model = new Breakpoints.IModel(MOCK_BREAKPOINTS);
+    this.model = new Breakpoints.IModel([] as Breakpoints.IBreakpoint[]);
     this.addClass('jp-DebuggerBreakpoints');
     this.title.label = 'Breakpoints';
 
@@ -47,11 +51,119 @@ export class Breakpoints extends Panel {
         tooltip: 'Remove All Breakpoints'
       })
     );
+
+    this.notebook = options.notebook;
+
+    this.notebook.activeCellChanged.connect(this.onActiveCellChanged, this);
   }
 
   private isAllActive = true;
   readonly body: Widget;
   readonly model: Breakpoints.IModel;
+  notebook: INotebookTracker;
+  previousCell: CodeCell;
+
+  protected onActiveCellChanged() {
+    const activeCell = this.getCell();
+    if (this.previousCell && !this.previousCell.isDisposed) {
+      this.removeListner(this.previousCell);
+    }
+    this.previousCell = activeCell;
+    this.setEditor(activeCell);
+  }
+
+  protected getCell(): CodeCell {
+    return this.notebook.activeCell as CodeCell;
+  }
+
+  removeListner(cell: CodeCell) {
+    const editor = cell.editor as CodeMirrorEditor;
+    editor.setOption('lineNumbers', false);
+    editor.editor.off('gutterClick', this.addBreakpoint);
+    this.model.breakpoints = [];
+  }
+
+  setEditor(cell: CodeCell) {
+    if (!cell || !cell.editor) {
+      return;
+    }
+
+    const editor = cell.editor as CodeMirrorEditor;
+    editor.setOption('lineNumbers', true);
+    editor.editor.setOption('gutters', [
+      'CodeMirror-linenumbers',
+      'breakpoints'
+    ]);
+
+    editor.editor.on('gutterClick', this.addBreakpoint);
+  }
+
+  protected addBreakpoint = (editor: Editor, lineNumber: number) => {
+    const info = editor.lineInfo(lineNumber);
+    if (!info) {
+      return;
+    }
+
+    const breakpointMarker = {
+      line: lineNumber,
+      text: info.text,
+      remove: !!info.gutterMarkers
+    };
+
+    var breakpoint: Breakpoints.IBreakpoint = this.model.getBreakpointByLineNumber(
+      lineNumber
+    );
+
+    if (!breakpoint) {
+      breakpoint = {
+        id: this.model.breakpoints.length + 1,
+        active: true,
+        verified: true,
+        source: {
+          name: 'untitled.py'
+        },
+        line: lineNumber
+      };
+    }
+
+    editor.setGutterMarker(
+      lineNumber,
+      'breakpoints',
+      breakpointMarker.remove ? null : this.createMarkerNode()
+    );
+
+    this.model.breakpoint = breakpoint;
+    this.getExistingBreakpoints(this.getCell());
+  };
+
+  createMarkerNode() {
+    var marker = document.createElement('div');
+    marker.className = 'jp-breakpoint-marker';
+    marker.innerHTML = '●';
+    return marker;
+  }
+
+  protected getExistingBreakpoints(cell: Cell) {
+    const editor = cell.editor as CodeMirrorEditor;
+
+    // let lines = [];
+    editor.doc.eachLine(line => {
+      console.log(line);
+    });
+    //   for (let i = 0; i < editor.doc.lineCount(); i++) {
+    //     const info = editor.editor.lineInfo(i);
+    //     if (info.gutterMarkers) {
+    //       const breakpoint = {
+    //         line: info.line + 1, // lines start at 1
+    //         text: info.text,
+    //         remove: false
+    //       };
+    //       lines.push(breakpoint);
+    //     }
+    //   }
+    //   return lines;
+    // }
+  }
 }
 
 class BreakpointsHeader extends Widget {
@@ -107,9 +219,18 @@ export namespace Breakpoints {
       if (index !== -1) {
         this._state[index] = breakpoint;
         this._breakpointChanged.emit(breakpoint);
+      } else {
+        const breakpoints = this.breakpoints;
+        breakpoints.push(breakpoint);
+        this.breakpoints = [];
+        this.breakpoints = breakpoints;
       }
     }
 
+    getBreakpointByLineNumber(lineNumber: number) {
+      return this.breakpoints.find(ele => ele.line === lineNumber);
+    }
+
     private _state: IBreakpoint[];
     private _breakpointsChanged = new Signal<this, IBreakpoint[]>(this);
     private _breakpointChanged = new Signal<this, IBreakpoint>(this);
@@ -118,26 +239,28 @@ export namespace Breakpoints {
   /**
    * Instantiation options for `Breakpoints`;
    */
-  export interface IOptions extends Panel.IOptions {}
+  export interface IOptions extends Panel.IOptions {
+    notebook?: INotebookTracker;
+  }
 }
 
-const MOCK_BREAKPOINTS = [
-  {
-    id: 0,
-    active: true,
-    verified: true,
-    source: {
-      name: 'untitled.py'
-    },
-    line: 6
-  },
-  {
-    id: 1,
-    verified: true,
-    active: false,
-    source: {
-      name: 'untitled.py'
-    },
-    line: 7
-  }
-];
+// const MOCK_BREAKPOINTS = [
+//   {
+//     id: 0,
+//     active: true,
+//     verified: true,
+//     source: {
+//       name: 'untitled.py'
+//     },
+//     line: 6
+//   },
+//   {
+//     id: 1,
+//     verified: true,
+//     active: false,
+//     source: {
+//       name: 'untitled.py'
+//     },
+//     line: 7
+//   }
+// ];

+ 21 - 10
src/debugger.ts

@@ -3,33 +3,37 @@
 
 import { IDataConnector } from '@jupyterlab/coreutils';
 
-import { BoxPanel, TabPanel } from '@phosphor/widgets';
+import { BoxPanel } from '@phosphor/widgets';
 
 import { ReadonlyJSONValue, UUID } from '@phosphor/coreutils';
 
 import { IDisposable } from '@phosphor/disposable';
 
 import { DebuggerSidebar } from './sidebar';
+import { INotebookTracker } from '@jupyterlab/notebook';
+import { CodeCell } from '@jupyterlab/cells';
+import { IEditorTracker } from '@jupyterlab/fileeditor';
 
 export class Debugger extends BoxPanel {
-  readonly model: Debugger.Model;
-
-  readonly tabs = new TabPanel();
-
-  readonly sidebar: DebuggerSidebar;
-
   constructor(options: Debugger.IOptions) {
     super({ direction: 'left-to-right' });
-
+    this.notebook = options.notebook;
     this.model = new Debugger.Model(options);
     this.sidebar = new DebuggerSidebar(this.model);
     this.title.label = 'Debugger';
 
     this.addClass('jp-Debugger');
-    this.addWidget(this.tabs);
     this.addWidget(this.sidebar);
   }
 
+  readonly model: Debugger.Model;
+
+  readonly sidebar: DebuggerSidebar;
+
+  editor: IEditorTracker;
+  notebook: INotebookTracker;
+  previousCell: CodeCell;
+
   dispose(): void {
     if (this.isDisposed) {
       return;
@@ -45,7 +49,8 @@ export class Debugger extends BoxPanel {
 export namespace Debugger {
   export interface IOptions {
     connector?: IDataConnector<ReadonlyJSONValue>;
-
+    notebook?: INotebookTracker;
+    editor?: IEditorTracker;
     id?: string;
   }
 
@@ -53,6 +58,7 @@ export namespace Debugger {
     constructor(options: Debugger.Model.IOptions) {
       this.connector = options.connector || null;
       this.id = options.id || UUID.uuid4();
+      this._notebook = options.notebook;
       void this._populate();
     }
 
@@ -64,6 +70,10 @@ export namespace Debugger {
       return this._isDisposed;
     }
 
+    get notebook() {
+      return this._notebook;
+    }
+
     dispose(): void {
       this._isDisposed = true;
     }
@@ -77,6 +87,7 @@ export namespace Debugger {
     }
 
     private _isDisposed = false;
+    private _notebook: INotebookTracker;
   }
 
   export namespace Model {

+ 17 - 9
src/index.ts

@@ -19,7 +19,7 @@ import { INotebookTracker } from '@jupyterlab/notebook';
 
 import { Debugger } from './debugger';
 
-import { DebuggerSidebar } from './sidebar';
+// import { DebuggerSidebar } from './sidebar';
 
 import { IDebugger, IDebuggerSidebar } from './tokens';
 
@@ -96,18 +96,23 @@ const notebooks: JupyterFrontEndPlugin<void> = {
 /**
  * A plugin providing a condensed sidebar UI for debugging.
  */
-const sidebar: JupyterFrontEndPlugin<IDebuggerSidebar> = {
+const sidebar: JupyterFrontEndPlugin<Debugger> = {
   id: '@jupyterlab/debugger:sidebar',
-  optional: [ILayoutRestorer],
+  optional: [ILayoutRestorer, INotebookTracker, IEditorTracker],
   autoStart: true,
   activate: (
     app: JupyterFrontEnd,
-    restorer: ILayoutRestorer | null
-  ): DebuggerSidebar => {
+    restorer: ILayoutRestorer | null,
+    notebookTracker: INotebookTracker,
+    editorTracker: IEditorTracker
+  ): Debugger => {
     const { shell } = app;
     const label = 'Environment';
     const namespace = 'jp-debugger-sidebar';
-    const sidebar = new DebuggerSidebar(null);
+    const sidebar = new Debugger({
+      notebook: notebookTracker,
+      editor: editorTracker
+    });
 
     sidebar.id = namespace;
     sidebar.title.label = label;
@@ -126,7 +131,7 @@ const sidebar: JupyterFrontEndPlugin<IDebuggerSidebar> = {
  */
 const tracker: JupyterFrontEndPlugin<IDebugger> = {
   id: '@jupyterlab/debugger:tracker',
-  optional: [ILayoutRestorer, IDebuggerSidebar],
+  optional: [ILayoutRestorer, IDebuggerSidebar, INotebookTracker],
   requires: [IStateDB],
   provides: IDebugger,
   autoStart: true,
@@ -143,7 +148,7 @@ const tracker: JupyterFrontEndPlugin<IDebugger> = {
     app.commands.addCommand(CommandIDs.create, {
       execute: args => {
         const id = (args.id as string) || '';
-
+        console.log(id, 'hi');
         if (id) {
           console.log('Debugger ID: ', id);
         }
@@ -153,7 +158,10 @@ const tracker: JupyterFrontEndPlugin<IDebugger> = {
         }
 
         const widget = new MainAreaWidget({
-          content: new Debugger({ connector: state, id })
+          content: new Debugger({
+            connector: state,
+            id: id
+          })
         });
 
         void tracker.add(widget);

+ 3 - 1
src/sidebar.ts

@@ -18,9 +18,11 @@ export class DebuggerSidebar extends SplitPanel {
     this.orientation = 'vertical';
     this.addClass('jp-DebuggerSidebar');
 
+    const notebook = this.model.notebook;
+
     this.variables = new Variables();
     this.callstack = new Callstack();
-    this.breakpoints = new Breakpoints();
+    this.breakpoints = new Breakpoints({ notebook: notebook });
 
     this.addWidget(this.variables);
     this.addWidget(this.callstack);

+ 7 - 0
style/breakpoints.css

@@ -6,3 +6,10 @@
 .jp-DebuggerBreakpoints-body {
   padding: 10px;
 }
+
+.jp-breakpoint-marker {
+  position: absolute;
+  left: -30px;
+  top: -1px;
+  color: #f22;
+}