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

Add jump to cursor #14594

Merged
merged 3 commits into from
Jan 6, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { injectable, inject } from '@theia/core/shared/inversify';
import * as monaco from '@theia/monaco-editor-core';
import { MenuModelRegistry, CommandRegistry, MAIN_MENU_BAR, Command, Emitter, Mutable, CompoundMenuNodeRole } from '@theia/core/lib/common';
import { EDITOR_LINENUMBER_CONTEXT_MENU, EditorManager } from '@theia/editor/lib/browser';
import { EDITOR_CONTEXT_MENU, EDITOR_LINENUMBER_CONTEXT_MENU, EditorManager } from '@theia/editor/lib/browser';
import { DebugSessionManager } from './debug-session-manager';
import { DebugWidget } from './view/debug-widget';
import { FunctionBreakpoint } from './breakpoint/breakpoint-marker';
Expand Down Expand Up @@ -241,6 +241,11 @@ export namespace DebugCommands {
id: 'editor.debug.action.showDebugHover',
label: 'Debug: Show Hover'
});
export const JUMP_TO_CURSOR = Command.toDefaultLocalizedCommand({
id: 'editor.debug.action.jumpToCursor',
category: DEBUG_CATEGORY,
label: 'Jump to Cursor'
});

export const RESTART_FRAME = Command.toDefaultLocalizedCommand({
id: 'debug.frame.restart',
Expand Down Expand Up @@ -376,6 +381,9 @@ export namespace DebugEditorContextCommands {
export const DISABLE_LOGPOINT = {
id: 'debug.editor.context.logpoint.disable'
};
export const JUMP_TO_CURSOR = {
id: 'debug.editor.context.jumpToCursor'
};
}
export namespace DebugBreakpointWidgetCommands {
export const ACCEPT = {
Expand Down Expand Up @@ -613,6 +621,11 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
DebugCommands.DISABLE_ALL_BREAKPOINTS
);

const DEBUG_EDITOR_CONTEXT_MENU_GROUP = [...EDITOR_CONTEXT_MENU, '2_debug'];
registerMenuActions(DEBUG_EDITOR_CONTEXT_MENU_GROUP,
DebugCommands.JUMP_TO_CURSOR
);

registerMenuActions(DebugEditorModel.CONTEXT_MENU,
{ ...DebugEditorContextCommands.ADD_BREAKPOINT, label: nls.localizeByDefault('Add Breakpoint') },
{ ...DebugEditorContextCommands.ADD_CONDITIONAL_BREAKPOINT, label: DebugCommands.ADD_CONDITIONAL_BREAKPOINT.label },
Expand All @@ -624,7 +637,8 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
{ ...DebugEditorContextCommands.REMOVE_LOGPOINT, label: DebugCommands.REMOVE_LOGPOINT.label },
{ ...DebugEditorContextCommands.EDIT_LOGPOINT, label: DebugCommands.EDIT_LOGPOINT.label },
{ ...DebugEditorContextCommands.ENABLE_LOGPOINT, label: nlsEnableBreakpoint('Logpoint') },
{ ...DebugEditorContextCommands.DISABLE_LOGPOINT, label: nlsDisableBreakpoint('Logpoint') }
{ ...DebugEditorContextCommands.DISABLE_LOGPOINT, label: nlsDisableBreakpoint('Logpoint') },
{ ...DebugEditorContextCommands.JUMP_TO_CURSOR, label: nls.localizeByDefault('Jump to Cursor') }
);
menus.linkSubmenu(EDITOR_LINENUMBER_CONTEXT_MENU, DebugEditorModel.CONTEXT_MENU, { role: CompoundMenuNodeRole.Group });
}
Expand Down Expand Up @@ -837,6 +851,20 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isEnabled: () => this.editors.canShowHover()
});

registry.registerCommand(DebugCommands.JUMP_TO_CURSOR, {
execute: () => {
const model = this.editors.model;
if (model && this.manager.currentThread) {
this.manager.currentThread.jumpToCursor(
model.editor.getResourceUri(),
model.position
);
}
},
isEnabled: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto,
isVisible: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto
});

registry.registerCommand(DebugCommands.RESTART_FRAME, {
execute: () => this.selectedFrame && this.selectedFrame.restart(),
isEnabled: () => !!this.selectedFrame
Expand Down Expand Up @@ -936,6 +964,15 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isEnabled: position => this.isPosition(position) && !!this.editors.getLogpointEnabled(this.asPosition(position)),
isVisible: position => this.isPosition(position) && !!this.editors.getLogpointEnabled(this.asPosition(position))
});
registry.registerCommand(DebugEditorContextCommands.JUMP_TO_CURSOR, {
execute: position => {
if (this.isPosition(position) && this.editors.currentUri && this.manager.currentThread) {
this.manager.currentThread.jumpToCursor(this.editors.currentUri, this.asPosition(position));
}
},
isEnabled: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto,
isVisible: () => !!this.manager.currentThread && this.manager.currentThread.supportsGoto
});

