Skip to content
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

debug: improve step in target UI #152131

Merged
merged 3 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/vs/workbench/contrib/debug/browser/debug.contribution.ts
Expand Up @@ -16,11 +16,11 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import {
IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED,
} from 'vs/workbench/contrib/debug/common/debug';
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider';
import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
Expand Down Expand Up @@ -114,6 +114,7 @@ registerDebugCommandPaletteItem(RESTART_SESSION_ID, RESTART_LABEL);
registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE);
registerDebugCommandPaletteItem(STEP_OVER_ID, STEP_OVER_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugCommandPaletteItem(STEP_INTO_ID, STEP_INTO_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugCommandPaletteItem(STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')));
registerDebugCommandPaletteItem(STEP_OUT_ID, STEP_OUT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugCommandPaletteItem(PAUSE_ID, PAUSE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('running'));
registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED));
Expand Down
78 changes: 75 additions & 3 deletions src/vs/workbench/contrib/debug/browser/debugCommands.ts
Expand Up @@ -8,7 +8,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService } from 'vs/platform/list/browser/listService';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_IN_DEBUG_REPL } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_IN_DEBUG_REPL, CONTEXT_STEP_INTO_TARGETS_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
Expand All @@ -25,7 +25,7 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { deepClone } from 'vs/base/common/objects';
import { isWeb, isWindows } from 'vs/base/common/platform';
Expand All @@ -41,6 +41,7 @@ export const RESTART_SESSION_ID = 'workbench.action.debug.restart';
export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread';
export const STEP_OVER_ID = 'workbench.action.debug.stepOver';
export const STEP_INTO_ID = 'workbench.action.debug.stepInto';
export const STEP_INTO_TARGET_ID = 'workbench.action.debug.stepIntoTarget';
export const STEP_OUT_ID = 'workbench.action.debug.stepOut';
export const PAUSE_ID = 'workbench.action.debug.pause';
export const DISCONNECT_ID = 'workbench.action.debug.disconnect';
Expand All @@ -65,6 +66,7 @@ export const PREV_DEBUG_CONSOLE_ID = 'workbench.action.debug.prevConsole';
export const RESTART_LABEL = nls.localize('restartDebug', "Restart");
export const STEP_OVER_LABEL = nls.localize('stepOverDebug', "Step Over");
export const STEP_INTO_LABEL = nls.localize('stepIntoDebug', "Step Into");
export const STEP_INTO_TARGET_LABEL = nls.localize('stepIntoTargetDebug', "Step Into Target");
export const STEP_OUT_LABEL = nls.localize('stepOutDebug', "Step Out");
export const PAUSE_LABEL = nls.localize('pauseDebug', "Pause");
export const DISCONNECT_LABEL = nls.localize('disconnect', "Disconnect");
Expand Down Expand Up @@ -332,10 +334,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
});

// Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut
const STEP_INTO_KEYBINDING = (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11;

KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STEP_INTO_ID,
weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging
primary: (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11, // Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut
primary: STEP_INTO_KEYBINDING,
// Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times
when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
Expand Down Expand Up @@ -373,6 +378,73 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
});


KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STEP_INTO_TARGET_ID,
primary: STEP_INTO_KEYBINDING | KeyMod.CtrlCmd,
when: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')),
weight: KeybindingWeight.WorkbenchContrib,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const quickInputService = accessor.get(IQuickInputService);
const debugService = accessor.get(IDebugService);
const session = debugService.getViewModel().focusedSession;
const frame = debugService.getViewModel().focusedStackFrame;
if (!frame || !session) {
return;
}

const editor = await accessor.get(IEditorService).openEditor({
resource: frame.source.uri,
options: { revealIfOpened: true }
});

let codeEditor: ICodeEditor | undefined;
if (editor) {
const ctrl = editor?.getControl();
if (isCodeEditor(ctrl)) {
codeEditor = ctrl;
}
}

interface ITargetItem extends IQuickPickItem {
target: DebugProtocol.StepInTarget;
}

const qp = quickInputService.createQuickPick<ITargetItem>();
qp.busy = true;
qp.show();

qp.onDidChangeActive(([item]) => {
if (codeEditor && item && item.target.line !== undefined) {
codeEditor.revealLineInCenterIfOutsideViewport(item.target.line);
codeEditor.setSelection({
startLineNumber: item.target.line,
startColumn: item.target.column || 1,
endLineNumber: item.target.endLine || item.target.line,
endColumn: item.target.endColumn || item.target.column || 1,
});
}
});

qp.onDidAccept(() => {
if (qp.activeItems.length) {
session.stepIn(frame.thread.threadId, qp.activeItems[0].target.id);
}
});

qp.onDidHide(() => qp.dispose());

session.stepInTargets(frame.frameId).then(targets => {
qp.busy = false;
if (targets?.length) {
qp.items = targets?.map(target => ({ target, label: target.label }));
} else {
qp.placeholder = nls.localize('editor.debug.action.stepIntoTargets.none', "No step targets available");
}
});
}
});

async function stopHandler(accessor: ServicesAccessor, _: string, context: CallStackContext | unknown, disconnect: boolean, suspend?: boolean): Promise<void> {
const debugService = accessor.get(IDebugService);
let session: IDebugSession | undefined;
Expand Down
79 changes: 60 additions & 19 deletions src/vs/workbench/contrib/debug/browser/debugEditorActions.ts
Expand Up @@ -16,14 +16,16 @@ import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpo
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { PanelFocusContext } from 'vs/workbench/common/contextkeys';
import { IViewsService } from 'vs/workbench/common/views';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Action } from 'vs/base/common/actions';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { registerAction2, MenuId, Action2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { MessageController } from 'vs/editor/contrib/message/browser/messageController';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { Position } from 'vs/editor/common/core/position';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Action } from 'vs/base/common/actions';

