123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- ILabShell,
- ILayoutRestorer,
- JupyterFrontEnd,
- JupyterFrontEndPlugin
- } from '@jupyterlab/application';
- import {
- ICommandPalette,
- IThemeManager,
- MainAreaWidget,
- WidgetTracker
- } from '@jupyterlab/apputils';
- import { IEditorServices } from '@jupyterlab/codeeditor';
- import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console';
- import { DocumentWidget } from '@jupyterlab/docregistry';
- import { FileEditor, IEditorTracker } from '@jupyterlab/fileeditor';
- import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
- import { Session } from '@jupyterlab/services';
- import { ISettingRegistry } from '@jupyterlab/settingregistry';
- import {
- continueIcon,
- stepIntoIcon,
- stepOutIcon,
- stepOverIcon,
- terminateIcon,
- variableIcon
- } from './icons';
- import { Debugger } from './debugger';
- import { TrackerHandler } from './handlers/tracker';
- import { DebuggerHandler } from './handler';
- import { IDebugger, IDebuggerConfig, IDebuggerSources } from './tokens';
- import { VariablesBodyGrid } from './panels/variables/grid';
- /**
- * The command IDs used by the debugger plugin.
- */
- export namespace CommandIDs {
- export const debugContinue = 'debugger:continue';
- export const terminate = 'debugger:terminate';
- export const next = 'debugger:next';
- export const stepIn = 'debugger:stepIn';
- export const stepOut = 'debugger:stepOut';
- export const inspectVariable = 'debugger:inspect-variable';
- }
- /**
- * A plugin that provides visual debugging support for consoles.
- */
- const consoles: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:consoles',
- autoStart: true,
- requires: [IDebugger, IDebuggerSources, IConsoleTracker],
- optional: [ILabShell],
- activate: (
- app: JupyterFrontEnd,
- debug: IDebugger,
- debuggerSources: IDebugger.ISources,
- consoleTracker: IConsoleTracker,
- labShell: ILabShell
- ) => {
- const handler = new DebuggerHandler({
- type: 'console',
- shell: app.shell,
- service: debug
- });
- const updateHandlerAndCommands = async (
- widget: ConsolePanel
- ): Promise<void> => {
- const { sessionContext } = widget;
- await sessionContext.ready;
- await handler.updateContext(widget, sessionContext);
- app.commands.notifyCommandChanged();
- };
- if (labShell) {
- labShell.currentChanged.connect(async (_, update) => {
- const widget = update.newValue;
- if (!(widget instanceof ConsolePanel)) {
- return;
- }
- await updateHandlerAndCommands(widget);
- });
- return;
- }
- consoleTracker.currentChanged.connect(async (_, consolePanel) => {
- await updateHandlerAndCommands(consolePanel);
- });
- }
- };
- /**
- * A plugin that provides visual debugging support for file editors.
- */
- const files: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:files',
- autoStart: true,
- requires: [IDebugger, IEditorTracker],
- optional: [ILabShell],
- activate: (
- app: JupyterFrontEnd,
- debug: IDebugger,
- editorTracker: IEditorTracker,
- labShell: ILabShell
- ) => {
- const handler = new DebuggerHandler({
- type: 'file',
- shell: app.shell,
- service: debug
- });
- const activeSessions: {
- [id: string]: Session.ISessionConnection;
- } = {};
- const updateHandlerAndCommands = async (
- widget: DocumentWidget
- ): Promise<void> => {
- const sessions = app.serviceManager.sessions;
- try {
- const model = await sessions.findByPath(widget.context.path);
- let session = activeSessions[model.id];
- if (!session) {
- // Use `connectTo` only if the session does not exist.
- // `connectTo` sends a kernel_info_request on the shell
- // channel, which blocks the debug session restore when waiting
- // for the kernel to be ready
- session = sessions.connectTo({ model });
- activeSessions[model.id] = session;
- }
- await handler.update(widget, session);
- app.commands.notifyCommandChanged();
- } catch {
- return;
- }
- };
- if (labShell) {
- labShell.currentChanged.connect(async (_, update) => {
- const widget = update.newValue;
- if (!(widget instanceof DocumentWidget)) {
- return;
- }
- const content = widget.content;
- if (!(content instanceof FileEditor)) {
- return;
- }
- await updateHandlerAndCommands(widget);
- });
- }
- editorTracker.currentChanged.connect(async (_, documentWidget) => {
- await updateHandlerAndCommands(
- (documentWidget as unknown) as DocumentWidget
- );
- });
- }
- };
- /**
- * A plugin that provides visual debugging support for notebooks.
- */
- const notebooks: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:notebooks',
- autoStart: true,
- requires: [IDebugger, INotebookTracker],
- optional: [ILabShell],
- activate: (
- app: JupyterFrontEnd,
- service: IDebugger,
- notebookTracker: INotebookTracker,
- labShell: ILabShell
- ) => {
- const handler = new DebuggerHandler({
- type: 'notebook',
- shell: app.shell,
- service
- });
- const updateHandlerAndCommands = async (
- widget: NotebookPanel
- ): Promise<void> => {
- const { sessionContext } = widget;
- await sessionContext.ready;
- await handler.updateContext(widget, sessionContext);
- app.commands.notifyCommandChanged();
- };
- if (labShell) {
- labShell.currentChanged.connect(async (_, update) => {
- const widget = update.newValue;
- if (!(widget instanceof NotebookPanel)) {
- return;
- }
- await updateHandlerAndCommands(widget);
- });
- return;
- }
- notebookTracker.currentChanged.connect(
- async (_, notebookPanel: NotebookPanel) => {
- await updateHandlerAndCommands(notebookPanel);
- }
- );
- }
- };
- /**
- * A plugin that tracks notebook, console and file editors used for debugging.
- */
- const tracker: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:tracker',
- autoStart: true,
- requires: [IDebugger, IEditorServices, IDebuggerSources],
- activate: (
- _,
- debug: IDebugger,
- editorServices: IEditorServices,
- debuggerSources: IDebugger.ISources
- ) => {
- new TrackerHandler({
- editorServices,
- debuggerService: debug,
- debuggerSources
- });
- }
- };
- /**
- * A plugin that provides a debugger service.
- */
- const service: JupyterFrontEndPlugin<IDebugger> = {
- id: '@jupyterlab/debugger:service',
- autoStart: true,
- provides: IDebugger,
- requires: [IDebuggerConfig, IDebuggerSources],
- activate: (
- app: JupyterFrontEnd,
- config: IDebugger.IConfig,
- debuggerSources: IDebugger.ISources
- ) =>
- new Debugger.Service({
- config,
- debuggerSources,
- specsManager: app.serviceManager.kernelspecs
- })
- };
- /**
- * A plugin that provides a configuration with hash method.
- */
- const configuration: JupyterFrontEndPlugin<IDebugger.IConfig> = {
- id: '@jupyterlab/debugger:config',
- provides: IDebuggerConfig,
- autoStart: true,
- activate: () => new Debugger.Config()
- };
- /**
- * A plugin that provides source/editor functionality for debugging.
- */
- const sources: JupyterFrontEndPlugin<IDebugger.ISources> = {
- id: '@jupyterlab/debugger:sources',
- autoStart: true,
- provides: IDebuggerSources,
- requires: [IDebuggerConfig, IEditorServices],
- optional: [INotebookTracker, IConsoleTracker, IEditorTracker],
- activate: (
- app: JupyterFrontEnd,
- config: IDebugger.IConfig,
- editorServices: IEditorServices,
- notebookTracker: INotebookTracker | null,
- consoleTracker: IConsoleTracker | null,
- editorTracker: IEditorTracker | null
- ): IDebugger.ISources => {
- return new Debugger.Sources({
- config,
- shell: app.shell,
- editorServices,
- notebookTracker,
- consoleTracker,
- editorTracker
- });
- }
- };
- /*
- * A plugin to open detailed views for variables.
- */
- const variables: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:variables',
- autoStart: true,
- requires: [IDebugger],
- optional: [IThemeManager],
- activate: (
- app: JupyterFrontEnd,
- service: IDebugger,
- themeManager: IThemeManager
- ) => {
- const { commands, shell } = app;
- const tracker = new WidgetTracker<MainAreaWidget<VariablesBodyGrid>>({
- namespace: 'debugger/inspect-variable'
- });
- commands.addCommand(CommandIDs.inspectVariable, {
- label: 'Inspect Variable',
- caption: 'Inspect Variable',
- execute: async args => {
- const { variableReference } = args;
- if (!variableReference || variableReference === 0) {
- return;
- }
- const variables = await service.inspectVariable(
- variableReference as number
- );
- const title = args.title as string;
- const id = `jp-debugger-variable-${title}`;
- if (
- !variables ||
- variables.length === 0 ||
- tracker.find(widget => widget.id === id)
- ) {
- return;
- }
- const model = (service.model as Debugger.Model).variables;
- const widget = new MainAreaWidget<VariablesBodyGrid>({
- content: new VariablesBodyGrid({
- model,
- commands,
- scopes: [{ name: title, variables }]
- })
- });
- widget.addClass('jp-DebuggerVariables');
- widget.id = id;
- widget.title.icon = variableIcon;
- widget.title.label = `${service.session?.connection?.name} - ${title}`;
- void tracker.add(widget);
- model.changed.connect(() => widget.dispose());
- if (themeManager) {
- const updateStyle = (): void => {
- const isLight = themeManager?.theme
- ? themeManager.isLight(themeManager.theme)
- : true;
- widget.content.theme = isLight ? 'light' : 'dark';
- };
- themeManager.themeChanged.connect(updateStyle);
- widget.disposed.connect(() =>
- themeManager.themeChanged.disconnect(updateStyle)
- );
- updateStyle();
- }
- shell.add(widget, 'main', {
- mode: tracker.currentWidget ? 'split-right' : 'split-bottom'
- });
- }
- });
- }
- };
- /**
- * The main debugger UI plugin.
- */
- const main: JupyterFrontEndPlugin<void> = {
- id: '@jupyterlab/debugger:main',
- requires: [IDebugger, IEditorServices],
- optional: [
- ILabShell,
- ILayoutRestorer,
- ICommandPalette,
- ISettingRegistry,
- IThemeManager
- ],
- autoStart: true,
- activate: async (
- app: JupyterFrontEnd,
- service: IDebugger,
- editorServices: IEditorServices,
- labShell: ILabShell | null,
- restorer: ILayoutRestorer | null,
- palette: ICommandPalette | null,
- settingRegistry: ISettingRegistry | null,
- themeManager: IThemeManager | null
- ): Promise<void> => {
- const { commands, shell } = app;
- commands.addCommand(CommandIDs.debugContinue, {
- label: 'Continue',
- caption: 'Continue',
- icon: continueIcon,
- isEnabled: () => {
- return service.hasStoppedThreads();
- },
- execute: async () => {
- await service.continue();
- commands.notifyCommandChanged();
- }
- });
- commands.addCommand(CommandIDs.terminate, {
- label: 'Terminate',
- caption: 'Terminate',
- icon: terminateIcon,
- isEnabled: () => {
- return service.hasStoppedThreads();
- },
- execute: async () => {
- await service.restart();
- commands.notifyCommandChanged();
- }
- });
- commands.addCommand(CommandIDs.next, {
- label: 'Next',
- caption: 'Next',
- icon: stepOverIcon,
- isEnabled: () => {
- return service.hasStoppedThreads();
- },
- execute: async () => {
- await service.next();
- }
- });
- commands.addCommand(CommandIDs.stepIn, {
- label: 'StepIn',
- caption: 'Step In',
- icon: stepIntoIcon,
- isEnabled: () => {
- return service.hasStoppedThreads();
- },
- execute: async () => {
- await service.stepIn();
- }
- });
- commands.addCommand(CommandIDs.stepOut, {
- label: 'StepOut',
- caption: 'Step Out',
- icon: stepOutIcon,
- isEnabled: () => {
- return service.hasStoppedThreads();
- },
- execute: async () => {
- await service.stepOut();
- }
- });
- 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
- });
- 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<string>(list);
- }
- };
- updateSettings();
- setting.changed.connect(updateSettings);
- service.sessionChanged.connect(updateSettings);
- }
- if (themeManager) {
- const updateStyle = (): void => {
- const isLight = themeManager?.theme
- ? themeManager.isLight(themeManager.theme)
- : true;
- sidebar.variables.theme = isLight ? 'light' : 'dark';
- };
- themeManager.themeChanged.connect(updateStyle);
- updateStyle();
- }
- service.eventMessage.connect((_, event): void => {
- commands.notifyCommandChanged();
- if (labShell && event.event === 'initialized') {
- labShell.expandRight();
- }
- });
- service.sessionChanged.connect(_ => {
- commands.notifyCommandChanged();
- });
- if (restorer) {
- restorer.add(sidebar, 'debugger-sidebar');
- }
- shell.add(sidebar, 'right');
- if (palette) {
- const category = 'Debugger';
- [
- CommandIDs.debugContinue,
- CommandIDs.terminate,
- CommandIDs.next,
- CommandIDs.stepIn,
- CommandIDs.stepOut
- ].forEach(command => {
- palette.addItem({ command, category });
- });
- }
- }
- };
- /**
- * Export the plugins as default.
- */
- const plugins: JupyterFrontEndPlugin<any>[] = [
- service,
- consoles,
- files,
- notebooks,
- tracker,
- variables,
- main,
- sources,
- configuration
- ];
- export default plugins;
|