registry.registerCommand(DebugBreakpointWidgetCommands.ACCEPT, {
execute: () => this.editors.acceptBreakpoint()
Expand Down
18 changes: 11 additions & 7 deletions packages/debug/src/browser/debug-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ export class DebugSession implements CompositeTreeElement {
return currentFrame ? currentFrame.getScopes() : [];
}

showMessage(messageType: MessageType, message: string): void {
this.messages.showMessage({
type: messageType,
text: message,
options: {
timeout: 10000
}
});
}

async start(): Promise<void> {
await this.initialize();
await this.launchOrAttach();
Expand Down Expand Up @@ -343,13 +353,7 @@ export class DebugSession implements CompositeTreeElement {
try {
await this.sendRequest((this.configuration.request as keyof DebugRequestTypes), this.configuration);
} catch (reason) {
this.messages.showMessage({
type: MessageType.Error,
text: reason.message || 'Debug session initialization failed. See console for details.',
options: {
timeout: 10000
}
});
this.showMessage(MessageType.Error, reason.message || 'Debug session initialization failed. See console for details.');
throw reason;
}
}
Expand Down
6 changes: 6 additions & 0 deletions packages/debug/src/browser/editor/debug-editor-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { DebugEditorModel, DebugEditorModelFactory } from './debug-editor-model'
import { BreakpointManager, SourceBreakpointsChangeEvent } from '../breakpoint/breakpoint-manager';
import { DebugSourceBreakpoint } from '../model/debug-source-breakpoint';
import { DebugBreakpointWidget } from './debug-breakpoint-widget';
import URI from '@theia/core/lib/common/uri';

@injectable()
export class DebugEditorService {
Expand Down Expand Up @@ -71,6 +72,11 @@ export class DebugEditorService {
return uri && this.models.get(uri.toString());
}

get currentUri(): URI | undefined {
const { currentEditor } = this.editors;
return currentEditor && currentEditor.getResourceUri();
}

getLogpoint(position: monaco.Position): DebugSourceBreakpoint | undefined {
const logpoint = this.anyBreakpoint(position);
return logpoint && logpoint.logMessage ? logpoint : undefined;
Expand Down
32 changes: 28 additions & 4 deletions packages/debug/src/browser/model/debug-thread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
// *****************************************************************************

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

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

get supportsGoto(): boolean {
return !!this.session.capabilities.supportsGotoTargetsRequest;
}

async jumpToCursor(uri: URI, position: monaco.Position): Promise<DebugProtocol.GotoResponse | undefined> {
const source = await this.session?.toDebugSource(uri);

if (!source) {
return undefined;
}

const response: DebugProtocol.GotoTargetsResponse = await this.session.sendRequest('gotoTargets', { source, line: position.lineNumber, column: position.column });

if (response && response.body.targets.length === 0) {
this.session.showMessage(MessageType.Warning, 'No executable code is associated at the current cursor position.');
return;
}

const targetId = response.body.targets[0].id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: It would be great if you could implement a "choose the location" quick pick like it's done in vscode in case there are multiple targets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @msujew , many thanks for your review, I could not find a debugger which supported multiple targets, as I could not test this feature, I decided not to implement the QuickInputService.pick here.

return this.session.sendRequest('goto', this.toArgs({ targetId }));
}

async getExceptionInfo(): Promise<DebugExceptionInfo | undefined> {
if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') {
if (this.session.capabilities.supportsExceptionInfoRequest) {
Expand Down Expand Up @@ -261,8 +285,8 @@ export class DebugThread extends DebugThreadData implements TreeElement {
const localizedReason = this.getLocalizedReason(reason);

return reason
? nls.localizeByDefault('Paused on {0}', localizedReason)
: nls.localizeByDefault('Paused');
? nls.localizeByDefault('Paused on {0}', localizedReason)
: nls.localizeByDefault('Paused');
}

protected getLocalizedReason(reason: string | undefined): string {
Expand All @@ -281,7 +305,7 @@ export class DebugThread extends DebugThreadData implements TreeElement {
return nls.localize('theia/debug/goto', 'goto');
case 'function breakpoint':
return nls.localize('theia/debug/functionBreakpoint', 'function breakpoint');
case 'data breakpoint':
case 'data breakpoint':
return nls.localize('theia/debug/dataBreakpoint', 'data breakpoint');
case 'instruction breakpoint':
return nls.localize('theia/debug/instructionBreakpoint', 'instruction breakpoint');
Expand Down
Loading