@jupyterlab/debugger 4.5.0-beta.0 → 4.5.0-rc.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 (67) hide show
  1. package/lib/displayregistry.d.ts +31 -0
  2. package/lib/displayregistry.js +39 -0
  3. package/lib/displayregistry.js.map +1 -0
  4. package/lib/handler.d.ts +11 -0
  5. package/lib/handler.js +13 -4
  6. package/lib/handler.js.map +1 -1
  7. package/lib/handlers/console.d.ts +7 -0
  8. package/lib/handlers/console.js +32 -0
  9. package/lib/handlers/console.js.map +1 -1
  10. package/lib/handlers/file.d.ts +7 -0
  11. package/lib/handlers/file.js +30 -1
  12. package/lib/handlers/file.js.map +1 -1
  13. package/lib/handlers/notebook.d.ts +7 -0
  14. package/lib/handlers/notebook.js +30 -0
  15. package/lib/handlers/notebook.js.map +1 -1
  16. package/lib/handlers/pausedoverlay.d.ts +37 -0
  17. package/lib/handlers/pausedoverlay.js +80 -0
  18. package/lib/handlers/pausedoverlay.js.map +1 -0
  19. package/lib/index.d.ts +2 -1
  20. package/lib/index.js +2 -1
  21. package/lib/index.js.map +1 -1
  22. package/lib/model.d.ts +16 -1
  23. package/lib/model.js +10 -4
  24. package/lib/model.js.map +1 -1
  25. package/lib/panels/breakpoints/body.d.ts +0 -3
  26. package/lib/panels/breakpoints/body.js +8 -12
  27. package/lib/panels/breakpoints/body.js.map +1 -1
  28. package/lib/panels/breakpoints/model.d.ts +24 -1
  29. package/lib/panels/breakpoints/model.js +12 -1
  30. package/lib/panels/breakpoints/model.js.map +1 -1
  31. package/lib/panels/callstack/body.js +3 -11
  32. package/lib/panels/callstack/body.js.map +1 -1
  33. package/lib/panels/callstack/model.d.ts +33 -1
  34. package/lib/panels/callstack/model.js +15 -1
  35. package/lib/panels/callstack/model.js.map +1 -1
  36. package/lib/panels/sources/body.js +7 -6
  37. package/lib/panels/sources/body.js.map +1 -1
  38. package/lib/panels/sources/model.d.ts +15 -1
  39. package/lib/panels/sources/model.js +19 -0
  40. package/lib/panels/sources/model.js.map +1 -1
  41. package/lib/service.d.ts +11 -0
  42. package/lib/service.js +35 -13
  43. package/lib/service.js.map +1 -1
  44. package/lib/sidebar.d.ts +1 -1
  45. package/lib/tokens.d.ts +39 -1
  46. package/lib/tokens.js +4 -0
  47. package/lib/tokens.js.map +1 -1
  48. package/package.json +18 -17
  49. package/src/displayregistry.ts +59 -0
  50. package/src/handler.ts +26 -4
  51. package/src/handlers/console.ts +43 -0
  52. package/src/handlers/file.ts +41 -0
  53. package/src/handlers/notebook.ts +40 -0
  54. package/src/handlers/pausedoverlay.ts +112 -0
  55. package/src/index.ts +4 -1
  56. package/src/model.ts +27 -4
  57. package/src/panels/breakpoints/body.tsx +9 -12
  58. package/src/panels/breakpoints/index.ts +1 -0
  59. package/src/panels/breakpoints/model.ts +33 -2
  60. package/src/panels/callstack/body.tsx +2 -10
  61. package/src/panels/callstack/model.ts +49 -2
  62. package/src/panels/sources/body.ts +7 -5
  63. package/src/panels/sources/model.ts +28 -1
  64. package/src/service.ts +57 -11
  65. package/src/sidebar.ts +1 -1
  66. package/src/tokens.ts +49 -1
  67. package/style/base.css +36 -1
