-
Notifications
You must be signed in to change notification settings - Fork 299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rob's comments and update variable view when done debugging #7527
Changes from 7 commits
485f398
ce4de53
48d96fa
2872d03
d4b5485
2dead32
0610fbd
e0619ea
af79fd4
a5975c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ export class DebuggerVariables extends DebugLocationTracker | |
@inject(IVSCodeNotebook) private readonly vscNotebook: IVSCodeNotebook | ||
) { | ||
super(undefined); | ||
this.debuggingManager.onDoneDebugging(() => this.refreshEventEmitter.fire(), this); | ||
} | ||
|
||
public get refreshRequired(): Event<void> { | ||
|
@@ -236,32 +237,8 @@ export class DebuggerVariables extends DebugLocationTracker | |
// When the initialize response comes back, indicate we have started. | ||
if (message.type === 'response' && message.command === 'initialize') { | ||
this.debuggingStarted = true; | ||
} else if ( | ||
(message as DebugProtocol.StackTraceResponse).command === 'stackTrace' && | ||
this.activeNotebookIsDebugging() | ||
) { | ||
const sf = (message as DebugProtocol.StackTraceResponse).body.stackFrames[0]; | ||
const callScopes = async () => { | ||
const doc = this.vscNotebook.activeNotebookEditor?.document; | ||
if (doc) { | ||
const session = await this.debuggingManager.getDebugSession(doc); | ||
const mode = this.debuggingManager.getDebugMode(doc); | ||
if (mode === KernelDebugMode.RunByLine) { | ||
const cell = this.debuggingManager.getDebugCell(doc); | ||
// Only call scopes (and variables) if we are stopped on the cell we are executing | ||
if (sf.source && cell && sf.source.path === cell.document.uri.toString()) { | ||
void session?.customRequest('scopes', { frameId: sf.id }); | ||
} | ||
} else { | ||
// Only call scopes (and variables) if we are stopped on the notebook we are executing | ||
const docURI = path.basename(doc.uri.toString()); | ||
if (sf.source && sf.source.path && sf.source.path.includes(docURI)) { | ||
void session?.customRequest('scopes', { frameId: sf.id }); | ||
} | ||
} | ||
} | ||
}; | ||
void callScopes(); | ||
} else if (message.type === 'event' && message.event === 'stopped' && this.activeNotebookIsDebugging()) { | ||
void this.handleNotebookVariables(message as DebugProtocol.StoppedEvent); | ||
} else if (message.type === 'response' && message.command === 'scopes' && message.body && message.body.scopes) { | ||
const response = message as DebugProtocol.ScopesResponse; | ||
|
||
|
@@ -271,16 +248,6 @@ export class DebuggerVariables extends DebugLocationTracker | |
this.currentVariablesReference = newVariablesReference; | ||
this.currentSeqNumsForVariables.clear(); | ||
} | ||
|
||
// Catch the scopes response and use its variablesReference to send a variables message | ||
const doc = this.vscNotebook.activeNotebookEditor?.document; | ||
if (this.activeNotebookIsDebugging() && doc) { | ||
const callVariables = async () => { | ||
const session = await this.debuggingManager.getDebugSession(doc); | ||
void session?.customRequest('variables', { variablesReference: newVariablesReference }); | ||
}; | ||
void callVariables(); | ||
} | ||
} else if ( | ||
message.type === 'response' && | ||
message.command === 'variables' && | ||
|
@@ -300,10 +267,6 @@ export class DebuggerVariables extends DebugLocationTracker | |
|
||
this.updateVariables(undefined, message as DebugProtocol.VariablesResponse); | ||
this.monkeyPatchDataViewableVariables(message); | ||
|
||
if (this.activeNotebookIsDebugging()) { | ||
this.refreshEventEmitter.fire(); | ||
} | ||
} else if (message.type === 'event' && message.event === 'terminated') { | ||
// When the debugger exits, make sure the variables are cleared | ||
this.lastKnownVariables = []; | ||
|
@@ -452,6 +415,65 @@ export class DebuggerVariables extends DebugLocationTracker | |
const activeNotebook = this.vscNotebook.activeNotebookEditor; | ||
return !!activeNotebook && this.debuggingManager.isDebugging(activeNotebook.document); | ||
} | ||
|
||
// This handles all the debug session calls, variable handling, and refresh calls needed for notebook debugging | ||
private async handleNotebookVariables(stoppedMessage: DebugProtocol.StoppedEvent): Promise<void> { | ||
const doc = this.vscNotebook.activeNotebookEditor?.document; | ||
const threadId = stoppedMessage.body.threadId; | ||
|
||
if (doc) { | ||
const session = await this.debuggingManager.getDebugSession(doc); | ||
if (session) { | ||
// Call stack trace | ||
const stResponse = await session.customRequest('stackTrace', { | ||
threadId, | ||
startFrame: 0, | ||
levels: 1 | ||
}); | ||
|
||
// Call scopes | ||
const sf = stResponse.stackFrames[0]; | ||
const mode = this.debuggingManager.getDebugMode(doc); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
let scopesResponse: any | undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
|
||
if (mode === KernelDebugMode.RunByLine) { | ||
// Only call scopes (and variables) if we are stopped on the cell we are executing | ||
const cell = this.debuggingManager.getDebugCell(doc); | ||
if (sf.source && cell && sf.source.path === cell.document.uri.toString()) { | ||
scopesResponse = await session.customRequest('scopes', { frameId: sf.id }); | ||
} | ||
} else { | ||
// Only call scopes (and variables) if we are stopped on the notebook we are executing | ||
const docURI = path.basename(doc.uri.toString()); | ||
if (sf.source && sf.source.path && sf.source.path.includes(docURI)) { | ||
scopesResponse = await session.customRequest('scopes', { frameId: sf.id }); | ||
} | ||
} | ||
|
||
// Call variables | ||
if (scopesResponse) { | ||
// Keep track of variablesReference because "hover" requests also try to update variables | ||
const newVariablesReference = scopesResponse.scopes[0].variablesReference; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure you only want to update the first scope? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no hahah |
||
if (newVariablesReference !== this.currentVariablesReference) { | ||
this.currentVariablesReference = newVariablesReference; | ||
this.currentSeqNumsForVariables.clear(); | ||
} | ||
|
||
// Catch the scopes response and use its variablesReference to send a variables message | ||
const varResponse: DebugProtocol.VariablesResponse = await session.customRequest('variables', { | ||
variablesReference: newVariablesReference | ||
}); | ||
|
||
// Refresh variable view | ||
this.updateVariables(undefined, varResponse as DebugProtocol.VariablesResponse); | ||
this.monkeyPatchDataViewableVariables(varResponse); | ||
|
||
this.refreshEventEmitter.fire(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
export function convertDebugProtocolVariableToIJupyterVariable(variable: DebugProtocol.Variable) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,9 +13,9 @@ import { Commands } from '../../datascience/constants'; | |
import { IKernel } from '../../datascience/jupyter/kernels/types'; | ||
import { sendTelemetryEvent } from '../../telemetry'; | ||
import { DebuggingTelemetry } from '../constants'; | ||
import { DebuggingDelegate, IKernelDebugAdapter, KernelDebugMode } from '../types'; | ||
import { IDebuggingDelegate, IKernelDebugAdapter, KernelDebugMode } from '../types'; | ||
|
||
export class DebugCellController implements DebuggingDelegate { | ||
export class DebugCellController implements IDebuggingDelegate { | ||
constructor( | ||
private readonly debugAdapter: IKernelDebugAdapter, | ||
public readonly debugCell: NotebookCell, | ||
|
@@ -25,11 +25,11 @@ export class DebugCellController implements DebuggingDelegate { | |
sendTelemetryEvent(DebuggingTelemetry.successfullyStartedRunAndDebugCell); | ||
} | ||
|
||
public async willSendEvent(_msg: DebugProtocolMessage): Promise<boolean> { | ||
public async onWillSendEvent(_msg: DebugProtocolMessage): Promise<boolean> { | ||
return false; | ||
} | ||
|
||
public async willSendRequest(request: DebugProtocol.Request): Promise<void> { | ||
public async onWillSendRequest(request: DebugProtocol.Request): Promise<void> { | ||
if (request.command === 'configurationDone') { | ||
await cellDebugSetup(this.kernel, this.debugAdapter, this.debugCell); | ||
|
||
|
@@ -41,7 +41,7 @@ export class DebugCellController implements DebuggingDelegate { | |
} | ||
} | ||
|
||
export class RunByLineController implements DebuggingDelegate { | ||
export class RunByLineController implements IDebuggingDelegate { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious, do we need all of these interfaces? Why can't we just use classes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were following the extension patterns. |
||
private lastPausedThreadId: number | undefined; | ||
|
||
constructor( | ||
|
@@ -72,7 +72,7 @@ export class RunByLineController implements DebuggingDelegate { | |
return config.__mode; | ||
} | ||
|
||
public async willSendEvent(msg: DebugProtocolMessage): Promise<boolean> { | ||
public async onWillSendEvent(msg: DebugProtocolMessage): Promise<boolean> { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const anyMsg = msg as any; | ||
|
||
|
@@ -87,7 +87,7 @@ export class RunByLineController implements DebuggingDelegate { | |
return false; | ||
} | ||
|
||
public async willSendRequest(request: DebugProtocol.Request): Promise<void> { | ||
public async onWillSendRequest(request: DebugProtocol.Request): Promise<void> { | ||
if (request.command === 'configurationDone') { | ||
await this.initializeExecute(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
import { debug, NotebookDocument, DebugSession, DebugSessionOptions, DebugConfiguration } from 'vscode'; | ||
|
||
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) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might try using a deferred object for this idea of remembering the promise resolve/reject. If you create a deferred object, you can implement resolve/reject with it. |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this async if we're ignoring the promise returned by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because we have to wait to get the session |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this stuff be deleted, it seems like you copied the code to line 469
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it was needed, but after testing, its not, I'll remove it.