Skip to content

Commit

Permalink
Add debug API for call stack selection changes (63943) (#179132)
Browse files Browse the repository at this point in the history
* feat: Initial pass at Debug Focus, listener for thread or stack frame
selection (#63943)

Add debug.onDidChangeDebugFocus. May provide ids for session, thread,
and stackFrame, as appropriate.

Fixes: #63943 api should provide thread/stack frame id (paraphrasing)

* chore: small comment tweaks in proposal

* fix: bad copy/paste in proposed. moved types out of 'debug' namespace

* fix: separate 'debugFocus' into thread and stackFrame specific
listeners, and create separate contexts for each

* fix: Revert prev change, switch to single api with union type

This reverts commit c308bc3.

* fix: rename accessor from 'focus' to 'stackFrameFocus'

* fix: review comments; remove unused type, imrpove comments

* fixL review comments, type change:  sessionID property cannot be undefined

* Remove comments

---------

Co-authored-by: Rob Lourens <roblourens@gmail.com>
  • Loading branch information
mblout and roblourens committed Apr 17, 2023
1 parent d3d397c commit 2de3b04
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 6 deletions.
25 changes: 24 additions & 1 deletion src/vs/workbench/api/browser/mainThreadDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { URI as uri, UriComponents } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions, IDebugConfiguration
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions, IDebugConfiguration, IThreadFocusDto, IStackFrameFocusDto
} from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import severity from 'vs/base/common/severity';
Expand Down Expand Up @@ -56,6 +56,29 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
this._debugConfigurationProviders = new Map();
this._debugAdapterDescriptorFactories = new Map();
this._sessions = new Set();

this._toDispose.add(this.debugService.getViewModel().onDidFocusThread(({ thread, explicit, session }) => {
if (session) {
const dto: IThreadFocusDto = {
kind: 'thread',
threadId: thread?.threadId,
sessionId: session!.getId(),
};
this._proxy.$acceptStackFrameFocus(dto);
}
}));

this._toDispose.add(this.debugService.getViewModel().onDidFocusStackFrame(({ stackFrame, explicit, session }) => {
if (session) {
const dto: IStackFrameFocusDto = {
kind: 'stackFrame',
threadId: stackFrame?.thread.threadId,
frameId: stackFrame?.frameId,
sessionId: session.getId(),
};
this._proxy.$acceptStackFrameFocus(dto);
}
}));
}