@@ -12,6 +12,8 @@ import { Signal } from '@lumino/signaling';
12
12
  import { EditorHandler } from '../handlers/editor';
13
13
 
14
14
  import { IDebugger } from '../tokens';
15
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
16
+ import { DebuggerPausedOverlay } from './pausedoverlay';
15
17
 
16
18
  /**
17
19
  * A handler for files.
@@ -25,6 +27,7 @@ export class FileHandler implements IDisposable {
25
27
  constructor(options: FileHandler.IOptions) {
26
28
  this._debuggerService = options.debuggerService;
27
29
  this._fileEditor = options.widget.content;
30
+ this.translator = options.translator || nullTranslator;
28
31
 
29
32
  this._hasLineNumber =
30
33
  (this._fileEditor.editor.getOption('lineNumbers') as
@@ -36,6 +39,34 @@ export class FileHandler implements IDisposable {
36
39
  getEditor: () => this._fileEditor.editor,
37
40
  src: this._fileEditor.model.sharedModel
38
41
  });
42
+
43
+ this._pausedOverlay = new DebuggerPausedOverlay({
44
+ debuggerService: this._debuggerService,
45
+ container: options.widget.node,
46
+ translator: this.translator
47
+ });
48
+
49
+ this._debuggerService.session?.eventMessage.connect((_, event) => {
50
+ const session = this._debuggerService.session;
51
+ const contextSession = options.widget.context.sessionContext.session;
52
+
53
+ if (!session || !contextSession) {
54
+ return;
55
+ }
56
+ if (session.connection?.kernel?.id !== contextSession.kernel?.id) {
57
+ return;
58
+ }
59
+
60
+ if (event.event === 'stopped') {
61
+ void this._pausedOverlay.show();
62
+ } else if (event.event === 'continued') {
63
+ void this._pausedOverlay.hide();
64
+ }
65
+ });
66
+
67
+ if (this._debuggerService.hasStoppedThreads()) {
68
+ void this._pausedOverlay.show();
69
+ }
39
70
  }
40
71
 
41
72
  /**
@@ -51,6 +82,9 @@ export class FileHandler implements IDisposable {
51
82
  return;
52
83
  }
53
84
  this.isDisposed = true;
85
+
86
+ this._pausedOverlay.dispose();
87
+
54
88
  this._editorHandler?.dispose();
55
89
  // Restore editor options
56
90
  this._editorHandler?.editor!.setOptions({
@@ -62,7 +96,9 @@ export class FileHandler implements IDisposable {
62
96
  private _fileEditor: FileEditor;
63
97
  private _debuggerService: IDebugger;
64
98
  private _editorHandler: EditorHandler;
99
+ private _pausedOverlay: DebuggerPausedOverlay;
65
100
  private _hasLineNumber: boolean;
101
+ protected translator: ITranslator;
66
102
  }
67
103
 
68
104
  /**
@@ -82,5 +118,10 @@ export namespace FileHandler {
82
118
  * The widget to handle.
83
119
  */
84
120
  widget: DocumentWidget<FileEditor>;
121
+
122
+ /**
123
+ * The application language translator.
124
+ */
125
+ translator?: ITranslator;
85
126
  }
86
127
  }
@@ -12,6 +12,8 @@ import { IDisposable } from '@lumino/disposable';
12
12
  import { Signal } from '@lumino/signaling';
13
13
  import { IDebugger } from '../tokens';
14
14
  import { EditorHandler } from './editor';
15
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
16
+ import { DebuggerPausedOverlay } from './pausedoverlay';
15
17
 
