Skip to content

Commit 3207cc0

Browse files
authored
Add jump to cursor (#14594)
Extend context menu of editor with jump to cursor option. Extend context menu of editor margin with jump to cursor option. The option is visible only during the debugging session and if the debug adapter supports this. Fixes issue #14500 Signed-off-by: Nandish Mahadevaiah <[email protected]>
1 parent e27e4d4 commit 3207cc0

File tree

4 files changed

+84
-13
lines changed

4 files changed

+84
-13
lines changed

packages/debug/src/browser/debug-frontend-application-contribution.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { injectable, inject } from '@theia/core/shared/inversify';
2121
import * as monaco from '@theia/monaco-editor-core';
2222
import { MenuModelRegistry, CommandRegistry, MAIN_MENU_BAR, Command, Emitter, Mutable, CompoundMenuNodeRole } from '@theia/core/lib/common';
23-
import { EDITOR_LINENUMBER_CONTEXT_MENU, EditorManager } from '@theia/editor/lib/browser';
23+
import { EDITOR_CONTEXT_MENU, EDITOR_LINENUMBER_CONTEXT_MENU, EditorManager } from '@theia/editor/lib/browser';
2424
import { DebugSessionManager } from './debug-session-manager';
2525
import { DebugWidget } from './view/debug-widget';
2626
import { FunctionBreakpoint } from './breakpoint/breakpoint-marker';
@@ -241,6 +241,11 @@ export namespace DebugCommands {
241241
id: 'editor.debug.action.showDebugHover',
242242
label: 'Debug: Show Hover'
243243
});
244+
export const JUMP_TO_CURSOR = Command.toDefaultLocalizedCommand({
245+
id: 'editor.debug.action.jumpToCursor',
246+
category: DEBUG_CATEGORY,
247+
label: 'Jump to Cursor'
248+
});
244249

245250
export const RESTART_FRAME = Command.toDefaultLocalizedCommand({
246251
id: 'debug.frame.restart',
@@ -376,6 +381,9 @@ export namespace DebugEditorContextCommands {
376381
export const DISABLE_LOGPOINT = {
377382
id: 'debug.editor.context.logpoint.disable'
378383
};
384+
export const JUMP_TO_CURSOR = {
385+
id: 'debug.editor.context.jumpToCursor'
386+
};
379387
}
380388
export namespace DebugBreakpointWidgetCommands {
381389
export const ACCEPT = {
@@ -613,6 +621,11 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
613621
DebugCommands.DISABLE_ALL_BREAKPOINTS
614622
);
615623

624+
const DEBUG_EDITOR_CONTEXT_MENU_GROUP = [...EDITOR_CONTEXT_MENU, '2_debug'];
625+
registerMenuActions(DEBUG_EDITOR_CONTEXT_MENU_GROUP,
626+
DebugCommands.JUMP_TO_CURSOR
627+
);
628+
616629
registerMenuActions(DebugEditorModel.CONTEXT_MENU,
617630
{ ...DebugEditorContextCommands.ADD_BREAKPOINT, label: nls.localizeByDefault('Add Breakpoint') },
618631
{ ...DebugEditorContextCommands.ADD_CONDITIONAL_BREAKPOINT, label: DebugCommands.ADD_CONDITIONAL_BREAKPOINT.label },
@@ -624,7 +637,8 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
624637
{ ...DebugEditorContextCommands.REMOVE_LOGPOINT, label: DebugCommands.REMOVE_LOGPOINT.label },
625638
{ ...DebugEditorContextCommands.EDIT_LOGPOINT, label: DebugCommands.EDIT_LOGPOINT.label },
626639
{ ...DebugEditorContextCommands.ENABLE_LOGPOINT, label: nlsEnableBreakpoint('Logpoint') },
627-
{ ...DebugEditorContextCommands.DISABLE_LOGPOINT, label: nlsDisableBreakpoint('Logpoint') }
640+
{ ...DebugEditorContextCommands.DISABLE_LOGPOINT, label: nlsDisableBreakpoint('Logpoint') },
641+
{ ...DebugEditorContextCommands.JUMP_TO_CURSOR, label: nls.localizeByDefault('Jump to Cursor') }
628642
);
629643
menus.linkSubmenu(EDITOR_LINENUMBER_CONTEXT_MENU, DebugEditorModel.CONTEXT_MENU, { role: CompoundMenuNodeRole.Group });
630644
}
@@ -837,6 +851,20 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
837851
isEnabled: () => this.editors.canShowHover()
838852
});
839853

854+
registry.registerCommand(DebugCommands.JUMP_TO_CURSOR, {
855+
execute: () => {
856+
const model = this.editors.model;
857+
if (model && this.manager.currentThread) {
858+
this.manager.currentThread.jumpToCursor(
859+
model.editor.getResourceUri(),
860+
model.position
861+
);
862+
}
863+
},
864+
isEnabled: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto,
865+
isVisible: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto
866+
});
867+
840868
registry.registerCommand(DebugCommands.RESTART_FRAME, {
841869
execute: () => this.selectedFrame && this.selectedFrame.restart(),
842870
isEnabled: () => !!this.selectedFrame
@@ -936,6 +964,15 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
936964
isEnabled: position => this.isPosition(position) && !!this.editors.getLogpointEnabled(this.asPosition(position)),
937965
isVisible: position => this.isPosition(position) && !!this.editors.getLogpointEnabled(this.asPosition(position))
938966
});
967+
registry.registerCommand(DebugEditorContextCommands.JUMP_TO_CURSOR, {
968+
execute: position => {
969+
if (this.isPosition(position) && this.editors.currentUri && this.manager.currentThread) {
970+
this.manager.currentThread.jumpToCursor(this.editors.currentUri, this.asPosition(position));
971+
}
972+
},
973+
isEnabled: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto,
974+
isVisible: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto
975+
});
939976

