diff --git a/src/index.ts b/src/index.ts index 9312d17d..ea7f5c34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,12 +46,19 @@ import { DebuggerHandler } from './handler'; import { EditorHandler } from './handlers/editor'; -import { IDebugger, IDebuggerConfig, IDebuggerSources } from './tokens'; +import { + IDebugger, + IDebuggerConfig, + IDebuggerSources, + IDebuggerSidebar +} from './tokens'; import { ReadOnlyEditorFactory } from './panels/sources/factory'; import { VariablesBodyGrid } from './panels/variables/grid'; +export { IDebugger, IDebuggerSidebar } from './tokens'; + /** * The command IDs used by the debugger plugin. */ @@ -352,30 +359,76 @@ const variables: JupyterFrontEndPlugin = { } }; +/** + * Debugger sidebar provider plugin. + */ +const sidebar: JupyterFrontEndPlugin = { + id: '@jupyterlab/debugger-extension:sidebar', + provides: IDebuggerSidebar, + requires: [IDebugger, IEditorServices], + optional: [IThemeManager, ISettingRegistry], + autoStart: true, + activate: async ( + app: JupyterFrontEnd, + service: IDebugger, + editorServices: IEditorServices, + themeManager: IThemeManager | null, + settingRegistry: ISettingRegistry | null + ): Promise => { + const { commands } = app; + + const callstackCommands = { + registry: commands, + continue: CommandIDs.debugContinue, + terminate: CommandIDs.terminate, + next: CommandIDs.next, + stepIn: CommandIDs.stepIn, + stepOut: CommandIDs.stepOut + }; + + const sidebar = new Debugger.Sidebar({ + service, + callstackCommands, + editorServices, + themeManager + }); + + if (settingRegistry) { + const setting = await settingRegistry.load(main.id); + const updateSettings = (): void => { + const filters = setting.get('variableFilters').composite as { + [key: string]: string[]; + }; + const kernel = service.session?.connection?.kernel?.name ?? ''; + if (kernel && filters[kernel]) { + sidebar.variables.filter = new Set(filters[kernel]); + } + }; + updateSettings(); + setting.changed.connect(updateSettings); + service.sessionChanged.connect(updateSettings); + } + + return sidebar; + } +}; + /** * The main debugger UI plugin. */ const main: JupyterFrontEndPlugin = { id: '@jupyterlab/debugger:main', - requires: [IDebugger, IEditorServices], - optional: [ - ILabShell, - ILayoutRestorer, - ICommandPalette, - ISettingRegistry, - IThemeManager, - IDebuggerSources - ], + requires: [IDebugger, IEditorServices, IDebuggerSidebar], + optional: [ILabShell, ILayoutRestorer, ICommandPalette, IDebuggerSources], autoStart: true, activate: async ( app: JupyterFrontEnd, service: IDebugger, editorServices: IEditorServices, + sidebar: IDebugger.ISidebar, labShell: ILabShell | null, restorer: ILayoutRestorer | null, palette: ICommandPalette | null, - settingRegistry: ISettingRegistry | null, - themeManager: IThemeManager | null, debuggerSources: IDebugger.ISources | null ): Promise => { const { commands, shell, serviceManager } = app; @@ -459,39 +512,6 @@ const main: JupyterFrontEndPlugin = { } }); - const callstackCommands = { - registry: commands, - continue: CommandIDs.debugContinue, - terminate: CommandIDs.terminate, - next: CommandIDs.next, - stepIn: CommandIDs.stepIn, - stepOut: CommandIDs.stepOut - }; - - const sidebar = new Debugger.Sidebar({ - service, - callstackCommands, - editorServices, - themeManager - }); - - if (settingRegistry) { - const setting = await settingRegistry.load(main.id); - const updateSettings = (): void => { - const filters = setting.get('variableFilters').composite as { - [key: string]: string[]; - }; - const list = filters[service.session?.connection?.kernel?.name]; - if (list) { - sidebar.variables.filter = new Set(list); - } - }; - - updateSettings(); - setting.changed.connect(updateSettings); - service.sessionChanged.connect(updateSettings); - } - service.eventMessage.connect((_, event): void => { commands.notifyCommandChanged(); if (labShell && event.event === 'initialized') { @@ -611,6 +631,7 @@ const plugins: JupyterFrontEndPlugin[] = [ files, notebooks, variables, + sidebar, main, sources, configuration diff --git a/src/sidebar.ts b/src/sidebar.ts index 66c8c708..88e9fa47 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -22,7 +22,7 @@ import { IDebugger } from './tokens'; /** * A debugger sidebar. */ -export class DebuggerSidebar extends Panel { +export class DebuggerSidebar extends Panel implements IDebugger.ISidebar { /** * Instantiate a new Debugger.Sidebar * @@ -31,7 +31,7 @@ export class DebuggerSidebar extends Panel { constructor(options: DebuggerSidebar.IOptions) { super(); this.id = 'jp-debugger-sidebar'; - this.title.iconRenderer = bugIcon; + this.title.icon = bugIcon; this.addClass('jp-DebuggerSidebar'); const { @@ -73,16 +73,50 @@ export class DebuggerSidebar extends Panel { header.title.label = title; }); - const body = new SplitPanel(); + this._body = new SplitPanel(); + this._body.orientation = 'vertical'; + this._body.addClass('jp-DebuggerSidebar-body'); + this.addWidget(this._body); - body.orientation = 'vertical'; - body.addWidget(this.variables); - body.addWidget(this.callstack); - body.addWidget(this.breakpoints); - body.addWidget(this.sources); - body.addClass('jp-DebuggerSidebar-body'); + this.addItem(this.variables); + this.addItem(this.callstack); + this.addItem(this.breakpoints); + this.addItem(this.sources); + } - this.addWidget(body); + /** + * Add an item at the end of the sidebar. + * + * @param widget - The widget to add to the sidebar. + * + * #### Notes + * If the widget is already contained in the sidebar, it will be moved. + * The item can be removed from the sidebar by setting its parent to `null`. + */ + addItem(widget: Widget): void { + this._body.addWidget(widget); + } + + /** + * Insert an item at the specified index. + * + * @param index - The index at which to insert the widget. + * + * @param widget - The widget to insert into to the sidebar. + * + * #### Notes + * If the widget is already contained in the sidebar, it will be moved. + * The item can be removed from the sidebar by setting its parent to `null`. + */ + insertItem(index: number, widget: Widget): void { + this._body.insertWidget(index, widget); + } + + /** + * A read-only array of the sidebar items. + */ + get items(): readonly Widget[] { + return this._body.widgets; } /** @@ -119,6 +153,11 @@ export class DebuggerSidebar extends Panel { * The sources widget. */ readonly sources: SourcesPanel; + + /** + * Container for sidebar items. + */ + private _body: SplitPanel; } /** diff --git a/src/tokens.ts b/src/tokens.ts index 928f7671..ab9d0204 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -11,6 +11,8 @@ import { IObservableDisposable } from '@lumino/disposable'; import { ISignal, Signal } from '@lumino/signaling'; +import { Widget } from '@lumino/widgets'; + import { DebugProtocol } from 'vscode-debugprotocol'; /** @@ -484,6 +486,26 @@ export namespace IDebugger { } } + /** + * Debugger sidebar interface. + */ + export interface ISidebar extends Widget { + /** + * Add item at the end of the sidebar. + */ + addItem(widget: Widget): void; + + /** + * Insert item at a specified index. + */ + insertItem(index: number, widget: Widget): void; + + /** + * Return all items that were added to sidebar. + */ + readonly items: readonly Widget[]; + } + /** * A utility to find text editors used by the debugger. */ @@ -741,3 +763,10 @@ export const IDebuggerConfig = new Token( export const IDebuggerSources = new Token( '@jupyterlab/debugger:IDebuggerSources' ); + +/** + * The debugger sidebar token. + */ +export const IDebuggerSidebar = new Token( + '@jupyterlab/debugger:IDebuggerSidebar' +);