16
18
  /**
17
19
  * A handler for notebooks.
@@ -26,10 +28,38 @@ export class NotebookHandler implements IDisposable {
26
28
  this._debuggerService = options.debuggerService;
27
29
  this._notebookPanel = options.widget;
28
30
  this._cellMap = new ObservableMap<EditorHandler>();
31
+ this.translator = options.translator || nullTranslator;
32
+ this._pausedOverlay = new DebuggerPausedOverlay({
33
+ debuggerService: this._debuggerService,
34
+ container: this._notebookPanel.node,
35
+ translator: this.translator
36
+ });
29
37
 
30
38
  const notebook = this._notebookPanel.content;
31
39
  notebook.model!.cells.changed.connect(this._onCellsChanged, this);
32
40
 
41
+ this._debuggerService.session?.eventMessage.connect((_, event) => {
42
+ const session = this._debuggerService.session;
43
+ const contextSession = this._notebookPanel.sessionContext.session;
44
+
45
+ if (!session || !contextSession) {
46
+ return;
47
+ }
48
+ if (session.connection?.kernel?.id !== contextSession.kernel?.id) {
49
+ return;
50
+ }
51
+
52
+ if (event.event === 'stopped') {
53
+ void this._pausedOverlay.show();
54
+ } else if (event.event === 'continued') {
55
+ void this._pausedOverlay.hide();
56
+ }
57
+ });
58
+
59
+ if (this._debuggerService.hasStoppedThreads() === true) {
60
+ void this._pausedOverlay.show();
61
+ }
62
+
33
63
  this._onCellsChanged();
34
64
  }
35
65
 
@@ -46,6 +76,9 @@ export class NotebookHandler implements IDisposable {
46
76
  return;
47
77
  }
48
78
  this.isDisposed = true;
79
+
80
+ this._pausedOverlay.dispose();
81
+
49
82
  this._cellMap.values().forEach(handler => {
50
83
  handler.dispose();
51
84
  // Ensure to restore notebook editor settings
@@ -105,6 +138,8 @@ export class NotebookHandler implements IDisposable {
105
138
  private _debuggerService: IDebugger;
106
139
  private _notebookPanel: NotebookPanel;
107
140
  private _cellMap: IObservableMap<EditorHandler>;
141
+ private _pausedOverlay: DebuggerPausedOverlay;
142
+ protected translator: ITranslator;
108
143
  }
109
144
 
110
145
  /**
@@ -124,5 +159,10 @@ export namespace NotebookHandler {
124
159
  * The widget to handle.
125
160
  */
126
161
  widget: NotebookPanel;
162
+
163
+ /**
164
+ * The application language translator.
165
+ */
166
+ translator?: ITranslator;
127
167
  }
128
168
  }