public dispose(): void {
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get breakpoints() {
return extHostDebugService.breakpoints;
},
get stackFrameFocus() {
return extHostDebugService.stackFrameFocus;
},
onDidStartDebugSession(listener, thisArg?, disposables?) {
return extHostDebugService.onDidStartDebugSession(listener, thisArg, disposables);
},
Expand All @@ -1120,6 +1123,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
onDidChangeBreakpoints(listener, thisArgs?, disposables?) {
return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables);
},
onDidChangeStackFrameFocus(listener, thisArg?, disposables?) {
return extHostDebugService.onDidChangeStackFrameFocus(listener, thisArg, disposables);
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, triggerKind?: vscode.DebugConfigurationProviderTriggerKind) {
return extHostDebugService.registerDebugConfigurationProvider(debugType, provider, triggerKind || DebugConfigurationProviderTriggerKind.Initial);
},
Expand Down
15 changes: 15 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,20 @@ export interface IDebugSessionFullDto {

export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID;

export interface IThreadFocusDto {
kind: 'thread';
sessionId: string;
threadId: number | undefined;
}

export interface IStackFrameFocusDto {
kind: 'stackFrame';
sessionId: string;
threadId: number | undefined;
frameId: number | undefined;
}


export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Promise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
Expand All @@ -2048,6 +2062,7 @@ export interface ExtHostDebugServiceShape {
$acceptDebugSessionCustomEvent(session: IDebugSessionDto, event: any): void;
$acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void;
$acceptDebugSessionNameChanged(session: IDebugSessionDto, name: string): void;
$acceptStackFrameFocus(focus: IThreadFocusDto | IStackFrameFocusDto | undefined): void;
}


Expand Down
44 changes: 43 additions & 1 deletion src/vs/workbench/api/common/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ISignService } from 'vs/platform/sign/common/sign';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { DebugSessionUUID, ExtHostDebugServiceShape, IBreakpointsDeltaDto, IDebugSessionDto, IFunctionBreakpointDto, ISourceMultiBreakpointDto, MainContext, MainThreadDebugServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { DebugSessionUUID, ExtHostDebugServiceShape, IBreakpointsDeltaDto, IThreadFocusDto, IStackFrameFocusDto, IDebugSessionDto, IFunctionBreakpointDto, ISourceMultiBreakpointDto, MainContext, MainThreadDebugServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
Expand Down Expand Up @@ -41,6 +41,8 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
onDidReceiveDebugSessionCustomEvent: Event<vscode.DebugSessionCustomEvent>;
onDidChangeBreakpoints: Event<vscode.BreakpointsChangeEvent>;
breakpoints: vscode.Breakpoint[];
onDidChangeStackFrameFocus: Event<vscode.ThreadFocus | vscode.StackFrameFocus | undefined>;
stackFrameFocus: vscode.ThreadFocus | vscode.StackFrameFocus | undefined;

addBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise<void>;
removeBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise<void>;
Expand Down Expand Up @@ -91,6 +93,9 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E

private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;

private _stackFrameFocus: vscode.ThreadFocus | vscode.StackFrameFocus | undefined;
private readonly _onDidChangeStackFrameFocus: Emitter<vscode.ThreadFocus | vscode.StackFrameFocus | undefined>;

private _debugAdapters: Map<number, IDebugAdapter>;
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;

Expand Down Expand Up @@ -129,6 +134,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
}
});

this._onDidChangeStackFrameFocus = new Emitter<vscode.ThreadFocus | vscode.StackFrameFocus | undefined>();

this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy);

this._breakpoints = new Map<string, vscode.Breakpoint>();
Expand Down Expand Up @@ -190,6 +197,15 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E

// extension debug API


get stackFrameFocus(): vscode.ThreadFocus | vscode.StackFrameFocus | undefined {
return this._stackFrameFocus;
}

get onDidChangeStackFrameFocus(): Event<vscode.ThreadFocus | vscode.StackFrameFocus | undefined> {
return this._onDidChangeStackFrameFocus.event;
}

get onDidChangeBreakpoints(): Event<vscode.BreakpointsChangeEvent> {
return this._onDidChangeBreakpoints.event;
}
Expand Down Expand Up @@ -584,6 +600,32 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
this.fireBreakpointChanges(a, r, c);
}

public async $acceptStackFrameFocus(focusDto: IThreadFocusDto | IStackFrameFocusDto): Promise<void> {
let focus: vscode.ThreadFocus | vscode.StackFrameFocus;
const session = focusDto.sessionId ? await this.getSession(focusDto.sessionId) : undefined;
if (!session) {
throw new Error('no DebugSession found for debug focus context');
}

if (focusDto.kind === 'thread') {
focus = {
kind: focusDto.kind,
threadId: focusDto.threadId,
session,
};
} else {
focus = {
kind: focusDto.kind,
threadId: focusDto.threadId,
frameId: focusDto.frameId,
session,
};
}

this._stackFrameFocus = focus;
this._onDidChangeStackFrameFocus.fire(focus);
}