940977
registry.registerCommand(DebugBreakpointWidgetCommands.ACCEPT, {
941978
execute: () => this.editors.acceptBreakpoint()

packages/debug/src/browser/debug-session.tsx

+11-7
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,16 @@ export class DebugSession implements CompositeTreeElement {
312312
return currentFrame ? currentFrame.getScopes() : [];
313313
}
314314

315+
showMessage(messageType: MessageType, message: string): void {
316+
this.messages.showMessage({
317+
type: messageType,
318+
text: message,
319+
options: {
320+
timeout: 10000
321+
}
322+
});
323+
}
324+
315325
async start(): Promise<void> {
316326
await this.initialize();
317327
await this.launchOrAttach();
@@ -343,13 +353,7 @@ export class DebugSession implements CompositeTreeElement {
343353
try {
344354
await this.sendRequest((this.configuration.request as keyof DebugRequestTypes), this.configuration);
345355
} catch (reason) {
346-
this.messages.showMessage({
347-
type: MessageType.Error,
348-
text: reason.message || 'Debug session initialization failed. See console for details.',
349-
options: {
350-
timeout: 10000
351-
}
352-
});
356+
this.showMessage(MessageType.Error, reason.message || 'Debug session initialization failed. See console for details.');
353357
throw reason;
354358
}
355359
}

packages/debug/src/browser/editor/debug-editor-service.ts

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { DebugEditorModel, DebugEditorModelFactory } from './debug-editor-model'
2424
import { BreakpointManager, SourceBreakpointsChangeEvent } from '../breakpoint/breakpoint-manager';
2525
import { DebugSourceBreakpoint } from '../model/debug-source-breakpoint';
2626
import { DebugBreakpointWidget } from './debug-breakpoint-widget';
27+
import URI from '@theia/core/lib/common/uri';
2728

2829
@injectable()
2930
export class DebugEditorService {
@@ -71,6 +72,11 @@ export class DebugEditorService {
7172
return uri && this.models.get(uri.toString());
7273
}
7374

75+
get currentUri(): URI | undefined {
76+
const { currentEditor } = this.editors;
77+
return currentEditor && currentEditor.getResourceUri();
78+
}
79+
7480
getLogpoint(position: monaco.Position): DebugSourceBreakpoint | undefined {
7581
const logpoint = this.anyBreakpoint(position);
7682
return logpoint && logpoint.logMessage ? logpoint : undefined;

packages/debug/src/browser/model/debug-thread.tsx

+28-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
// *****************************************************************************
1616

1717
import * as React from '@theia/core/shared/react';
18-
import { CancellationTokenSource, Emitter, Event, nls } from '@theia/core';
18+
import { CancellationTokenSource, Emitter, Event, MessageType, nls } from '@theia/core';
1919
import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol';
2020
import { TreeElement } from '@theia/core/lib/browser/source-tree';
2121
import { DebugStackFrame } from './debug-stack-frame';
2222
import { DebugSession } from '../debug-session';
23+
import * as monaco from '@theia/monaco-editor-core';
24+
import URI from '@theia/core/lib/common/uri';
2325

2426
export type StoppedDetails = DebugProtocol.StoppedEvent['body'] & {
2527
framesErrorMessage?: string
@@ -111,6 +113,28 @@ export class DebugThread extends DebugThreadData implements TreeElement {
111113
return this.session.sendRequest('pause', this.toArgs());
112114
}
113115

116+
get supportsGoto(): boolean {
117+
return !!this.session.capabilities.supportsGotoTargetsRequest;
118+
}
119+
120+
async jumpToCursor(uri: URI, position: monaco.Position): Promise<DebugProtocol.GotoResponse | undefined> {
121+
const source = await this.session?.toDebugSource(uri);
122+
123+
if (!source) {
124+
return undefined;
125+
}
126+
127+
const response: DebugProtocol.GotoTargetsResponse = await this.session.sendRequest('gotoTargets', { source, line: position.lineNumber, column: position.column });
128+
129+
if (response && response.body.targets.length === 0) {
130+
this.session.showMessage(MessageType.Warning, 'No executable code is associated at the current cursor position.');
131+
return;
132+
}
133+
134+
const targetId = response.body.targets[0].id;
135+
return this.session.sendRequest('goto', this.toArgs({ targetId }));
136+
}
137+
114138
async getExceptionInfo(): Promise<DebugExceptionInfo | undefined> {
115139
if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') {
116140
if (this.session.capabilities.supportsExceptionInfoRequest) {
@@ -261,8 +285,8 @@ export class DebugThread extends DebugThreadData implements TreeElement {
261285
const localizedReason = this.getLocalizedReason(reason);
262286

263287
return reason
264-
? nls.localizeByDefault('Paused on {0}', localizedReason)
265-
: nls.localizeByDefault('Paused');
288+
? nls.localizeByDefault('Paused on {0}', localizedReason)
289+
: nls.localizeByDefault('Paused');
266290
}
267291

268292
protected getLocalizedReason(reason: string | undefined): string {
@@ -281,7 +305,7 @@ export class DebugThread extends DebugThreadData implements TreeElement {
281305
return nls.localize('theia/debug/goto', 'goto');
282306
case 'function breakpoint':
283307
return nls.localize('theia/debug/functionBreakpoint', 'function breakpoint');
284-
case 'data breakpoint':
308+
case 'data breakpoint':
285309
return nls.localize('theia/debug/dataBreakpoint', 'data breakpoint');
286310
case 'instruction breakpoint':
287311
return nls.localize('theia/debug/instructionBreakpoint', 'instruction breakpoint');

0 commit comments

Comments
 (0)