@@ -0,0 +1,112 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { IDisposable } from '@lumino/disposable';
5
+ import { IDebugger } from '../tokens';
6
+ import {
7
+ ITranslator,
8
+ nullTranslator,
9
+ TranslationBundle
10
+ } from '@jupyterlab/translation';
11
+ import { runIcon, stepOverIcon } from '@jupyterlab/ui-components';
12
+
13
+ /**
14
+ * A reusable helper to show a "Paused in Debugger" overlay and block interactions.
15
+ */
16
+ export class DebuggerPausedOverlay implements IDisposable {
17
+ constructor(options: DebuggerPausedOverlay.IOptions) {
18
+ this._debuggerService = options.debuggerService;
19
+ this._container = options.container;
20
+ this._trans = (options.translator || nullTranslator).load('jupyterlab');
21
+
22
+ // Create overlay
23
+ const overlay = document.createElement('div');
24
+ overlay.className = 'jp-DebuggerPausedOverlay';
25
+
26
+ const text = document.createElement('span');
27
+ text.textContent = this._trans.__('Paused in Debugger');
28
+ overlay.appendChild(text);
29
+
30
+ const continueBtn = document.createElement('button');
31
+ continueBtn.className = 'jp-DebuggerPausedButton';
32
+ continueBtn.title = this._trans.__('Continue');
33
+ runIcon.element({ container: continueBtn, elementPosition: 'center' });
34
+ continueBtn.onclick = () => {
35
+ void this._debuggerService.continue();
36
+ };
37
+
38
+ const nextBtn = document.createElement('button');
39
+ nextBtn.className = 'jp-DebuggerPausedButton';
40
+ nextBtn.title = this._trans.__('Next');
41
+ stepOverIcon.element({ container: nextBtn, elementPosition: 'center' });
42
+ nextBtn.onclick = () => {
43
+ void this._debuggerService.next();
44
+ };
45
+
46
+ overlay.appendChild(continueBtn);
47
+ overlay.appendChild(nextBtn);
48
+
49
+ overlay.style.pointerEvents = 'auto';
50
+ this._overlay = overlay;
51
+ }
52
+
53
+ /**
54
+ * Show the overlay, if enabled by user settings.
55
+ */
56
+ show(): void {
57
+ if (this._isDisposed) {
58
+ return;
59
+ }
60
+
61
+ const showOverlay = document.body.dataset.showPausedOverlay !== 'false';
62
+ if (!showOverlay || !this._overlay || this._overlay.isConnected) {
63
+ return;
64
+ }
65
+
66
+ this._container.appendChild(this._overlay);
67
+ }
68
+
69
+ /**
70
+ * Hide and unmount the overlay.
71
+ */
72
+ hide(): void {
73
+ if (this._isDisposed || !this._overlay || !this._overlay.isConnected) {
74
+ return;
75
+ }
76
+ this._container.style.pointerEvents = '';
77
+ this._overlay.remove();
78
+ }
79
+
80
+ /**
81
+ * Dispose of the overlay completely.
82
+ */
83
+ dispose(): void {
84
+ if (this._isDisposed) {
85
+ return;
86
+ }
87
+ this._isDisposed = true;
88
+ this.hide();
89
+ this._overlay = null;
90
+ }
91
+
92
+ /**
93
+ * Whether the overlay has been disposed.
94
+ */
95
+ get isDisposed(): boolean {
96
+ return this._isDisposed;
97
+ }
98
+
99
+ private _debuggerService: IDebugger;
100
+ private _container: HTMLElement;
101
+ private _trans: TranslationBundle;
102
+ private _overlay: HTMLDivElement | null;
103
+ private _isDisposed = false;
104
+ }
105
+
106
+ export namespace DebuggerPausedOverlay {
107
+ export interface IOptions {
108
+ debuggerService: IDebugger;
109
+ container: HTMLElement;
110
+ translator?: ITranslator;
111
+ }
112
+ }
package/src/index.ts CHANGED
@@ -7,11 +7,14 @@
7
7
 
8
8
  export { Debugger } from './debugger';
9
9
 
10
+ export { DebuggerDisplayRegistry } from './displayregistry';
11
+
10
12
  export {
11
13
  IDebugger,
12
14
  IDebuggerConfig,
13
15
  IDebuggerSources,
14
16
  IDebuggerSidebar,
15
17
  IDebuggerHandler,
16
- IDebuggerSourceViewer
18
+ IDebuggerSourceViewer,
19
+ IDebuggerDisplayRegistry
17
20
  } from './tokens';
package/src/model.ts CHANGED
@@ -14,6 +14,8 @@ import { SourcesModel } from './panels/sources/model';
14
14
  import { KernelSourcesModel } from './panels/kernelSources/model';
15
15
 
16
16
  import { VariablesModel } from './panels/variables/model';
17
+ import { IDebuggerDisplayRegistry } from './tokens';
18
+ import { DebuggerDisplayRegistry } from './displayregistry';
17
19
 
18
20
  /**
19
21
  * A model for a debugger.
@@ -22,12 +24,18 @@ export class DebuggerModel implements IDebugger.Model.IService {
22
24
  /**
23
25
  * Instantiate a new DebuggerModel
24
26
  */
