diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76a3fd5b2864b..9fbace7b4c1d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
- [core] introduce `FRONTEND_CONNECTION_TIMEOUT` environment variable to override application connexion settings. [#13936](https://github.com/eclipse-theia/theia/pull/13936) - Contributed on behalf of STMicroelectronics
- [core] tab selected should be adjacent when closing last one [#13912](https://github.com/eclipse-theia/theia/pull/13912) - contributed on behalf of STMicroelectronics
- [core] upgraded ws to 8.18.0 [#13903](https://github.com/eclipse-theia/theia/pull/13903) - contributed on behalf of STMicroelectronics
+- [debug] implement activeStackItem and related change event in debug namespace [#13900](https://github.com/eclipse-theia/theia/pull/13900) - contributed on behalf of STMicroelectronics
[Breaking Changes:](#breaking_changes_not_yet_released)
diff --git a/packages/debug/src/browser/debug-session-manager.ts b/packages/debug/src/browser/debug-session-manager.ts
index 788db2c5c9009..b462ecba88bb7 100644
--- a/packages/debug/src/browser/debug-session-manager.ts
+++ b/packages/debug/src/browser/debug-session-manager.ts
@@ -55,11 +55,6 @@ export interface DidChangeBreakpointsEvent {
uri: URI
}
-export interface DidFocusStackFrameEvent {
- session: DebugSession;
- frame: DebugStackFrame | undefined;
-}
-
export interface DebugSessionCustomEvent {
readonly body?: any // eslint-disable-line @typescript-eslint/no-explicit-any
readonly event: string
@@ -94,9 +89,12 @@ export class DebugSessionManager {
protected readonly onDidReceiveDebugSessionCustomEventEmitter = new Emitter();
readonly onDidReceiveDebugSessionCustomEvent: Event = this.onDidReceiveDebugSessionCustomEventEmitter.event;
- protected readonly onDidFocusStackFrameEmitter = new Emitter();
+ protected readonly onDidFocusStackFrameEmitter = new Emitter();
readonly onDidFocusStackFrame = this.onDidFocusStackFrameEmitter.event;
+ protected readonly onDidFocusThreadEmitter = new Emitter();
+ readonly onDidFocusThread = this.onDidFocusThreadEmitter.event;
+
protected readonly onDidChangeBreakpointsEmitter = new Emitter();
readonly onDidChangeBreakpoints = this.onDidChangeBreakpointsEmitter.event;
protected fireDidChangeBreakpoints(event: DidChangeBreakpointsEvent): void {
@@ -518,7 +516,10 @@ export class DebugSessionManager {
}
this.fireDidChange(current);
}));
- this.disposeOnCurrentSessionChanged.push(current.onDidFocusStackFrame(frame => this.onDidFocusStackFrameEmitter.fire({ session: current, frame })));
+ this.disposeOnCurrentSessionChanged.push(current.onDidFocusStackFrame(frame => this.onDidFocusStackFrameEmitter.fire(frame)));
+ this.disposeOnCurrentSessionChanged.push(current.onDidFocusThread(thread => this.onDidFocusThreadEmitter.fire(thread)));
+ const { currentThread } = current;
+ this.onDidFocusThreadEmitter.fire(currentThread);
}
this.updateBreakpoints(previous, current);
this.open();
diff --git a/packages/debug/src/browser/debug-session.tsx b/packages/debug/src/browser/debug-session.tsx
index 66b86ec487b7e..3a80f0efa8905 100644
--- a/packages/debug/src/browser/debug-session.tsx
+++ b/packages/debug/src/browser/debug-session.tsx
@@ -78,6 +78,11 @@ export class DebugSession implements CompositeTreeElement {
return this.onDidFocusStackFrameEmitter.event;
}
+ protected readonly onDidFocusThreadEmitter = new Emitter();
+ get onDidFocusThread(): Event {
+ return this.onDidFocusThreadEmitter.event;
+ }
+
protected readonly onDidChangeBreakpointsEmitter = new Emitter();
readonly onDidChangeBreakpoints: Event = this.onDidChangeBreakpointsEmitter.event;
protected fireDidChangeBreakpoints(uri: URI): void {
@@ -254,8 +259,12 @@ export class DebugSession implements CompositeTreeElement {
return this._currentThread;
}
set currentThread(thread: DebugThread | undefined) {
+ if (this._currentThread?.id === thread?.id) {
+ return;
+ }
this.toDisposeOnCurrentThread.dispose();
this._currentThread = thread;
+ this.onDidFocusThreadEmitter.fire(thread);
this.fireDidChange();
if (thread) {
this.toDisposeOnCurrentThread.push(thread.onDidChanged(() => this.fireDidChange()));
diff --git a/packages/debug/src/browser/model/debug-stack-frame.tsx b/packages/debug/src/browser/model/debug-stack-frame.tsx
index d03831d67dae4..65ad04af48b27 100644
--- a/packages/debug/src/browser/model/debug-stack-frame.tsx
+++ b/packages/debug/src/browser/model/debug-stack-frame.tsx
@@ -49,6 +49,13 @@ export class DebugStackFrame extends DebugStackFrameData implements TreeElement
return this.session.id + ':' + this.thread.id + ':' + this.raw.id;
}
+ /**
+ * Returns the frame identifier from the debug protocol.
+ */
+ get frameId(): number {
+ return this.raw.id;
+ }
+
protected _source: DebugSource | undefined;
get source(): DebugSource | undefined {
return this._source;
diff --git a/packages/debug/src/browser/model/debug-thread.tsx b/packages/debug/src/browser/model/debug-thread.tsx
index 1aae18af9d98a..306ff5d1082f6 100644
--- a/packages/debug/src/browser/model/debug-thread.tsx
+++ b/packages/debug/src/browser/model/debug-thread.tsx
@@ -56,11 +56,18 @@ export class DebugThread extends DebugThreadData implements TreeElement {
return this.session.id + ':' + this.raw.id;
}
+ get threadId(): number {
+ return this.raw.id;
+ }
+
protected _currentFrame: DebugStackFrame | undefined;
get currentFrame(): DebugStackFrame | undefined {
return this._currentFrame;
}
set currentFrame(frame: DebugStackFrame | undefined) {
+ if (this._currentFrame?.id === frame?.id) {
+ return;
+ }
this._currentFrame = frame;
this.onDidChangedEmitter.fire(undefined);
this.onDidFocusStackFrameEmitter.fire(frame);
diff --git a/packages/debug/src/browser/view/debug-threads-widget.ts b/packages/debug/src/browser/view/debug-threads-widget.ts
index 7933a84352ad1..aa70d04ec97f9 100644
--- a/packages/debug/src/browser/view/debug-threads-widget.ts
+++ b/packages/debug/src/browser/view/debug-threads-widget.ts
@@ -99,6 +99,7 @@ export class DebugThreadsWidget extends SourceTreeWidget {
this.viewModel.currentSession = node.element;
this.debugCallStackItemTypeKey.set('session');
} else if (node.element instanceof DebugThread) {
+ this.viewModel.currentSession = node.element.session;
node.element.session.currentThread = node.element;
this.debugCallStackItemTypeKey.set('thread');
}
diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
index 4ae9532f404e9..7a4ecdae2f2b4 100644
--- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
+++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
@@ -895,3 +895,13 @@ export interface InlineCompletionsProvider;
+ $onDidChangeActiveFrame(frame: DebugStackFrameDTO | undefined): void;
+ $onDidChangeActiveThread(thread: DebugThreadDTO | undefined): void;
$createDebugSession(debugConfiguration: DebugConfiguration, workspaceFolder: string | undefined): Promise;
$terminateDebugSession(sessionId: string): Promise;
$getTerminalCreationOptions(debugType: string): Promise;
diff --git a/packages/plugin-ext/src/main/browser/debug/debug-main.ts b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
index 8e1020a8a7fb1..703912d10d6fb 100644
--- a/packages/plugin-ext/src/main/browser/debug/debug-main.ts
+++ b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
@@ -25,7 +25,7 @@ import {
MAIN_RPC_CONTEXT
} from '../../../common/plugin-api-rpc';
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
-import { Breakpoint, WorkspaceFolder } from '../../../common/plugin-api-rpc-model';
+import { Breakpoint, DebugStackFrameDTO, DebugThreadDTO, WorkspaceFolder } from '../../../common/plugin-api-rpc-model';
import { LabelProvider } from '@theia/core/lib/browser';
import { EditorManager } from '@theia/editor/lib/browser';
import { BreakpointManager, BreakpointsChangeEvent } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
@@ -56,6 +56,8 @@ import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
import { ConnectionImpl } from '../../../common/connection';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { DebugSessionOptions as TheiaDebugSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
+import { DebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
+import { DebugThread } from '@theia/debug/lib/browser/model/debug-thread';
export class DebugMainImpl implements DebugMain, Disposable {
private readonly debugExt: DebugExt;
@@ -117,7 +119,9 @@ export class DebugMainImpl implements DebugMain, Disposable {
this.sessionManager.onDidStartDebugSession(debugSession => this.debugExt.$sessionDidStart(debugSession.id)),
this.sessionManager.onDidDestroyDebugSession(debugSession => this.debugExt.$sessionDidDestroy(debugSession.id)),
this.sessionManager.onDidChangeActiveDebugSession(event => this.debugExt.$sessionDidChange(event.current && event.current.id)),
- this.sessionManager.onDidReceiveDebugSessionCustomEvent(event => this.debugExt.$onSessionCustomEvent(event.session.id, event.event, event.body))
+ this.sessionManager.onDidReceiveDebugSessionCustomEvent(event => this.debugExt.$onSessionCustomEvent(event.session.id, event.event, event.body)),
+ this.sessionManager.onDidFocusStackFrame(stackFrame => this.debugExt.$onDidChangeActiveFrame(this.toDebugStackFrameDTO(stackFrame))),
+ this.sessionManager.onDidFocusThread(debugThread => this.debugExt.$onDidChangeActiveThread(this.toDebugThreadDTO(debugThread))),
]);
}
@@ -340,6 +344,21 @@ export class DebugMainImpl implements DebugMain, Disposable {
}
}
+ private toDebugStackFrameDTO(stackFrame: DebugStackFrame | undefined): DebugStackFrameDTO | undefined {
+ return stackFrame ? {
+ sessionId: stackFrame.session.id,
+ frameId: stackFrame.frameId,
+ threadId: stackFrame.thread.threadId
+ } : undefined;
+ }
+
+ private toDebugThreadDTO(debugThread: DebugThread | undefined): DebugThreadDTO | undefined {
+ return debugThread ? {
+ sessionId: debugThread.session.id,
+ threadId: debugThread.threadId
+ } : undefined;
+ }
+
private toTheiaPluginApiBreakpoints(breakpoints: (SourceBreakpoint | FunctionBreakpoint)[]): Breakpoint[] {
return breakpoints.map(b => this.toTheiaPluginApiBreakpoint(b));
}
diff --git a/packages/plugin-ext/src/plugin/debug/debug-ext.ts b/packages/plugin-ext/src/plugin/debug/debug-ext.ts
index af9486cab4dca..48c13a6ec9bee 100644
--- a/packages/plugin-ext/src/plugin/debug/debug-ext.ts
+++ b/packages/plugin-ext/src/plugin/debug/debug-ext.ts
@@ -18,14 +18,14 @@ import { Emitter } from '@theia/core/lib/common/event';
import { Path } from '@theia/core/lib/common/path';
import * as theia from '@theia/plugin';
import { URI } from '@theia/core/shared/vscode-uri';
-import { Breakpoint } from '../../common/plugin-api-rpc-model';
+import { Breakpoint, DebugStackFrameDTO, DebugThreadDTO } from '../../common/plugin-api-rpc-model';
import { DebugConfigurationProviderTriggerKind, DebugExt, DebugMain, PLUGIN_RPC_CONTEXT as Ext, TerminalOptionsExt } from '../../common/plugin-api-rpc';
import { PluginPackageDebuggersContribution } from '../../common/plugin-protocol';
import { RPCProtocol } from '../../common/rpc-protocol';
import { CommandRegistryImpl } from '../command-registry';
import { ConnectionImpl } from '../../common/connection';
import { DEBUG_SCHEME, SCHEME_PATTERN } from '@theia/debug/lib/common/debug-uri-utils';
-import { Disposable, Breakpoint as BreakpointExt, SourceBreakpoint, FunctionBreakpoint, Location, Range, URI as URIImpl } from '../types-impl';
+import { Disposable, Breakpoint as BreakpointExt, SourceBreakpoint, FunctionBreakpoint, Location, Range, URI as URIImpl, DebugStackFrame, DebugThread } from '../types-impl';
import { PluginDebugAdapterSession } from './plugin-debug-adapter-session';
import { PluginDebugAdapterTracker } from './plugin-debug-adapter-tracker';
import { generateUuid } from '@theia/core/lib/common/uuid';
@@ -79,6 +79,9 @@ export class DebugExtImpl implements DebugExt {
activeDebugSession: theia.DebugSession | undefined;
activeDebugConsole: theia.DebugConsole;
+ _activeStackItem: theia.DebugStackFrame | theia.DebugThread | undefined;
+ private readonly onDidChangeActiveStackItemEmitter = new Emitter();
+
private readonly _breakpoints = new Map();
private frontendAdapterCreator = new PluginDebugAdapterCreator();
@@ -149,6 +152,10 @@ export class DebugExtImpl implements DebugExt {
return this.onDidStartDebugSessionEmitter.event;
}
+ get onDidChangeActiveStackItem(): theia.Event {
+ return this.onDidChangeActiveStackItemEmitter.event;
+ }
+
get onDidChangeBreakpoints(): theia.Event {
return this.onDidChangeBreakpointsEmitter.event;
}
@@ -262,6 +269,40 @@ export class DebugExtImpl implements DebugExt {
});
}
+ set activeStackItem(stackItem: theia.DebugStackFrame | theia.DebugThread | undefined) {
+ if (this._activeStackItem === stackItem) {
+ return;
+ }
+ this._activeStackItem = stackItem;
+ this.onDidChangeActiveStackItemEmitter.fire(this.activeStackItem);
+ }
+
+ get activeStackItem(): theia.DebugStackFrame | theia.DebugThread | undefined {
+ return this._activeStackItem;
+ }
+
+ async $onDidChangeActiveThread(debugThread: DebugThreadDTO | undefined): Promise {
+ if (!debugThread) {
+ this.activeStackItem = undefined;
+ return;
+ }
+ const session = this.sessions.get(debugThread.sessionId);
+ if (session) {
+ this.activeStackItem = new DebugThread(session, debugThread.threadId);
+ }
+ }
+
+ async $onDidChangeActiveFrame(debugFrame: DebugStackFrameDTO | undefined): Promise {
+ if (!debugFrame) {
+ this.activeStackItem = undefined;
+ return;
+ }
+ const session = this.sessions.get(debugFrame!.sessionId);
+ if (session) {
+ this.activeStackItem = new DebugStackFrame(session, debugFrame.threadId, debugFrame.frameId);
+ }
+ }
+
async $onSessionCustomEvent(sessionId: string, event: string, body?: any): Promise {
const session = this.sessions.get(sessionId);
if (session) {
diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts
index 4367992efd925..1a66df549a33c 100644
--- a/packages/plugin-ext/src/plugin/plugin-context.ts
+++ b/packages/plugin-ext/src/plugin/plugin-context.ts
@@ -1101,13 +1101,11 @@ export function createAPIFactory(
get onDidChangeBreakpoints(): theia.Event {
return debugExt.onDidChangeBreakpoints;
},
- /** @stubbed */
get activeStackItem(): DebugThread | DebugStackFrame | undefined {
- return undefined;
+ return debugExt.activeStackItem;
},
- /** @stubbed */
get onDidChangeActiveStackItem(): theia.Event {
- return Event.None;
+ return debugExt.onDidChangeActiveStackItem;
},
registerDebugAdapterDescriptorFactory(debugType: string, factory: theia.DebugAdapterDescriptorFactory): Disposable {
return debugExt.registerDebugAdapterDescriptorFactory(debugType, factory);
diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts
index 3b9a28057432c..e1b1dd10bb3a7 100644
--- a/packages/plugin-ext/src/plugin/types-impl.ts
+++ b/packages/plugin-ext/src/plugin/types-impl.ts
@@ -3031,12 +3031,12 @@ export class FunctionBreakpoint extends Breakpoint {
}
}
-export class DebugThread {
- private constructor(readonly session: theia.DebugSession, readonly threadId: number) { }
+export class DebugThread implements theia.DebugThread {
+ constructor(readonly session: theia.DebugSession, readonly threadId: number) { }
}
-export class DebugStackFrame {
- private constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { }
+export class DebugStackFrame implements theia.DebugStackFrame {
+ constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { }
}
@es5ClassCompat
diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts
index a712b59b05b16..a1a07acfb420f 100644
--- a/packages/plugin/src/theia.d.ts
+++ b/packages/plugin/src/theia.d.ts
@@ -12548,13 +12548,11 @@ export module '@theia/plugin' {
* thread or stack is focused. A thread can be focused any time there is
* an active debug session, while a stack frame can only be focused when
* a session is paused and the call stack has been retrieved.
- * @stubbed
*/
export const activeStackItem: DebugThread | DebugStackFrame | undefined;
/**
* An event which fires when the {@link debug.activeStackItem} has changed.
- * @stubbed
*/
export const onDidChangeActiveStackItem: Event;