@jupyterlab/debugger 4.0.0-alpha.8 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/lib/debugger.d.ts +3 -1
  2. package/lib/debugger.js +5 -3
  3. package/lib/debugger.js.map +1 -1
  4. package/lib/dialogs/evaluate.d.ts +5 -0
  5. package/lib/dialogs/evaluate.js +16 -6
  6. package/lib/dialogs/evaluate.js.map +1 -1
  7. package/lib/factory.d.ts +1 -1
  8. package/lib/factory.js +15 -9
  9. package/lib/factory.js.map +1 -1
  10. package/lib/handler.js +15 -7
  11. package/lib/handler.js.map +1 -1
  12. package/lib/handlers/console.js +9 -3
  13. package/lib/handlers/console.js.map +1 -1
  14. package/lib/handlers/editor.d.ts +26 -12
  15. package/lib/handlers/editor.js +166 -89
  16. package/lib/handlers/editor.js.map +1 -1
  17. package/lib/handlers/file.js +6 -2
  18. package/lib/handlers/file.js.map +1 -1
  19. package/lib/handlers/notebook.d.ts +0 -7
  20. package/lib/handlers/notebook.js +11 -17
  21. package/lib/handlers/notebook.js.map +1 -1
  22. package/lib/icons.d.ts +2 -1
  23. package/lib/icons.js +6 -1
  24. package/lib/icons.js.map +1 -1
  25. package/lib/model.js +1 -0
  26. package/lib/model.js.map +1 -1
  27. package/lib/panels/breakpoints/index.d.ts +2 -2
  28. package/lib/panels/breakpoints/index.js +9 -7
  29. package/lib/panels/breakpoints/index.js.map +1 -1
  30. package/lib/panels/breakpoints/pauseonexceptions.d.ts +53 -0
  31. package/lib/panels/breakpoints/pauseonexceptions.js +98 -0
  32. package/lib/panels/breakpoints/pauseonexceptions.js.map +1 -0
  33. package/lib/panels/callstack/index.d.ts +1 -1
  34. package/lib/panels/kernelSources/body.d.ts +5 -7
  35. package/lib/panels/kernelSources/body.js +37 -47
  36. package/lib/panels/kernelSources/body.js.map +1 -1
  37. package/lib/panels/kernelSources/filter.d.ts +1 -2
  38. package/lib/panels/kernelSources/filter.js +2 -2
  39. package/lib/panels/kernelSources/filter.js.map +1 -1
  40. package/lib/panels/kernelSources/index.js +1 -1
  41. package/lib/panels/kernelSources/index.js.map +1 -1
  42. package/lib/panels/kernelSources/model.d.ts +10 -1
  43. package/lib/panels/kernelSources/model.js +19 -1
  44. package/lib/panels/kernelSources/model.js.map +1 -1
  45. package/lib/panels/sources/body.js +5 -3
  46. package/lib/panels/sources/body.js.map +1 -1
  47. package/lib/panels/variables/grid.d.ts +10 -0
  48. package/lib/panels/variables/grid.js +28 -351
  49. package/lib/panels/variables/grid.js.map +1 -1
  50. package/lib/panels/variables/gridpanel.d.ts +140 -0
  51. package/lib/panels/variables/gridpanel.js +363 -0
  52. package/lib/panels/variables/gridpanel.js.map +1 -0
  53. package/lib/panels/variables/mimerenderer.js +9 -1
  54. package/lib/panels/variables/mimerenderer.js.map +1 -1
  55. package/lib/panels/variables/scope.js +4 -0
  56. package/lib/panels/variables/scope.js.map +1 -1
  57. package/lib/panels/variables/tree.d.ts +1 -0
  58. package/lib/panels/variables/tree.js +136 -47
  59. package/lib/panels/variables/tree.js.map +1 -1
  60. package/lib/service.d.ts +20 -6
  61. package/lib/service.js +84 -59
  62. package/lib/service.js.map +1 -1
  63. package/lib/session.d.ts +20 -6
  64. package/lib/session.js +59 -11
  65. package/lib/session.js.map +1 -1
  66. package/lib/sidebar.js +1 -1
  67. package/lib/sources.d.ts +2 -2
  68. package/lib/sources.js +32 -17
  69. package/lib/sources.js.map +1 -1
  70. package/lib/tokens.d.ts +57 -13
  71. package/lib/tokens.js.map +1 -1
  72. package/package.json +39 -47
  73. package/src/config.ts +78 -0
  74. package/src/debugger.ts +154 -0
  75. package/src/dialogs/evaluate.ts +144 -0
  76. package/src/factory.ts +72 -0
  77. package/src/handler.ts +528 -0
  78. package/src/handlers/console.ts +118 -0
  79. package/src/handlers/editor.ts +469 -0
  80. package/src/handlers/file.ts +86 -0
  81. package/src/handlers/notebook.ts +128 -0
  82. package/src/hash.ts +69 -0
  83. package/src/icons.ts +64 -0
  84. package/src/index.ts +16 -0
  85. package/src/model.ts +155 -0
  86. package/src/panels/breakpoints/body.tsx +145 -0
  87. package/src/panels/breakpoints/index.ts +116 -0
  88. package/src/panels/breakpoints/model.ts +74 -0
  89. package/src/panels/breakpoints/pauseonexceptions.tsx +141 -0
  90. package/src/panels/callstack/body.tsx +96 -0
  91. package/src/panels/callstack/index.ts +152 -0
  92. package/src/panels/callstack/model.ts +86 -0
  93. package/src/panels/kernelSources/body.tsx +139 -0
  94. package/src/panels/kernelSources/filter.tsx +44 -0
  95. package/src/panels/kernelSources/index.tsx +106 -0
  96. package/src/panels/kernelSources/model.ts +160 -0
  97. package/src/panels/sources/body.ts +161 -0
  98. package/src/panels/sources/index.tsx +85 -0
  99. package/src/panels/sources/model.ts +94 -0
  100. package/src/panels/sources/sourcepath.tsx +28 -0
  101. package/src/panels/variables/grid.ts +144 -0
  102. package/src/panels/variables/gridpanel.ts +455 -0
  103. package/src/panels/variables/index.ts +199 -0
  104. package/src/panels/variables/mimerenderer.ts +125 -0
  105. package/src/panels/variables/model.ts +61 -0
  106. package/src/panels/variables/scope.tsx +132 -0
  107. package/src/panels/variables/tree.tsx +521 -0
  108. package/src/service.ts +1009 -0
  109. package/src/session.ts +342 -0
  110. package/src/sidebar.ts +194 -0
  111. package/src/sources.ts +327 -0
  112. package/src/svg.d.ts +9 -0
  113. package/src/tokens.ts +1071 -0
  114. package/style/breakpoints.css +21 -12
  115. package/style/icons/exceptions.svg +10 -0
  116. package/style/index.css +1 -1
  117. package/style/index.js +1 -1
  118. package/style/kernelSources.css +3 -0
  119. package/style/variables.css +68 -21