25
- constructor() {
26
- this.breakpoints = new BreakpointsModel();
27
- this.callstack = new CallstackModel();
27
+ constructor(options: DebuggerModel.IOptions) {
28
+ const displayRegistry =
29
+ options.displayRegistry ?? new DebuggerDisplayRegistry();
30
+
31
+ this.breakpoints = new BreakpointsModel({ displayRegistry });
32
+ this.callstack = new CallstackModel({
33
+ displayRegistry
34
+ });
28
35
  this.variables = new VariablesModel();
29
36
  this.sources = new SourcesModel({
30
- currentFrameChanged: this.callstack.currentFrameChanged
37
+ currentFrameChanged: this.callstack.currentFrameChanged,
38
+ displayRegistry
31
39
  });
32
40
  this.kernelSources = new KernelSourcesModel();
33
41
  }
@@ -164,3 +172,18 @@ export class DebuggerModel implements IDebugger.Model.IService {
164
172
  private _title = '-';
165
173
  private _titleChanged = new Signal<this, string>(this);
166
174
  }
175
+
176
+ /**
177
+ * A namespace for DebuggerModel
178
+ */
179
+ export namespace DebuggerModel {
180
+ /**
181
+ * Instantiation options for a DebuggerModel.
182
+ */
183
+ export interface IOptions {
184
+ /**
185
+ * The display registry.
186
+ */
187
+ displayRegistry?: IDebuggerDisplayRegistry | null;
188
+ }
189
+ }
@@ -34,9 +34,6 @@ export class BreakpointsBody extends ReactWidget {
34
34
  this.addClass('jp-DebuggerBreakpoints-body');
35
35
  }
36
36
 
37
- /**
38
- * Render the BreakpointsComponent.
39
- */
40
37
  render(): JSX.Element {
41
38
  return (
42
39
  <BreakpointsComponent model={this._model} translator={this._translator} />
@@ -52,6 +49,7 @@ export class BreakpointsBody extends ReactWidget {
52
49
  *
53
50
  * @param {object} props The component props.
54
51
  * @param props.model The model for the breakpoints.
52
+ * @returns A JSX element.
55
53
  */
56
54
  const BreakpointsComponent = ({
57
55
  model,
@@ -124,6 +122,7 @@ const BreakpointsComponent = ({
124
122
  * @param {object} props The component props.
125
123
  * @param props.breakpoints The list of breakpoints.
126
124
  * @param props.model The model for the breakpoints.
125
+ * @returns A JSX element.
127
126
  */
128
127
  const BreakpointCellComponent = ({
129
128
  breakpoints,
@@ -164,6 +163,7 @@ const BreakpointCellComponent = ({
164
163
  * @param {object} props The component props.
165
164
  * @param props.breakpoint The breakpoint.
166
165
  * @param props.model The model for the breakpoints.
166
+ * @returns A JSX element.
167
167
  */
168
168
  const BreakpointComponent = ({
169
169
  breakpoint,
@@ -176,15 +176,12 @@ const BreakpointComponent = ({
176
176
  isSelected: boolean;
177
177
  trans: TranslationBundle;
178
178
  }): JSX.Element => {
179
- const moveToEndFirstCharIfSlash = (breakpointSourcePath: string): string => {
180
- return breakpointSourcePath[0] === '/'
181
- ? breakpointSourcePath.slice(1) + '/'
182
- : breakpointSourcePath;
183
- };
179
+ const display = model.getDisplayName(breakpoint);
180
+
184
181
  return (
185
182
  <div
186
- className={'jp-DebuggerBreakpoint'}
187
- onClick={(): void => model.clicked.emit(breakpoint)}
183
+ className="jp-DebuggerBreakpoint"
184
+ onClick={() => model.clicked.emit(breakpoint)}
188
185
  title={breakpoint.source?.path}
189
186
  >
190
187
  <span className="jp-DebuggerBreakpoint-container">
@@ -199,9 +196,9 @@ const BreakpointComponent = ({
199
196
  )}
200
197
  </span>
201
198
  <span className={'jp-DebuggerBreakpoint-source jp-left-truncated'}>
202
- {moveToEndFirstCharIfSlash(breakpoint.source?.path ?? '')}
199
+ {display}
203
200
  </span>
204
- <span className={'jp-DebuggerBreakpoint-line'}>{breakpoint.line}</span>
201
+ <span className="jp-DebuggerBreakpoint-line">{breakpoint.line}</span>
205
202
  </div>
206
203
  );
207
204
  };
@@ -97,6 +97,7 @@ export namespace Breakpoints {
97
97
  */
98
98
  pauseOnExceptions: string;
99
99
  }
100
+
100
101
  /**
101
102
  * Instantiation options for `Breakpoints`.
102
103
  */
@@ -2,13 +2,18 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
 
4
4
  import { ISignal, Signal } from '@lumino/signaling';
5
-
6
- import { IDebugger } from '../../tokens';
5
+ import { IDebugger, IDebuggerDisplayRegistry } from '../../tokens';
6
+ import { DebuggerDisplayRegistry } from '../../displayregistry';
7
7
 
8
8
  /**
9
9
  * A model for a list of breakpoints.
10
10
  */
11
11
  export class BreakpointsModel implements IDebugger.Model.IBreakpoints {
12
+ constructor(options: { displayRegistry?: IDebuggerDisplayRegistry }) {
13
+ this._displayRegistry =
14
+ options.displayRegistry ?? new DebuggerDisplayRegistry();
15
+ }
16
+
12
17
  /**
13
18
  * Signal emitted when the model changes.
14
19
  */
@@ -89,10 +94,36 @@ export class BreakpointsModel implements IDebugger.Model.IBreakpoints {
89
94
  this._restored.emit();
90
95
  }
91
96
 
97
+ /**
98
+ * Get a human-readable display string for a breakpoint.
99
+ * Shows execution count if notebook cell, [*] if running, [ ] if never executed.
100
+ */
101
+ getDisplayName(breakpoint: IDebugger.IBreakpoint): string {
102
+ return this._displayRegistry.getDisplayName(
103
+ breakpoint.source as IDebugger.Source
104
+ );
105
+ }
106
+
92
107
  private _breakpoints = new Map<string, IDebugger.IBreakpoint[]>();
93
108
  private _changed = new Signal<this, IDebugger.IBreakpoint[]>(this);
94
109
  private _restored = new Signal<this, void>(this);
95
110
  private _clicked = new Signal<this, IDebugger.IBreakpoint>(this);
96
111
  private _selectedBreakpoint: IDebugger.IBreakpoint;
97
112
  private _selectedChanged = new Signal<this, IDebugger.IBreakpoint>(this);
113
+ private _displayRegistry: IDebuggerDisplayRegistry;
114
+ }
115
+
116
+ /**
117
+ * Namespace for BreakpointsModel
118
+ */
119
+ export namespace BreakpointsModel {
120
+ /**
121
+ * Instantiation options for a BreakpointsModel.
122
+ */
123
+ export interface IOptions {
124
+ /**
125
+ * The debugger display registry.
126
+ */
127
+ displayRegistry: IDebuggerDisplayRegistry;
128
+ }
98
129
  }
@@ -1,7 +1,6 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
 
4
- import { PathExt } from '@jupyterlab/coreutils';
5
4
  import { ReactWidget } from '@jupyterlab/ui-components';
6
5
  import React, { useEffect, useState } from 'react';
7
6
  import { IDebugger } from '../../tokens';
@@ -36,6 +35,7 @@ export class CallstackBody extends ReactWidget {
36
35
  *
37
36
  * @param {object} props The component props.
38
37
  * @param props.model The model for the callstack.
38
+ * @returns A JSX element.
39
39
  */
40
40
  const FramesComponent = ({
41
41
  model
@@ -62,14 +62,6 @@ const FramesComponent = ({
62
62
  };
63
63
  }, [model]);
64
64
 
65
- const toShortLocation = (el: IDebugger.IStackFrame) => {
66
- const path = el.source?.path || '';
67
- const base = PathExt.basename(PathExt.dirname(path));
68
- const filename = PathExt.basename(path);
69
- const shortname = PathExt.join(base, filename);
70
- return `${shortname}:${el.line}`;
71
- };
72
-
73
65
  return (
74
66
  <ul>
75
67
  {frames.map(ele => (
@@ -87,7 +79,7 @@ const FramesComponent = ({
87
79
  className={'jp-DebuggerCallstackFrame-location'}
88
80
  title={ele.source?.path}
89
81
  >
90
- {toShortLocation(ele)}
82
+ {model.getDisplayName?.(ele) ?? ele.source?.path}
91
83
  </span>
92
84
  </li>
93
85
  ))}
@@ -3,15 +3,23 @@
3
3
 
4
4
  import { ISignal, Signal } from '@lumino/signaling';
5
5
 
6
- import { IDebugger } from '../../tokens';
7
-
6
+ import { DebuggerDisplayRegistry } from '../../displayregistry';
7
+ import { IDebugger, IDebuggerDisplayRegistry } from '../../tokens';
8
+ import { INotebookTracker } from '@jupyterlab/notebook';
9
+ import { IConsoleTracker } from '@jupyterlab/console';
8
10
  /**
9
11
  * A model for a callstack.
10
12
  */
11
13
  export class CallstackModel implements IDebugger.Model.ICallstack {
14
+ constructor(options: { displayRegistry?: IDebuggerDisplayRegistry }) {
15
+ this._displayRegistry =
16
+ options.displayRegistry ?? new DebuggerDisplayRegistry();
17
+ }
18
+
12
19
  /**
13
20
  * Get all the frames.
14
21
  */
22
+
15
23
  get frames(): IDebugger.IStackFrame[] {
16
24
  return this._state;
17
25
  }
@@ -63,12 +71,51 @@ export class CallstackModel implements IDebugger.Model.ICallstack {
63
71
  return this._currentFrameChanged;
64
72
  }
65
73
 
74
+ /**
75
+ * Returns a human-readable display for a frame.
76
+ */
77
+ getDisplayName(frame: IDebugger.IStackFrame): string {
78
+ let name = this._displayRegistry.getDisplayName(
79
+ frame.source as IDebugger.Source
80
+ );
81
+ if (frame.line !== undefined) {
82
+ name += `:${frame.line}`;
83
+ }
84
+ return name;
85
+ }
86
+
66
87
  private _state: IDebugger.IStackFrame[] = [];
67
88
  private _currentFrame: IDebugger.IStackFrame | null = null;
68
89
  private _framesChanged = new Signal<this, IDebugger.IStackFrame[]>(this);
69
90
  private _currentFrameChanged = new Signal<this, IDebugger.IStackFrame | null>(
70
91
  this
71
92
  );
93
+ private _displayRegistry: IDebuggerDisplayRegistry;
94
+ }
95
+
96
+ /**
97
+ * A namespace for CallstackModel
98
+ */
99
+ export namespace CallstackModel {
100
+ /**
101
+ * Instantiation options for CallstackModel
102
+ */
103
+ export interface IOptions {
104
+ /**
105
+ * Debugger configuration.
106
+ */
107
+ config: IDebugger.IConfig;
108
+
109
+ /**
110
+ * The notebook tracker.
111
+ */
112
+ notebookTracker: INotebookTracker | null;
113
+
114
+ /**
115
+ * The console tracker.
116
+ */
117
+ consoleTracker: IConsoleTracker | null;
118
+ }
72
119
  }
73
120
 
74
121
  /**
@@ -85,10 +85,11 @@ export class SourcesBody extends Widget {
85
85
  * @param frame The current frame.
86
86
  */
87
87
  private async _showSource(frame: IDebugger.IStackFrame): Promise<void> {
88
- const path = frame.source?.path;
88
+ const displayPath = this._model.getDisplayName(frame);
89
+
89
90
  const source = await this._debuggerService.getSource({
90
91
  sourceReference: 0,
91
- path
92
+ path: frame.source?.path
92
93
  });
93
94
 
94
95
  if (!source?.content) {
@@ -102,7 +103,8 @@ export class SourcesBody extends Widget {
102
103
 
103
104
  const { content, mimeType } = source;
104
105
  const editorMimeType =
105
- mimeType || this._mimeTypeService.getMimeTypeByFilePath(path ?? '');
106
+ mimeType ||
107
+ this._mimeTypeService.getMimeTypeByFilePath(frame.source?.path ?? '');
106
108
 
107
109
  this._editor.model.sharedModel.setSource(content);
108
110
  this._editor.model.mimeType = editorMimeType;
@@ -111,14 +113,14 @@ export class SourcesBody extends Widget {
111
113
  debuggerService: this._debuggerService,
112
114
  editorReady: () => Promise.resolve(this._editor.editor),
113
115
  getEditor: () => this._editor.editor,
114
- path,
116
+ path: frame.source?.path ?? '',
115
117
  src: this._editor.model.sharedModel
116
118
  });
117
119
 
118
120
  this._model.currentSource = {
119
121
  content,
120
122
  mimeType: editorMimeType,
121
- path: path ?? ''
123
+ path: displayPath
122
124
  };
123
125
 
124
126
  requestAnimationFrame(() => {
@@ -3,7 +3,8 @@
3
3
 
4
4
  import { ISignal, Signal } from '@lumino/signaling';
5
5
 
6
- import { IDebugger } from '../../tokens';
6
+ import { DebuggerDisplayRegistry } from '../../displayregistry';
7
+ import { IDebugger, IDebuggerDisplayRegistry } from '../../tokens';
7
8
 
8
9
  /**
9
10
  * The model to keep track of the current source being displayed.
@@ -16,6 +17,8 @@ export class SourcesModel implements IDebugger.Model.ISources {
16
17
  */
17
18
  constructor(options: SourcesModel.IOptions) {
18
19
  this.currentFrameChanged = options.currentFrameChanged;
20
+ this._displayRegistry =
21
+ options.displayRegistry ?? new DebuggerDisplayRegistry();
19
22
  }
20
23
 
21
24
  /**
@@ -64,6 +67,24 @@ export class SourcesModel implements IDebugger.Model.ISources {
64
67
  this._currentSourceOpened.emit(this._currentSource);
65
68
  }
66
69
 
70
+ /**
71
+ * Get a human-readable display for a frame.
72
+ *
73
+ * For notebook cells, shows execution count if available, [*] if running, or [ ] if never executed.
74
+ */
75
+ /**
76
+ * Returns a human-readable display for a frame.
77
+ */
78
+ getDisplayName(frame: IDebugger.IStackFrame): string {
79
+ let name = this._displayRegistry.getDisplayName(
80
+ frame.source as IDebugger.Source
81
+ );
82
+ if (frame.line !== undefined) {
83
+ name += `:${frame.line}`;
84
+ }
85
+ return name;
86
+ }
87
+
67
88
  private _currentSource: IDebugger.Source | null;
68
89
  private _currentSourceOpened = new Signal<
69
90
  SourcesModel,
@@ -73,6 +94,7 @@ export class SourcesModel implements IDebugger.Model.ISources {
73
94
  SourcesModel,
74
95
  IDebugger.Source | null
75
96
  >(this);
97
+ private _displayRegistry: IDebuggerDisplayRegistry;
76
98
  }
77
99
 
78
100
  /**
@@ -90,5 +112,10 @@ export namespace SourcesModel {
90
112
  IDebugger.Model.ICallstack,
91
113
  IDebugger.IStackFrame | null
92
114
  >;
115
+
116
+ /**
117
+ * The display registry.
118
+ */
119
+ displayRegistry?: IDebuggerDisplayRegistry;
93
120
  }
94
121
  }