class ToggleBreakpointAction extends EditorAction {
constructor() {
Expand Down Expand Up @@ -329,16 +331,18 @@ class ShowDebugHoverAction extends EditorAction {
}
}

const NO_TARGETS_MESSAGE = nls.localize('editor.debug.action.stepIntoTargets.notAvailable', "Step targets are not available here");

class StepIntoTargetsAction extends EditorAction {

public static readonly ID = 'editor.debug.action.stepIntoTargets';
public static readonly LABEL = nls.localize({ key: 'stepIntoTargets', comment: ['Step Into Targets lets the user step into an exact function he or she is interested in.'] }, "Step Into Targets...");
public static readonly LABEL = nls.localize({ key: 'stepIntoTargets', comment: ['Step Into Targets lets the user step into an exact function he or she is interested in.'] }, "Step Into Target");

constructor() {
super({
id: StepIntoTargetsAction.ID,
label: StepIntoTargetsAction.LABEL,
alias: 'Debug: Step Into Targets...',
alias: 'Debug: Step Into Target',
precondition: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus),
contextMenuOpts: {
group: 'debug',
Expand All @@ -353,26 +357,63 @@ class StepIntoTargetsAction extends EditorAction {
const uriIdentityService = accessor.get(IUriIdentityService);
const session = debugService.getViewModel().focusedSession;
const frame = debugService.getViewModel().focusedStackFrame;
const selection = editor.getSelection();

if (session && frame && editor.hasModel() && uriIdentityService.extUri.isEqual(editor.getModel().uri, frame.source.uri)) {
const targets = await session.stepInTargets(frame.frameId);
if (!targets) {
return;
const targetPosition = selection?.getPosition() || (frame && { lineNumber: frame.range.startLineNumber, column: frame.range.startColumn });

if (!session || !frame || !editor.hasModel() || !uriIdentityService.extUri.isEqual(editor.getModel().uri, frame.source.uri)) {
if (targetPosition) {
MessageController.get(editor)?.showMessage(NO_TARGETS_MESSAGE, targetPosition);
}
return;
}

editor.revealLineInCenterIfOutsideViewport(frame.range.startLineNumber);
const cursorCoords = editor.getScrolledVisiblePosition({ lineNumber: frame.range.startLineNumber, column: frame.range.startColumn });
const editorCoords = getDomNodePagePosition(editor.getDomNode());
const x = editorCoords.left + cursorCoords.left;
const y = editorCoords.top + cursorCoords.top + cursorCoords.height;

contextMenuService.showContextMenu({
getAnchor: () => ({ x, y }),
getActions: () => {
return targets.map(t => new Action(`stepIntoTarget:${t.id}`, t.label, undefined, true, () => session.stepIn(frame.thread.threadId, t.id)));
const targets = await session.stepInTargets(frame.frameId);
if (!targets?.length) {
MessageController.get(editor)?.showMessage(NO_TARGETS_MESSAGE, targetPosition!);
return;
}

// If there is a selection, try to find the best target with a position to step into.
if (selection) {
const positionalTargets: { start: Position; end?: Position; target: DebugProtocol.StepInTarget }[] = [];
for (const target of targets) {
if (target.line) {
positionalTargets.push({
start: new Position(target.line, target.column || 1),
end: target.endLine ? new Position(target.endLine, target.endColumn || 1) : undefined,
target
});
}
});
}

positionalTargets.sort((a, b) => b.start.lineNumber - a.start.lineNumber || b.start.column - a.start.column);

const needle = selection.getPosition();

// Try to find a target with a start and end that is around the cursor
// position. Or, if none, whatever is before the cursor.
const best = positionalTargets.find(t => t.end && needle.isBefore(t.end) && t.start.isBeforeOrEqual(needle)) || positionalTargets.find(t => t.end === undefined && t.start.isBeforeOrEqual(needle));
if (best) {
session.stepIn(frame.thread.threadId, best.target.id);
return;
}
}

// Otherwise, show a context menu and have the user pick a target
editor.revealLineInCenterIfOutsideViewport(frame.range.startLineNumber);
const cursorCoords = editor.getScrolledVisiblePosition(targetPosition!);
const editorCoords = getDomNodePagePosition(editor.getDomNode());
const x = editorCoords.left + cursorCoords.left;
const y = editorCoords.top + cursorCoords.top + cursorCoords.height;

contextMenuService.showContextMenu({
getAnchor: () => ({ x, y }),
getActions: () => {
return targets.map(t => new Action(`stepIntoTarget:${t.id}`, t.label, undefined, true, () => session.stepIn(frame.thread.threadId, t.id)));
}
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/debug/common/debug.ts
Expand Up @@ -373,7 +373,7 @@ export interface IDebugSession extends ITreeElement {
restartFrame(frameId: number, threadId: number): Promise<void>;
next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepInTargets(frameId: number): Promise<{ id: number; label: string }[] | undefined>;
stepInTargets(frameId: number): Promise<DebugProtocol.StepInTarget[] | undefined>;
stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
continue(threadId: number): Promise<void>;
Expand Down