Skip to content

Commit

Permalink
Support debug "restart" #7670
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Oct 14, 2022
1 parent 298dcee commit 2feb642
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 194 deletions.
12 changes: 4 additions & 8 deletions src/interactive-window/debugger/jupyter/debuggingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,10 @@ export class InteractiveWindowDebuggingManager
__cellIndex: cell.index
};
const opts: DebugSessionOptions = { suppressSaveBeforeStart: true };
return this.startDebuggingConfig(doc, config, opts);
return this.startDebuggingConfig(config, opts);
}

protected override async createDebugAdapterDescriptor(
session: DebugSession
): Promise<DebugAdapterDescriptor | undefined> {
protected async createDebugAdapterDescriptor(session: DebugSession): Promise<DebugAdapterDescriptor | undefined> {
const config = session.configuration;
assertIsDebugConfig(config);

Expand Down Expand Up @@ -194,9 +192,7 @@ export class InteractiveWindowDebuggingManager
const cell = activeDoc.cellAt(config.__cellIndex);
const controller = new DebugCellController(adapter, cell, kernel!);
adapter.setDebuggingDelegate(controller);
controller.ready
.then(() => debug.resolve(session))
.catch((ex) => console.error('Failed waiting for controller to be ready', ex));
controller.ready.catch((ex) => console.error('Failed waiting for controller to be ready', ex)); // ?? TODO

this.trackDebugAdapter(activeDoc, adapter);
return new DebugAdapterInlineImplementation(adapter);
Expand All @@ -205,7 +201,7 @@ export class InteractiveWindowDebuggingManager
// TODO: This will likely be needed for mapping breakpoints and such
public async updateSourceMaps(notebookEditor: NotebookEditor, hashes: IFileGeneratedCodes[]): Promise<void> {
// Make sure that we have an active debugging session at this point
let debugSession = await this.getDebugSession(notebookEditor.notebook);
let debugSession = this.getDebugSession(notebookEditor.notebook);
if (debugSession) {
traceInfoIfCI(`Sending debug request for source map`);
await Promise.all(
Expand Down
2 changes: 1 addition & 1 deletion src/notebooks/debugger/debugCellControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class DebugCellController implements IDebuggingDelegate {
this.commandManager
.executeCommand('notebook.cell.execute', {
ranges: [{ start: this.debugCell.index, end: this.debugCell.index + 1 }],
document: this.debugCell.document.uri
document: this.debugCell.notebook.uri
})
.then(noop, noop);
}
Expand Down
35 changes: 3 additions & 32 deletions src/notebooks/debugger/debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,15 @@
// Licensed under the MIT License.

'use strict';
import { debug, NotebookDocument, DebugSession, DebugSessionOptions, DebugConfiguration } from 'vscode';
import { noop } from '../../platform/common/utils/misc';
import { DebugConfiguration, DebugSession, NotebookDocument } from 'vscode';

/**
* Wraps debug start in a promise
*/
export class Debugger {
private resolveFunc?: (value: DebugSession) => void;
private rejectFunc?: (reason?: Error) => void;

readonly session: Promise<DebugSession>;

constructor(
public readonly document: NotebookDocument,
public readonly config: DebugConfiguration,
options?: DebugSessionOptions
) {
this.session = new Promise<DebugSession>((resolve, reject) => {
this.resolveFunc = resolve;
this.rejectFunc = reject;

debug.startDebugging(undefined, config, options).then(undefined, reject);
});
}

resolve(session: DebugSession) {
if (this.resolveFunc) {
this.resolveFunc(session);
}
}

reject(reason: Error) {
if (this.rejectFunc) {
this.rejectFunc(reason);
}
}

async stop() {
void debug.stopDebugging(await this.session).then(noop, noop);
}
public readonly session: DebugSession
) {}
}
208 changes: 93 additions & 115 deletions src/notebooks/debugger/debuggingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ import { IConfigurationService } from '../../platform/common/types';
import { DataScience } from '../../platform/common/utils/localize';
import { noop } from '../../platform/common/utils/misc';
import { IServiceContainer } from '../../platform/ioc/types';
import { traceError, traceInfo, traceInfoIfCI } from '../../platform/logging';
import { traceInfo, traceInfoIfCI } from '../../platform/logging';
import { ResourceSet } from '../../platform/vscode-path/map';
import * as path from '../../platform/vscode-path/path';
import { sendTelemetryEvent } from '../../telemetry';
import { IControllerLoader, IControllerSelection } from '../controllers/types';
import { DebuggingTelemetry, pythonKernelDebugAdapter } from './constants';
import { DebugCellController } from './debugCellControllers';
import { Debugger } from './debugger';
import { DebuggingManagerBase } from './debuggingManagerBase';
import { IDebuggingManager, IKernelDebugAdapterConfig, KernelDebugMode } from './debuggingTypes';
import { assertIsDebugConfig, IpykernelCheckResult } from './helper';
import { KernelDebugAdapter } from './kernelDebugAdapter';
import { KernelDebugAdapterBase } from './kernelDebugAdapterBase';
import { RunByLineController } from './runByLineController';

/**
Expand Down Expand Up @@ -202,67 +202,34 @@ export class DebuggingManager
return;
}

if (this.notebookInProgress.has(editor.notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook`);
return;
}

if (this.isDebugging(editor.notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook document.`);
return;
await this.checkIpykernel(editor);
if (mode === KernelDebugMode.RunByLine || mode === KernelDebugMode.Cell) {
await this.startDebuggingCell(editor.notebook, mode, cell!);
} else {
await this.startDebugging(editor.notebook);
}
}

const checkIpykernelAndStart = async (allowSelectKernel = true): Promise<void> => {
const ipykernelResult = await this.checkForIpykernel6(editor.notebook);
switch (ipykernelResult) {
case IpykernelCheckResult.NotInstalled:
// User would have been notified about this, nothing more to do.
return;
case IpykernelCheckResult.Outdated:
case IpykernelCheckResult.Unknown: {
this.promptInstallIpykernel6().then(noop, noop);
return;
}
case IpykernelCheckResult.Ok: {
switch (mode) {
case KernelDebugMode.Everything: {
await this.startDebugging(editor.notebook);
return;
}
case KernelDebugMode.Cell:
if (cell) {
await this.startDebuggingCell(editor.notebook, KernelDebugMode.Cell, cell);
}
return;
case KernelDebugMode.RunByLine:
if (cell) {
await this.startDebuggingCell(editor.notebook, KernelDebugMode.RunByLine, cell);
}
return;
default:
return;
}
}
case IpykernelCheckResult.ControllerNotSelected: {
if (allowSelectKernel) {
await this.commandManager.executeCommand('notebook.selectKernel', { notebookEditor: editor });
await checkIpykernelAndStart(false);
}
protected async checkIpykernel(editor: NotebookEditor, allowSelectKernel: boolean = true): Promise<void> {
const ipykernelResult = await this.checkForIpykernel6(editor.notebook);
switch (ipykernelResult) {
case IpykernelCheckResult.NotInstalled:
// User would have been notified about this, nothing more to do.
return;
case IpykernelCheckResult.Outdated:
case IpykernelCheckResult.Unknown: {
this.promptInstallIpykernel6().then(noop, noop);
return;
}
case IpykernelCheckResult.ControllerNotSelected: {
if (allowSelectKernel) {
await this.commandManager.executeCommand('notebook.selectKernel', { notebookEditor: editor });
await this.checkIpykernel(editor, false);
}
}
};

try {
this.notebookInProgress.add(editor.notebook);
this.updateDebugContextKey();
await checkIpykernelAndStart();
} catch (e) {
traceInfo(`Error starting debugging: ${e}`);
} finally {
this.notebookInProgress.delete(editor.notebook);
this.updateDebugContextKey();
}
}

private async startDebuggingCell(
doc: NotebookDocument,
mode: KernelDebugMode.Cell | KernelDebugMode.RunByLine,
Expand All @@ -275,7 +242,8 @@ export class DebuggingManager
justMyCode: true,
// add a property to the config to know if the session is runByLine
__mode: mode,
__cellIndex: cell.index
__cellIndex: cell.index,
__notebookUri: doc.uri.toString()
};
const opts: DebugSessionOptions | undefined =
mode === KernelDebugMode.RunByLine
Expand All @@ -286,7 +254,7 @@ export class DebuggingManager
suppressSaveBeforeStart: true
}
: { suppressSaveBeforeStart: true };
return this.startDebuggingConfig(doc, config, opts);
return this.startDebuggingConfig(config, opts);
}

private async startDebugging(doc: NotebookDocument) {
Expand All @@ -296,71 +264,81 @@ export class DebuggingManager
request: 'attach',
internalConsoleOptions: 'neverOpen',
justMyCode: false,
__mode: KernelDebugMode.Everything
__mode: KernelDebugMode.Everything,
__notebookUri: doc.uri.toString()
};
return this.startDebuggingConfig(doc, config);
}

protected override trackDebugAdapter(notebook: NotebookDocument, adapter: KernelDebugAdapterBase): void {
super.trackDebugAdapter(notebook, adapter);
this.updateDebugContextKey();
return this.startDebuggingConfig(config);
}

protected override async createDebugAdapterDescriptor(
session: DebugSession
): Promise<DebugAdapterDescriptor | undefined> {
protected async createDebugAdapterDescriptor(session: DebugSession): Promise<DebugAdapterDescriptor | undefined> {
const config = session.configuration;
assertIsDebugConfig(config);
const activeDoc = config.__interactiveWindowNotebookUri
? this.vscNotebook.notebookDocuments.find(
(doc) => doc.uri.toString() === config.__interactiveWindowNotebookUri
)
: this.vscNotebook.activeNotebookEditor?.notebook;
if (activeDoc) {
// TODO we apparently always have a kernel here, clean up typings
const kernel = await this.ensureKernelIsRunning(activeDoc);
const debug = this.getDebuggerByUri(activeDoc);

if (debug) {
if (kernel?.session) {
const adapter = new KernelDebugAdapter(
session,
debug.document,
kernel.session,
kernel,
this.platform,
this.debugService
);

if (config.__mode === KernelDebugMode.RunByLine && typeof config.__cellIndex === 'number') {
const cell = activeDoc.cellAt(config.__cellIndex);
const controller = new RunByLineController(
adapter,
cell,
this.commandManager,
kernel!,
this.settings
);
adapter.setDebuggingDelegate(controller);
this.notebookToRunByLineController.set(debug.document, controller);
this.updateRunByLineContextKeys();
} else if (config.__mode === KernelDebugMode.Cell && typeof config.__cellIndex === 'number') {
const cell = activeDoc.cellAt(config.__cellIndex);
const controller = new DebugCellController(adapter, cell, kernel!, this.commandManager);
adapter.setDebuggingDelegate(controller);
}

this.trackDebugAdapter(debug.document, adapter);
const notebookUri = config.__interactiveWindowNotebookUri ?? config.__notebookUri;
const notebook = this.vscNotebook.notebookDocuments.find((doc) => doc.uri.toString() === notebookUri);

// Wait till we're attached before resolving the session
debug.resolve(session);
return new DebugAdapterInlineImplementation(adapter);
} else {
this.appShell.showInformationMessage(DataScience.kernelWasNotStarted()).then(noop, noop);
}
if (!notebook) {
traceInfo(`Cannot start debugging. Notebook ${notebookUri} not found.`);
return;
}

if (this.notebookInProgress.has(notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook`);
return;
}

if (this.isDebugging(notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook document.`);
return;
}

this.notebookToDebugger.set(notebook, new Debugger(notebook, config, session));
try {
this.notebookInProgress.add(notebook);
this.updateDebugContextKey();
return await this.doCreateDebugAdapterDescriptor(config, session, notebook);
} finally {
this.notebookInProgress.delete(notebook);
this.updateDebugContextKey();
}
}

private async doCreateDebugAdapterDescriptor(
config: IKernelDebugAdapterConfig,
session: DebugSession,
notebook: NotebookDocument
): Promise<DebugAdapterDescriptor | undefined> {
const kernel = await this.ensureKernelIsRunning(notebook);
if (kernel?.session) {
const adapter = new KernelDebugAdapter(
session,
notebook,
kernel.session,
kernel,
this.platform,
this.debugService
);

if (config.__mode === KernelDebugMode.RunByLine && typeof config.__cellIndex === 'number') {
const cell = notebook.cellAt(config.__cellIndex);
const controller = new RunByLineController(adapter, cell, this.commandManager, kernel!, this.settings);
adapter.setDebuggingDelegate(controller);
this.notebookToRunByLineController.set(notebook, controller);
this.updateRunByLineContextKeys();
} else if (config.__mode === KernelDebugMode.Cell && typeof config.__cellIndex === 'number') {
const cell = notebook.cellAt(config.__cellIndex);
const controller = new DebugCellController(adapter, cell, kernel!, this.commandManager);
adapter.setDebuggingDelegate(controller);
}

this.trackDebugAdapter(notebook, adapter);
this.updateDebugContextKey();

return new DebugAdapterInlineImplementation(adapter);
} else {
this.appShell.showInformationMessage(DataScience.kernelWasNotStarted()).then(noop, noop);
}
traceError('Debug sessions should start only from the cell toolbar command');

return;
}
}
Loading

0 comments on commit 2feb642

Please sign in to comment.