@@ -0,0 +1,469 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { CodeEditor } from '@jupyterlab/codeeditor';
5
+
6
+ import { CodeMirrorEditor } from '@jupyterlab/codemirror';
7
+
8
+ import { ActivityMonitor } from '@jupyterlab/coreutils';
9
+
10
+ import { IDisposable } from '@lumino/disposable';
11
+
12
+ import { Signal } from '@lumino/signaling';
13
+
14
+ import { ISharedText, SourceChange } from '@jupyter/ydoc';
15
+
16
+ import {
17
+ Compartment,
18
+ Prec,
19
+ RangeSet,
20
+ StateEffect,
21
+ StateEffectType,
22
+ StateField
23
+ } from '@codemirror/state';
24
+
25
+ import {
26
+ Decoration,
27
+ DecorationSet,
28
+ EditorView,
29
+ gutter,
30
+ GutterMarker
31
+ } from '@codemirror/view';
32
+
33
+ import { IDebugger } from '../tokens';
34
+
35
+ /**
36
+ * The class name added to the current line.
37
+ */
38
+ const LINE_HIGHLIGHT_CLASS = 'jp-DebuggerEditor-highlight';
39
+
40
+ /**
41
+ * The timeout for listening to editor content changes.
42
+ */
43
+ const EDITOR_CHANGED_TIMEOUT = 1000;
44
+
45
+ /**
46
+ * A handler for a CodeEditor.IEditor.
47
+ */
48
+ export class EditorHandler implements IDisposable {
49
+ /**
50
+ * Instantiate a new EditorHandler.
51
+ *
52
+ * @param options The instantiation options for a EditorHandler.
53
+ */
54
+ constructor(options: EditorHandler.IOptions) {
55
+ this._src = options.src;
56
+ this._id = options.debuggerService.session?.connection?.id ?? '';
57
+ this._path = options.path ?? '';
58
+ this._debuggerService = options.debuggerService;
59
+ this._editor = options.getEditor;
60
+
61
+ this._editorMonitor = new ActivityMonitor({
62
+ signal: this._src.changed,
63
+ timeout: EDITOR_CHANGED_TIMEOUT
64
+ });
65
+ this._editorMonitor.activityStopped.connect(() => {
66
+ this._sendEditorBreakpoints();
67
+ }, this);
68
+
69
+ this._debuggerService.model.breakpoints.changed.connect(async () => {
70
+ const editor = this.editor;
71
+ if (!editor || editor.isDisposed) {
72
+ return;
73
+ }
74
+ this._addBreakpointsToEditor();
75
+ });
76
+
77
+ this._debuggerService.model.breakpoints.restored.connect(async () => {
78
+ const editor = this.editor;
79
+ if (!editor || editor.isDisposed) {
80
+ return;
81
+ }
82
+ this._addBreakpointsToEditor();
83
+ });
84
+
85
+ this._debuggerService.model.callstack.currentFrameChanged.connect(() => {
86
+ const editor = this.editor;
87
+ if (editor) {
88
+ EditorHandler.clearHighlight(editor);
89
+ }
90
+ });
91
+
92
+ this._breakpointEffect = StateEffect.define<{ pos: number[] }>({
93
+ map: (value, mapping) => ({ pos: value.pos.map(v => mapping.mapPos(v)) })
94
+ });
95
+
96
+ this._breakpointState = StateField.define<RangeSet<GutterMarker>>({
97
+ create: () => {
98
+ return RangeSet.empty;
99
+ },
100
+ update: (breakpoints, transaction) => {
101
+ breakpoints = breakpoints.map(transaction.changes);
102
+ for (let ef of transaction.effects) {
103
+ if (ef.is(this._breakpointEffect)) {
104
+ let e = ef as StateEffect<{ pos: number[] }>;
105
+ if (e.value.pos.length) {
106
+ breakpoints = breakpoints.update({
107
+ add: e.value.pos.map(v => Private.breakpointMarker.range(v)),
108
+ sort: true
109
+ });
110
+ } else {
111
+ breakpoints = RangeSet.empty;
112
+ }
113
+ }
114
+ }
115
+ return breakpoints;
116
+ }
117
+ });
118
+
119
+ this._gutter = new Compartment();
120
+
121
+ this._highlightDeco = Decoration.line({ class: LINE_HIGHLIGHT_CLASS });
122
+
123
+ this._highlightState = StateField.define<DecorationSet>({
124
+ create: () => {
125
+ return Decoration.none;
126
+ },
127
+ update: (highlights, transaction) => {
128
+ highlights = highlights.map(transaction.changes);
129
+ for (let ef of transaction.effects) {
130
+ if (ef.is(EditorHandler._highlightEffect)) {
131
+ let e = ef as StateEffect<{ pos: number[] }>;
132
+ if (e.value.pos.length) {
133
+ highlights = highlights.update({
134
+ add: e.value.pos.map(v => this._highlightDeco.range(v))
135
+ });
136
+ } else {
137
+ highlights = Decoration.none;
138
+ }
139
+ }
140
+ }
141
+ return highlights;
142
+ },
143
+ provide: f => EditorView.decorations.from(f)
144
+ });
145
+
146
+ void options.editorReady().then(() => {
147
+ this._setupEditor();
148
+ });
149
+ }
150
+
151
+ /**
152
+ * The editor
153
+ */
154
+ get editor(): CodeEditor.IEditor | null {
155
+ return this._editor();
156
+ }
157
+
158
+ /**
159
+ * Whether the handler is disposed.
160
+ */
161
+ isDisposed: boolean;
162
+
163
+ /**
164
+ * Dispose the handler.
165
+ */
166
+ dispose(): void {
167
+ if (this.isDisposed) {
168
+ return;
169
+ }
170
+ this._editorMonitor.dispose();
171
+ this._clearEditor();
172
+ this.isDisposed = true;
173
+ Signal.clearData(this);
174
+ }
175
+
176
+ /**
177
+ * Refresh the breakpoints display
178
+ */
179
+ refreshBreakpoints(): void {
180
+ this._addBreakpointsToEditor();
181
+ }
182
+
183
+ /**
184
+ * Setup the editor.
185
+ */
186
+ private _setupEditor(): void {
187
+ const editor = this.editor;
188
+ if (!editor || editor.isDisposed) {
189
+ return;
190
+ }
191
+
192
+ editor.setOption('lineNumbers', true);
193
+ const breakpointGutter = [
194
+ this._breakpointState,
195
+ this._highlightState,
196
+ Prec.highest(
197
+ gutter({
198
+ class: 'cm-breakpoint-gutter',
199
+ renderEmptyElements: true,
200
+ markers: v => v.state.field(this._breakpointState),
201
+ initialSpacer: () => Private.breakpointMarker,
202
+ domEventHandlers: {
203
+ mousedown: (view, line): boolean => {
204
+ this._onGutterClick(view, line.from);
205
+ return true;
206
+ }
207
+ }
208
+ })
209
+ )
210
+ ];
211
+ editor.injectExtension(this._gutter.of(breakpointGutter));
212
+
213
+ this._addBreakpointsToEditor();
214
+ }
215
+
216
+ /**
217
+ * Clear the editor by removing visual elements and handlers.
218
+ */
219
+ private _clearEditor(): void {
220
+ const editor = this.editor as CodeMirrorEditor | null;
221
+ if (!editor || editor.isDisposed) {
222
+ return;
223
+ }
224
+
225
+ EditorHandler.clearHighlight(editor);
226
+ this._clearGutter(editor);
227
+ editor.setOption('lineNumbers', false);
228
+ editor.editor.dispatch({
229
+ effects: this._gutter.reconfigure([])
230
+ });
231
+ }
232
+
233
+ /**
234
+ * Send the breakpoints from the editor UI via the debug service.
235
+ */
236
+ private _sendEditorBreakpoints(): void {
237
+ if (this.editor?.isDisposed) {
238
+ return;
239
+ }
240
+
241
+ const breakpoints = this._getBreakpointsFromEditor().map(lineNumber => {
242
+ return Private.createBreakpoint(
243
+ this._debuggerService.session?.connection?.name || '',
244
+ lineNumber
245
+ );
246
+ });
247
+
248
+ void this._debuggerService.updateBreakpoints(
249
+ this._src.getSource(),
250
+ breakpoints,
251
+ this._path
252
+ );
253
+ }
254
+
255
+ /**
256
+ * Handle a click on the gutter.
257
+ *
258
+ * @param editor The editor from where the click originated.
259
+ * @param position The position corresponding to the click event.
260
+ */
261
+ private _onGutterClick(editor: EditorView, position: number): void {
262
+ if (this._id !== this._debuggerService.session?.connection?.id) {
263
+ return;
264
+ }
265
+
266
+ const lineNumber = editor.state.doc.lineAt(position).number;
267
+ let stateBreakpoints = editor.state.field(this._breakpointState);
268
+ let hasBreakpoint = false;
269
+ stateBreakpoints.between(position, position, () => {
270
+ hasBreakpoint = true;
271
+ });
272
+ let breakpoints: IDebugger.IBreakpoint[] = this._getBreakpoints();
273
+ if (hasBreakpoint) {
274
+ breakpoints = breakpoints.filter(ele => ele.line !== lineNumber);
275
+ } else {
276
+ breakpoints.push(
277
+ Private.createBreakpoint(
278
+ this._path ?? this._debuggerService.session.connection.name,
279
+ lineNumber
280
+ )
281
+ );
282
+ }
283
+
284
+ breakpoints.sort((a, b) => {
285
+ return a.line! - b.line!;
286
+ });
287
+
288
+ void this._debuggerService.updateBreakpoints(
289
+ this._src.getSource(),
290
+ breakpoints,
291
+ this._path
292
+ );
293
+ }
294
+
295
+ /**
296
+ * Add the breakpoints to the editor.
297
+ */
298
+ private _addBreakpointsToEditor(): void {
299
+ if (this._id !== this._debuggerService.session?.connection?.id) {
300
+ return;
301
+ }
302
+
303
+ const editor = this.editor as CodeMirrorEditor;
304
+ const breakpoints = this._getBreakpoints();
305
+
306
+ this._clearGutter(editor);
307
+ const breakpointPos = breakpoints.map(b => {
308
+ return editor.state.doc.line(b.line!).from;
309
+ });
310
+
311
+ editor.editor.dispatch({
312
+ effects: this._breakpointEffect.of({ pos: breakpointPos })
313
+ });
314
+ }
315
+
316
+ /**
317
+ * Retrieve the breakpoints from the editor.
318
+ */
319
+ private _getBreakpointsFromEditor(): number[] {
320
+ const editor = this.editor as CodeMirrorEditor;
321
+ const breakpoints = editor.editor.state.field(this._breakpointState);
322
+ let lines: number[] = [];
323
+ breakpoints.between(0, editor.doc.length, (from: number) => {
324
+ lines.push(editor.doc.lineAt(from).number);
325
+ });
326
+
327
+ return lines;
328
+ }
329
+
330
+ private _clearGutter(editor: CodeEditor.IEditor): void {
331
+ if (!editor) {
332
+ return;
333
+ }
334
+
335
+ const view = (editor as CodeMirrorEditor).editor;
336
+ view.dispatch({
337
+ effects: this._breakpointEffect.of({ pos: [] })
338
+ });
339
+ }
340
+
341
+ /**
342
+ * Get the breakpoints for the editor using its content (code),
343
+ * or its path (if it exists).
344
+ */
345
+ private _getBreakpoints(): IDebugger.IBreakpoint[] {
346
+ const code = this._src.getSource();
347
+ return this._debuggerService.model.breakpoints.getBreakpoints(
348
+ this._path || this._debuggerService.getCodeId(code)
349
+ );
350
+ }
351
+
352
+ private _id: string;
353
+ private _debuggerService: IDebugger;
354
+ private _editor: () => CodeEditor.IEditor | null;
355
+ private _breakpointEffect: StateEffectType<{ pos: number[] }>;
356
+ private _breakpointState: StateField<RangeSet<GutterMarker>>;
357
+ private _gutter: Compartment;
358
+ private _highlightDeco: Decoration;
359
+ private _highlightState: StateField<DecorationSet>;
360
+ private _editorMonitor: ActivityMonitor<ISharedText, SourceChange>;
361
+ private _path: string;
362
+ private _src: ISharedText;
363
+ }
364
+
365
+ /**
366
+ * A namespace for EditorHandler `statics`.
367
+ */
368
+ export namespace EditorHandler {
369
+ /**
370
+ * Instantiation options for `EditorHandler`.
371
+ */
372
+ export interface IOptions {
373
+ /**
374
+ * The debugger service.
375
+ */
376
+ debuggerService: IDebugger;
377
+
378
+ /**
379
+ * Promise resolving when the editor is ready.
380
+ */
381
+ editorReady(): Promise<CodeEditor.IEditor>;
382
+
383
+ /**
384
+ * Get the code editor to handle.
385
+ */
386
+ getEditor(): CodeEditor.IEditor | null;
387
+
388
+ /**
389
+ * An optional path to a source file.
390
+ */
391
+ path?: string;
392
+
393
+ /**
394
+ * The code source to debug
395
+ */
396
+ src: ISharedText;
397
+ }
398
+
399
+ export const _highlightEffect = StateEffect.define<{ pos: number[] }>({
400
+ map: (value, mapping) => ({ pos: value.pos.map(v => mapping.mapPos(v)) })
401
+ });
402
+
403
+ /**
404
+ * Highlight the current line of the frame in the given editor.
405
+ *
406
+ * @param editor The editor to highlight.
407
+ * @param line The line number.
408
+ */
409
+ export function showCurrentLine(
410
+ editor: CodeEditor.IEditor,
411
+ line: number
412
+ ): void {
413
+ clearHighlight(editor);
414
+ const cmEditor = editor as CodeMirrorEditor;
415
+ const linePos = cmEditor.doc.line(line).from;
416
+ cmEditor.editor.dispatch({
417
+ effects: _highlightEffect.of({ pos: [linePos] })
418
+ });
419
+ }
420
+
421
+ /**
422
+ * Remove all line highlighting indicators for the given editor.
423
+ *
424
+ * @param editor The editor to cleanup.
425
+ */
426
+ export function clearHighlight(editor: CodeEditor.IEditor): void {
427
+ if (!editor || editor.isDisposed) {
428
+ return;
429
+ }
430
+ const cmEditor = editor as CodeMirrorEditor;
431
+ cmEditor.editor.dispatch({
432
+ effects: _highlightEffect.of({ pos: [] })
433
+ });
434
+ }
435
+ }
436
+
437
+ /**
438
+ * A namespace for module private data.
439
+ */
440
+ namespace Private {
441
+ /**
442
+ * Create a marker DOM element for a breakpoint.
443
+ */
444
+ export const breakpointMarker = new (class extends GutterMarker {
445
+ toDOM() {
446
+ const marker = document.createTextNode('●');
447
+ return marker;
448
+ }
449
+ })();
450
+
451
+ /**
452
+ * Create a new breakpoint.
453
+ *
454
+ * @param session The name of the session.
455
+ * @param line The line number of the breakpoint.
456
+ */
457
+ export function createBreakpoint(
458
+ session: string,
459
+ line: number
460
+ ): IDebugger.IBreakpoint {
461
+ return {
462
+ line,
463
+ verified: true,
464
+ source: {
465
+ name: session
466
+ }
467
+ };
468
+ }
469
+ }
@@ -0,0 +1,86 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { DocumentWidget } from '@jupyterlab/docregistry';
5
+
6
+ import { FileEditor } from '@jupyterlab/fileeditor';
7
+
8
+ import { IDisposable } from '@lumino/disposable';
9
+
10
+ import { Signal } from '@lumino/signaling';
11
+
12
+ import { EditorHandler } from '../handlers/editor';
13
+
14
+ import { IDebugger } from '../tokens';
15
+
16
+ /**
17
+ * A handler for files.
18
+ */
19
+ export class FileHandler implements IDisposable {
20
+ /**
21
+ * Instantiate a new FileHandler.
22
+ *
23
+ * @param options The instantiation options for a FileHandler.
24
+ */
25
+ constructor(options: FileHandler.IOptions) {
26
+ this._debuggerService = options.debuggerService;
27
+ this._fileEditor = options.widget.content;
28
+
29
+ this._hasLineNumber =
30
+ (this._fileEditor.editor.getOption('lineNumbers') as
31
+ | boolean
32
+ | undefined) ?? false;
33
+ this._editorHandler = new EditorHandler({
34
+ debuggerService: this._debuggerService,
35
+ editorReady: () => Promise.resolve(this._fileEditor.editor),
36
+ getEditor: () => this._fileEditor.editor,
37
+ src: this._fileEditor.model.sharedModel
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Whether the handler is disposed.
43
+ */
44
+ isDisposed: boolean;
45
+
46
+ /**
47
+ * Dispose the handler.
48
+ */
49
+ dispose(): void {
50
+ if (this.isDisposed) {
51
+ return;
52
+ }
53
+ this.isDisposed = true;
54
+ this._editorHandler?.dispose();
55
+ // Restore editor options
56
+ this._editorHandler?.editor!.setOptions({
57
+ lineNumbers: this._hasLineNumber
58
+ });
59
+ Signal.clearData(this);
60
+ }
61
+
62
+ private _fileEditor: FileEditor;
63
+ private _debuggerService: IDebugger;
64
+ private _editorHandler: EditorHandler;
65
+ private _hasLineNumber: boolean;
66
+ }
67
+
68
+ /**
69
+ * A namespace for FileHandler `statics`.
70
+ */
71
+ export namespace FileHandler {
72
+ /**
73
+ * Instantiation options for `FileHandler`.
74
+ */
75
+ export interface IOptions {
76
+ /**
77
+ * The debugger service.
78
+ */
79
+ debuggerService: IDebugger;
80
+
81
+ /**
82
+ * The widget to handle.
83
+ */
84
+ widget: DocumentWidget<FileEditor>;
85
+ }
86
+ }
@@ -0,0 +1,128 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { Cell, CodeCell, ICellModel } from '@jupyterlab/cells';
5
+ import { NotebookPanel } from '@jupyterlab/notebook';
6
+ import {
7
+ IObservableList,
8
+ IObservableMap,
9
+ ObservableMap
10
+ } from '@jupyterlab/observables';
11
+ import { IDisposable } from '@lumino/disposable';
12
+ import { Signal } from '@lumino/signaling';
13
+ import { IDebugger } from '../tokens';
14
+ import { EditorHandler } from './editor';
15
+
16
+ /**
17
+ * A handler for notebooks.
18
+ */
19
+ export class NotebookHandler implements IDisposable {
20
+ /**
21
+ * Instantiate a new NotebookHandler.
22
+ *
23
+ * @param options The instantiation options for a NotebookHandler.
24
+ */
25
+ constructor(options: NotebookHandler.IOptions) {
26
+ this._debuggerService = options.debuggerService;
27
+ this._notebookPanel = options.widget;
28
+ this._cellMap = new ObservableMap<EditorHandler>();
29
+
30
+ const notebook = this._notebookPanel.content;
31
+ notebook.model!.cells.changed.connect(this._onCellsChanged, this);
32
+
33
+ this._onCellsChanged();
34
+ }
35
+
36
+ /**
37
+ * Whether the handler is disposed.
38
+ */
39
+ isDisposed: boolean;
40
+
41
+ /**
42
+ * Dispose the handler.
43
+ */
44
+ dispose(): void {
45
+ if (this.isDisposed) {
46
+ return;
47
+ }
48
+ this.isDisposed = true;
49
+ this._cellMap.values().forEach(handler => {
50
+ handler.dispose();
51
+ // Ensure to restore notebook editor settings
52
+ handler.editor?.setOptions({
53
+ ...this._notebookPanel.content.editorConfig.code
54
+ });
55
+ });
56
+ this._cellMap.dispose();
57
+ Signal.clearData(this);
58
+ }
59
+
60
+ /**
61
+ * Handle a notebook cells changed event.
62
+ */
63
+ private _onCellsChanged(
64
+ cells?: any,
65
+ changes?: IObservableList.IChangedArgs<ICellModel>
66
+ ): void {
67
+ this._notebookPanel.content.widgets.forEach(cell =>
68
+ this._addEditorHandler(cell)
69
+ );
70
+
71
+ if (changes?.type === 'move') {
72
+ for (const cell of changes.newValues) {
73
+ this._cellMap.get(cell.id)?.refreshBreakpoints();
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Add a new editor handler for the given cell.
80
+ *
81
+ * @param cell The cell to add the handler to.
82
+ */
83
+ private _addEditorHandler(cell: Cell): void {
84
+ const modelId = cell.model.id;
85
+ if (cell.model.type !== 'code' || this._cellMap.has(modelId)) {
86
+ return;
87
+ }
88
+ const codeCell = cell as CodeCell;
89
+ const editorHandler = new EditorHandler({
90
+ debuggerService: this._debuggerService,
91
+ editorReady: async () => {
92
+ await codeCell.ready;
93
+ return codeCell.editor!;
94
+ },
95
+ getEditor: () => codeCell.editor,
96
+ src: cell.model.sharedModel
97
+ });
98
+ codeCell.disposed.connect(() => {
99
+ this._cellMap.delete(modelId);
100
+ editorHandler.dispose();
101
+ });
102
+ this._cellMap.set(cell.model.id, editorHandler);
103
+ }
104
+
105
+ private _debuggerService: IDebugger;
106
+ private _notebookPanel: NotebookPanel;
107
+ private _cellMap: IObservableMap<EditorHandler>;
108
+ }
109
+
110
+ /**
111
+ * A namespace for NotebookHandler statics.
112
+ */
113
+ export namespace NotebookHandler {
114
+ /**
115
+ * Instantiation options for `NotebookHandler`.
116
+ */
117
+ export interface IOptions {
118
+ /**
119
+ * The debugger service.
120
+ */
121
+ debuggerService: IDebugger;
122
+
123
+ /**
124
+ * The widget to handle.
125
+ */
126
+ widget: NotebookPanel;
127
+ }
128
+ }