public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise<vscode.DebugConfiguration[]> {
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/contrib/debug/common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,8 @@ export interface IViewModel extends ITreeElement {
isMultiSessionView(): boolean;

onDidFocusSession: Event<IDebugSession | undefined>;
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined; explicit: boolean }>;
onDidFocusThread: Event<{ thread: IThread | undefined; explicit: boolean; session: IDebugSession | undefined }>;
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined; explicit: boolean; session: IDebugSession | undefined }>;
onDidSelectExpression: Event<{ expression: IExpression; settingWatch: boolean } | undefined>;
onDidEvaluateLazyExpression: Event<IExpressionContainer>;
onWillUpdateViews: Event<void>;
Expand Down
17 changes: 14 additions & 3 deletions src/vs/workbench/contrib/debug/common/debugViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export class ViewModel implements IViewModel {
private _focusedThread: IThread | undefined;
private selectedExpression: { expression: IExpression; settingWatch: boolean } | undefined;
private readonly _onDidFocusSession = new Emitter<IDebugSession | undefined>();
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined; explicit: boolean }>();
private readonly _onDidFocusThread = new Emitter<{ thread: IThread | undefined; explicit: boolean; session: IDebugSession | undefined }>();
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined; explicit: boolean; session: IDebugSession | undefined }>();
private readonly _onDidSelectExpression = new Emitter<{ expression: IExpression; settingWatch: boolean } | undefined>();
private readonly _onDidEvaluateLazyExpression = new Emitter<IExpressionContainer>();
private readonly _onWillUpdateViews = new Emitter<void>();
Expand Down Expand Up @@ -74,6 +75,8 @@ export class ViewModel implements IViewModel {
setFocus(stackFrame: IStackFrame | undefined, thread: IThread | undefined, session: IDebugSession | undefined, explicit: boolean): void {
const shouldEmitForStackFrame = this._focusedStackFrame !== stackFrame;
const shouldEmitForSession = this._focusedSession !== session;
const shouldEmitForThread = this._focusedThread !== thread;


this._focusedStackFrame = stackFrame;
this._focusedThread = thread;
Expand All @@ -98,16 +101,24 @@ export class ViewModel implements IViewModel {
if (shouldEmitForSession) {
this._onDidFocusSession.fire(session);
}

// should not call onDidFocusThread if onDidFocusStackFrame is called.
if (shouldEmitForStackFrame) {
this._onDidFocusStackFrame.fire({ stackFrame, explicit });
this._onDidFocusStackFrame.fire({ stackFrame, explicit, session });
} else if (shouldEmitForThread) {
this._onDidFocusThread.fire({ thread, explicit, session });
}
}

get onDidFocusSession(): Event<IDebugSession | undefined> {
return this._onDidFocusSession.event;
}

get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame | undefined; explicit: boolean }> {
get onDidFocusThread(): Event<{ thread: IThread | undefined; explicit: boolean; session: IDebugSession | undefined }> {
return this._onDidFocusThread.event;
}

get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame | undefined; explicit: boolean; session: IDebugSession | undefined }> {
return this._onDidFocusStackFrame.event;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const allApiProposals = Object.freeze({
contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts',
contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts',
customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts',
debugFocus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.debugFocus.d.ts',
diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts',
diffContentOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffContentOptions.d.ts',
documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts',
Expand Down
55 changes: 55 additions & 0 deletions src/vscode-dts/vscode.proposed.debugFocus.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// See https://github.com/microsoft/vscode/issues/63943

export interface ThreadFocus {
kind: 'thread';

/**
* Debug session for thread.
*/
readonly session: DebugSession;

/**
* Id of the associated thread (DAP id). May be undefined if thread has become unselected.
*/
readonly threadId: number | undefined;
}

export interface StackFrameFocus {
kind: 'stackFrame';

/**
* Debug session for thread.
*/
readonly session: DebugSession;

/**
* Id of the associated thread (DAP id). May be undefined if a frame is unselected.
*/
readonly threadId: number | undefined;
/**
* Id of the stack frame (DAP id). May be undefined if a frame is unselected.
*/
readonly frameId: number | undefined;
}


export namespace debug {
/**
* The currently focused thread or stack frame id, or `undefined` if this has not been set. (e.g. not in debug mode).
*/
export let stackFrameFocus: ThreadFocus | StackFrameFocus | undefined;

/**
* An {@link Event} which fires when the {@link debug.stackFrameFocus} changes. Provides a sessionId. threadId is not undefined
* when a thread of frame has gained focus. frameId is defined when a stackFrame has gained focus.
*/
export const onDidChangeStackFrameFocus: Event<ThreadFocus | StackFrameFocus | undefined>;
}
}

0 comments on commit 2de3b04

Please sign in to comment.