mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-14 04:00:38 +01:00
Merge pull request #274414 from microsoft/brchen/debug-context-menu
Fix watch view context menu when Variables pane is hidden
This commit is contained in:
@@ -566,8 +566,8 @@ export class DebugSession implements IDebugSession {
|
||||
return this._dataBreakpointInfo({ name: address, bytes, asAddress: true });
|
||||
}
|
||||
|
||||
dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null; description: string; canPersist?: boolean } | undefined> {
|
||||
return this._dataBreakpointInfo({ name, variablesReference });
|
||||
dataBreakpointInfo(name: string, variablesReference?: number, frameId?: number): Promise<{ dataId: string | null; description: string; canPersist?: boolean } | undefined> {
|
||||
return this._dataBreakpointInfo({ name, variablesReference, frameId });
|
||||
}
|
||||
|
||||
private async _dataBreakpointInfo(args: DebugProtocol.DataBreakpointInfoArguments): Promise<{ dataId: string | null; description: string; canPersist?: boolean } | undefined> {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../pla
|
||||
import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js';
|
||||
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
|
||||
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js';
|
||||
@@ -68,7 +69,8 @@ export class WatchExpressionsView extends ViewPane implements IDebugViewWithVari
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IHoverService hoverService: IHoverService,
|
||||
@IMenuService private readonly menuService: IMenuService
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);
|
||||
|
||||
@@ -221,15 +223,14 @@ export class WatchExpressionsView extends ViewPane implements IDebugViewWithVari
|
||||
|
||||
const selection = this.tree.getSelection();
|
||||
|
||||
const contextKeyService = element && await getContextForWatchExpressionMenuWithDataAccess(this.contextKeyService, element);
|
||||
const contextKeyService = element && await getContextForWatchExpressionMenuWithDataAccess(this.contextKeyService, element, this.debugService, this.logService);
|
||||
const menu = this.menuService.getMenuActions(MenuId.DebugWatchContext, contextKeyService, { arg: element, shouldForwardArgs: false });
|
||||
const { secondary } = getContextMenuActions(menu, 'inline');
|
||||
|
||||
// const actions = getFlatContextMenuActions(this.menu.getActions({ arg: element, shouldForwardArgs: true }));
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => secondary,
|
||||
getActionsContext: () => element && selection.includes(element) ? selection : element ? [element] : [],
|
||||
getActionsContext: () => element && selection.includes(element) ? selection : element ? [element] : []
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -409,14 +410,48 @@ function getContextForWatchExpressionMenu(parentContext: IContextKeyService, exp
|
||||
/**
|
||||
* Gets a context key overlay that has context for the given expression, including data access info.
|
||||
*/
|
||||
async function getContextForWatchExpressionMenuWithDataAccess(parentContext: IContextKeyService, expression: IExpression) {
|
||||
async function getContextForWatchExpressionMenuWithDataAccess(parentContext: IContextKeyService, expression: IExpression, debugService: IDebugService, logService: ILogService) {
|
||||
const session = expression.getSession();
|
||||
if (!session || !session.capabilities.supportsDataBreakpoints) {
|
||||
return getContextForWatchExpressionMenu(parentContext, expression);
|
||||
}
|
||||
|
||||
const contextKeys: [string, unknown][] = [];
|
||||
const dataBreakpointInfoResponse = await session.dataBreakpointInfo('evaluateName' in expression ? expression.evaluateName as string : expression.name);
|
||||
const stackFrame = debugService.getViewModel().focusedStackFrame;
|
||||
let dataBreakpointInfoResponse;
|
||||
|
||||
try {
|
||||
// Per DAP spec:
|
||||
// - If evaluateName is available: use it as an expression (top-level evaluation)
|
||||
// - Otherwise, check if it's a Variable: use name + parent reference (container-relative)
|
||||
// - Otherwise: use name as an expression
|
||||
if ('evaluateName' in expression && expression.evaluateName) {
|
||||
// Use evaluateName if available (more precise for evaluation context)
|
||||
dataBreakpointInfoResponse = await session.dataBreakpointInfo(
|
||||
expression.evaluateName as string,
|
||||
undefined,
|
||||
stackFrame?.frameId
|
||||
);
|
||||
} else if (expression instanceof Variable) {
|
||||
// Variable without evaluateName: use name relative to parent container
|
||||
dataBreakpointInfoResponse = await session.dataBreakpointInfo(
|
||||
expression.name,
|
||||
expression.parent.reference,
|
||||
stackFrame?.frameId
|
||||
);
|
||||
} else {
|
||||
// Expression without evaluateName: use name as the expression to evaluate
|
||||
dataBreakpointInfoResponse = await session.dataBreakpointInfo(
|
||||
expression.name,
|
||||
undefined,
|
||||
stackFrame?.frameId
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// silently continue without data breakpoint support for this item
|
||||
logService.error('Failed to get data breakpoint info for watch expression:', error);
|
||||
}
|
||||
|
||||
const dataBreakpointId = dataBreakpointInfoResponse?.dataId;
|
||||
const dataBreakpointAccessTypes = dataBreakpointInfoResponse?.accessTypes;
|
||||
setDataBreakpointInfoResponse(dataBreakpointInfoResponse);
|
||||
|
||||
@@ -445,7 +445,7 @@ export interface IDebugSession extends ITreeElement, IDisposable {
|
||||
|
||||
sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise<void>;
|
||||
sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void>;
|
||||
dataBreakpointInfo(name: string, variablesReference?: number): Promise<IDataBreakpointInfoResponse | undefined>;
|
||||
dataBreakpointInfo(name: string, variablesReference?: number, frameId?: number): Promise<IDataBreakpointInfoResponse | undefined>;
|
||||
dataBytesBreakpointInfo(address: string, bytes: number): Promise<IDataBreakpointInfoResponse | undefined>;
|
||||
sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise<void>;
|
||||
sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise<void>;
|
||||
|
||||
@@ -235,7 +235,7 @@ export class MockSession implements IDebugSession {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined } | undefined> {
|
||||
dataBreakpointInfo(name: string, variablesReference?: number | undefined, frameId?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined } | undefined> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user