|
@@ -9,8 +9,8 @@ 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';
|
|
|
+import { Editor, Doc } from 'codemirror';
|
|
|
+import { CodeCell } from '@jupyterlab/cells';
|
|
|
|
|
|
export class Breakpoints extends Panel {
|
|
|
constructor(options: Breakpoints.IOptions) {
|
|
@@ -47,6 +47,8 @@ export class Breakpoints extends Panel {
|
|
|
iconClassName: 'jp-CloseAllIcon',
|
|
|
onClick: () => {
|
|
|
this.model.breakpoints = [];
|
|
|
+ this.cellsBreakpoints[this.getCell().id] = {};
|
|
|
+ this.removeAllGutterBreakpoints(this.getCell());
|
|
|
},
|
|
|
tooltip: 'Remove All Breakpoints'
|
|
|
})
|
|
@@ -63,25 +65,43 @@ export class Breakpoints extends Panel {
|
|
|
readonly model: Breakpoints.IModel;
|
|
|
notebook: INotebookTracker;
|
|
|
previousCell: CodeCell;
|
|
|
+ previousLineCount: number;
|
|
|
+ cellsBreakpoints: any = {};
|
|
|
|
|
|
protected onActiveCellChanged() {
|
|
|
const activeCell = this.getCell();
|
|
|
- if (this.previousCell && !this.previousCell.isDisposed) {
|
|
|
- this.removeListner(this.previousCell);
|
|
|
+ if (this.model && activeCell) {
|
|
|
+ if (this.previousCell && !this.previousCell.isDisposed) {
|
|
|
+ this.removeListner(this.previousCell);
|
|
|
+ }
|
|
|
+ this.previousCell = activeCell;
|
|
|
+ const id: string = activeCell.model.id;
|
|
|
+ if (id && !this.cellsBreakpoints[id]) {
|
|
|
+ this.cellsBreakpoints[id] = [];
|
|
|
+ }
|
|
|
+ this.model.breakpoints = this.cellsBreakpoints[id];
|
|
|
+ this.setEditor(activeCell);
|
|
|
}
|
|
|
- this.previousCell = activeCell;
|
|
|
- this.setEditor(activeCell);
|
|
|
}
|
|
|
|
|
|
protected getCell(): CodeCell {
|
|
|
return this.notebook.activeCell as CodeCell;
|
|
|
}
|
|
|
|
|
|
+ protected removeAllGutterBreakpoints(cell: CodeCell) {
|
|
|
+ const editor = cell.editor as CodeMirrorEditor;
|
|
|
+ editor.editor.getDoc().eachLine(line => {
|
|
|
+ editor.editor.setGutterMarker(line, 'breakpoints', null);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
removeListner(cell: CodeCell) {
|
|
|
const editor = cell.editor as CodeMirrorEditor;
|
|
|
+ this.cellsBreakpoints[cell.model.id] = this.model.breakpoints;
|
|
|
+ this.model.breakpoints = [];
|
|
|
editor.setOption('lineNumbers', false);
|
|
|
editor.editor.off('gutterClick', this.addBreakpoint);
|
|
|
- this.model.breakpoints = [];
|
|
|
+ editor.editor.off('renderLine', this.onNewRenderLine);
|
|
|
}
|
|
|
|
|
|
setEditor(cell: CodeCell) {
|
|
@@ -97,44 +117,74 @@ export class Breakpoints extends Panel {
|
|
|
]);
|
|
|
|
|
|
editor.editor.on('gutterClick', this.addBreakpoint);
|
|
|
+ editor.editor.on('renderLine', this.onNewRenderLine);
|
|
|
}
|
|
|
|
|
|
+ protected onNewRenderLine = (editor: Editor, line: any) => {
|
|
|
+ const lineInfo = editor.lineInfo(line);
|
|
|
+ if (
|
|
|
+ !this.model.breakpoints &&
|
|
|
+ this.model.breakpoints.length < 1 &&
|
|
|
+ lineInfo.handle &&
|
|
|
+ lineInfo.handle.order === false
|
|
|
+ ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const doc: Doc = editor.getDoc();
|
|
|
+ const linesNumber = doc.lineCount();
|
|
|
+
|
|
|
+ if (this.previousLineCount !== linesNumber) {
|
|
|
+ if (this.previousLineCount > linesNumber) {
|
|
|
+ this.model.changeLines(lineInfo.line, -1);
|
|
|
+ }
|
|
|
+ if (this.previousLineCount < linesNumber) {
|
|
|
+ this.model.changeLines(lineInfo.line, +1);
|
|
|
+ }
|
|
|
+ this.previousLineCount = linesNumber;
|
|
|
+ }
|
|
|
+ // eage case for backspace line 2
|
|
|
+ if (lineInfo.line === 0) {
|
|
|
+ const breakpoint: Breakpoints.IBreakpoint = this.model.getBreakpointByLineNumber(
|
|
|
+ -1
|
|
|
+ );
|
|
|
+ if (breakpoint) {
|
|
|
+ this.model.removeBreakpoint(breakpoint);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
protected addBreakpoint = (editor: Editor, lineNumber: number) => {
|
|
|
const info = editor.lineInfo(lineNumber);
|
|
|
if (!info) {
|
|
|
return;
|
|
|
}
|
|
|
+ const removeGutter = !!info.gutterMarkers;
|
|
|
|
|
|
- const breakpointMarker = {
|
|
|
- line: lineNumber,
|
|
|
- text: info.text,
|
|
|
- remove: !!info.gutterMarkers
|
|
|
- };
|
|
|
-
|
|
|
- var breakpoint: Breakpoints.IBreakpoint = this.model.getBreakpointByLineNumber(
|
|
|
+ const breakpoint: Breakpoints.IBreakpoint = this.model.getBreakpointByLineNumber(
|
|
|
lineNumber
|
|
|
);
|
|
|
|
|
|
- if (!breakpoint) {
|
|
|
- breakpoint = {
|
|
|
+ if (!breakpoint && !removeGutter) {
|
|
|
+ this.model.breakpoint = {
|
|
|
id: this.model.breakpoints.length + 1,
|
|
|
active: true,
|
|
|
verified: true,
|
|
|
source: {
|
|
|
+ // TODO: need get filename
|
|
|
name: 'untitled.py'
|
|
|
},
|
|
|
line: lineNumber
|
|
|
};
|
|
|
+ } else if (removeGutter) {
|
|
|
+ this.model.removeBreakpoint(breakpoint);
|
|
|
}
|
|
|
|
|
|
editor.setGutterMarker(
|
|
|
lineNumber,
|
|
|
'breakpoints',
|
|
|
- breakpointMarker.remove ? null : this.createMarkerNode()
|
|
|
+ removeGutter ? null : this.createMarkerNode()
|
|
|
);
|
|
|
-
|
|
|
- this.model.breakpoint = breakpoint;
|
|
|
- this.getExistingBreakpoints(this.getCell());
|
|
|
};
|
|
|
|
|
|
createMarkerNode() {
|
|
@@ -143,30 +193,7 @@ export class Breakpoints extends Panel {
|
|
|
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 {
|
|
|
constructor(title: string) {
|
|
|
super({ node: document.createElement('header') });
|
|
@@ -228,6 +255,23 @@ export namespace Breakpoints {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ removeBreakpoint(breakpoint: IBreakpoint) {
|
|
|
+ const breakpoints = this.breakpoints.filter(
|
|
|
+ ele => ele.line !== breakpoint.line
|
|
|
+ );
|
|
|
+ this.breakpoints = [];
|
|
|
+ this.breakpoints = breakpoints;
|
|
|
+ }
|
|
|
+
|
|
|
+ changeLines(lineEnter: number, howMany: number) {
|
|
|
+ const breakpoints = this.breakpoints.map(ele => {
|
|
|
+ ele.line = lineEnter <= ele.line ? ele.line + howMany : ele.line;
|
|
|
+ return ele;
|
|
|
+ });
|
|
|
+ this.breakpoints = [];
|
|
|
+ this.breakpoints = breakpoints;
|
|
|
+ }
|
|
|
+
|
|
|
getBreakpointByLineNumber(lineNumber: number) {
|
|
|
return this.breakpoints.find(ele => ele.line === lineNumber);
|
|
|
}
|
|
@@ -244,24 +288,3 @@ export namespace Breakpoints {
|
|
|
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
|
|
|
-// }
|
|
|
-// ];
|