Skip to content

Commit 5d52762

Browse files
danarad05EstherPerelman
authored andcommitted
4016-debug-extension: support debugging of extensions using Debug panel
1. Supports start, restart, stop of pwa-extensionHost launch type on debug toolbar. 2. Utilizes Hosted Plugin functionality. Signed-off-by: Dan Arad <[email protected]>
1 parent ec9084c commit 5d52762

18 files changed

+241
-83
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
- [core] updated `nsfw` dependency to `^2.1.2` [#9267](https://github.com/eclipse-theia/theia/pull/9267)
122122
- [debug] fixed hover issues for the `currentFrame` editor [#9256](https://github.com/eclipse-theia/theia/pull/9256)
123123
- [debug] improved error messages [#9386](https://github.com/eclipse-theia/theia/pull/9386)
124+
- [debug] added support for managing debug sessions for extensions from debug panel (previously only possible using `Hosted Plugin` commands) [#8706](https://github.com/eclipse-theia/theia/pull/8706)
124125
- [documentation] added roadmap information to the readme [#9308](https://github.com/eclipse-theia/theia/pull/9308)
125126
- [documentation] updated pre-publishing steps [#9257](https://github.com/eclipse-theia/theia/pull/9257)
126127
- [editor-preview] updated logic to activate editor-preview editors only if already active [#9346](https://github.com/eclipse-theia/theia/pull/9346)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/********************************************************************************
2+
* Copyright (c) 2021 SAP SE or an SAP affiliate company and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
17+
import { DebugProtocol } from 'vscode-debugprotocol';
18+
import { DebugSessionConnection } from './debug-session-connection';
19+
20+
export const DebugContribution = Symbol('DebugContribution');
21+
22+
export interface DebugContribution {
23+
register(configType: string, connection: DebugSessionConnection): void;
24+
}
25+
26+
export interface DebugPluginConfiguration {
27+
debugMode?: string;
28+
pluginLocation?: string;
29+
debugPort?: string;
30+
}
31+
32+
// copied from https://github.com/microsoft/vscode-node-debug2/blob/bcd333ef87642b817ac96d28fde7ab96fee3f6a9/src/nodeDebugInterfaces.d.ts
33+
export interface LaunchVSCodeRequest extends DebugProtocol.Request {
34+
arguments: LaunchVSCodeArguments;
35+
}
36+
37+
export interface LaunchVSCodeArguments {
38+
args: LaunchVSCodeArgument[];
39+
env?: { [key: string]: string | null; };
40+
}
41+
42+
export interface LaunchVSCodeArgument {
43+
prefix?: string;
44+
path?: string;
45+
}
46+
47+
export interface LaunchVSCodeResult {
48+
rendererDebugPort?: number;
49+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,11 +611,11 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
611611
execute: (config?: DebugSessionOptions) => this.start(true, config)
612612
});
613613
registry.registerCommand(DebugCommands.STOP, {
614-
execute: () => this.manager.currentSession && this.manager.currentSession.terminate(),
614+
execute: () => this.manager.terminateSessions(),
615615
isEnabled: () => this.manager.state !== DebugState.Inactive
616616
});
617617
registry.registerCommand(DebugCommands.RESTART, {
618-
execute: () => this.manager.restart(),
618+
execute: () => this.manager.restartSessions(),
619619
isEnabled: () => this.manager.state !== DebugState.Inactive
620620
});
621621

packages/debug/src/browser/debug-frontend-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-stor
6060
import { TabBarDecorator } from '@theia/core/lib/browser/shell/tab-bar-decorator';
6161
import { DebugTabBarDecorator } from './debug-tab-bar-decorator';
6262
import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution';
63+
import { DebugContribution } from './debug-contribution';
6364

6465
export default new ContainerModule((bind: interfaces.Bind) => {
66+
bindContributionProvider(bind, DebugContribution);
67+
6568
bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) =>
6669
container.get(ContextKeyService).createKey('callStackItemType', undefined)
6770
).inSingletonScope();

packages/debug/src/browser/debug-session-contribution.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { IWebSocket } from '@theia/core/shared/vscode-ws-jsonrpc';
3030
import { DebugAdapterPath } from '../common/debug-service';
3131
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
3232
import { FileService } from '@theia/filesystem/lib/browser/file-service';
33+
import { DebugContribution } from './debug-contribution';
3334

3435
/**
3536
* DebugSessionContribution symbol for DI.
@@ -50,7 +51,6 @@ export interface DebugSessionContribution {
5051
*/
5152
debugSessionFactory(): DebugSessionFactory;
5253
}
53-
5454
/**
5555
* DebugSessionContributionRegistry symbol for DI.
5656
*/
@@ -95,7 +95,6 @@ export interface DebugSessionFactory {
9595

9696
@injectable()
9797
export class DefaultDebugSessionFactory implements DebugSessionFactory {
98-
9998
@inject(WebSocketConnectionProvider)
10099
protected readonly connectionProvider: WebSocketConnectionProvider;
101100
@inject(TerminalService)
@@ -114,6 +113,8 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory {
114113
protected readonly debugPreferences: DebugPreferences;
115114
@inject(FileService)
116115
protected readonly fileService: FileService;
116+
@inject(ContributionProvider) @named(DebugContribution)
117+
protected readonly debugContributionProvider: ContributionProvider<DebugContribution>;
117118

118119
get(sessionId: string, options: DebugSessionOptions): DebugSession {
119120
const connection = new DebugSessionConnection(
@@ -133,7 +134,8 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory {
133134
this.breakpoints,
134135
this.labelProvider,
135136
this.messages,
136-
this.fileService);
137+
this.fileService,
138+
this.debugContributionProvider);
137139
}
138140

139141
protected getTraceOutputChannel(): OutputChannel | undefined {

packages/debug/src/browser/debug-session-manager.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,16 @@ export class DebugSessionManager {
306306
return this.start(options);
307307
}
308308

309+
async terminateSessions(): Promise<void> {
310+
this.updateCurrentSession(undefined);
311+
this.currentSession?.terminate();
312+
}
313+
314+
async restartSessions(): Promise<void> {
315+
this.updateCurrentSession(undefined);
316+
this.currentSession?.restart();
317+
}
318+
309319
protected remove(sessionId: string): void {
310320
this._sessions.delete(sessionId);
311321
const { currentSession } = this;

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

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import * as React from '@theia/core/shared/react';
2020
import { LabelProvider } from '@theia/core/lib/browser';
2121
import { DebugProtocol } from 'vscode-debugprotocol';
22-
import { Emitter, Event, DisposableCollection, Disposable, MessageClient, MessageType, Mutable } from '@theia/core/lib/common';
22+
import { Emitter, Event, DisposableCollection, Disposable, MessageClient, MessageType, Mutable, ContributionProvider } from '@theia/core/lib/common';
2323
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
2424
import { EditorManager } from '@theia/editor/lib/browser';
2525
import { CompositeTreeElement } from '@theia/core/lib/browser/source-tree';
@@ -39,6 +39,7 @@ import { SourceBreakpoint, ExceptionBreakpoint } from './breakpoint/breakpoint-m
3939
import { TerminalWidgetOptions, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
4040
import { DebugFunctionBreakpoint } from './model/debug-function-breakpoint';
4141
import { FileService } from '@theia/filesystem/lib/browser/file-service';
42+
import { DebugContribution } from './debug-contribution';
4243

4344
export enum DebugState {
4445
Inactive,
@@ -49,7 +50,6 @@ export enum DebugState {
4950

5051
// FIXME: make injectable to allow easily inject services
5152
export class DebugSession implements CompositeTreeElement {
52-
5353
protected readonly onDidChangeEmitter = new Emitter<void>();
5454
readonly onDidChange: Event<void> = this.onDidChangeEmitter.event;
5555
protected fireDidChange(): void {
@@ -73,8 +73,12 @@ export class DebugSession implements CompositeTreeElement {
7373
protected readonly breakpoints: BreakpointManager,
7474
protected readonly labelProvider: LabelProvider,
7575
protected readonly messages: MessageClient,
76-
protected readonly fileService: FileService) {
76+
protected readonly fileService: FileService,
77+
protected readonly debugContributionProvider: ContributionProvider<DebugContribution>
78+
) {
7779
this.connection.onRequest('runInTerminal', (request: DebugProtocol.RunInTerminalRequest) => this.runInTerminal(request));
80+
this.registerDebugContributions(options.configuration.type, this.connection);
81+
7882
this.toDispose.pushAll([
7983
this.onDidChangeEmitter,
8084
this.onDidChangeBreakpointsEmitter,
@@ -115,9 +119,11 @@ export class DebugSession implements CompositeTreeElement {
115119
this.sources.set(uri, source);
116120
return source;
117121
}
122+
118123
getSourceForUri(uri: URI): DebugSource | undefined {
119124
return this.sources.get(uri.toString());
120125
}
126+
121127
async toSource(uri: URI): Promise<DebugSource> {
122128
const source = this.getSourceForUri(uri);
123129
if (source) {
@@ -149,19 +155,23 @@ export class DebugSession implements CompositeTreeElement {
149155
get threads(): IterableIterator<DebugThread> {
150156
return this._threads.values();
151157
}
158+
152159
get threadCount(): number {
153160
return this._threads.size;
154161
}
162+
155163
*getThreads(filter: (thread: DebugThread) => boolean): IterableIterator<DebugThread> {
156164
for (const thread of this.threads) {
157165
if (filter(thread)) {
158166
yield thread;
159167
}
160168
}
161169
}
170+
162171
get runningThreads(): IterableIterator<DebugThread> {
163172
return this.getThreads(thread => !thread.stopped);
164173
}
174+
165175
get stoppedThreads(): IterableIterator<DebugThread> {
166176
return this.getThreads(thread => thread.stopped);
167177
}
@@ -238,6 +248,7 @@ export class DebugSession implements CompositeTreeElement {
238248
await this.initialize();
239249
await this.launchOrAttach();
240250
}
251+
241252
protected async initialize(): Promise<void> {
242253
const response = await this.connection.sendRequest('initialize', {
243254
clientID: 'Theia',
@@ -251,15 +262,12 @@ export class DebugSession implements CompositeTreeElement {
251262
supportsVariablePaging: false,
252263
supportsRunInTerminalRequest: true
253264
});
254-
this.updateCapabilities(response.body || {});
265+
this.updateCapabilities(response?.body || {});
255266
}
267+
256268
protected async launchOrAttach(): Promise<void> {
257269
try {
258-
if (this.configuration.request === 'attach') {
259-
await this.sendRequest('attach', this.configuration);
260-
} else {
261-
await this.sendRequest('launch', this.configuration);
262-
}
270+
await this.sendRequest((this.configuration.request as keyof DebugRequestTypes), this.configuration);
263271
} catch (reason) {
264272
this.fireExited(reason);
265273
await this.messages.showMessage({
@@ -272,6 +280,7 @@ export class DebugSession implements CompositeTreeElement {
272280
throw reason;
273281
}
274282
}
283+
275284
protected initialized = false;
276285
protected async configure(): Promise<void> {
277286
if (this.capabilities.exceptionBreakpointFilters) {
@@ -302,22 +311,27 @@ export class DebugSession implements CompositeTreeElement {
302311
await this.disconnect(restart);
303312
}
304313
}
314+
305315
protected async disconnect(restart?: boolean): Promise<void> {
306-
try {
307-
await this.sendRequest('disconnect', { restart });
308-
} catch (reason) {
309-
this.fireExited(reason);
310-
return;
311-
}
312-
const timeout = 500;
313-
if (!await this.exited(timeout)) {
314-
this.fireExited(new Error(`timeout after ${timeout} ms`));
315-
}
316+
const TIMEOUT_MS = 1000;
317+
Promise.race([
318+
this.sendRequest('disconnect', { restart }),
319+
new Promise(reject => setTimeout(reject, TIMEOUT_MS, new Error('TIMEOUT_ERR')))
320+
]).then(res => {
321+
if (res instanceof Error) {
322+
this.fireExited(res);
323+
}
324+
}).catch(this.fireExited);
316325
}
317326

318327
protected fireExited(reason?: Error): void {
319-
this.connection['fire']('exited', { reason });
328+
try {
329+
this.connection['fire']('exited', { reason });
330+
} catch (e) {
331+
console.error(e);
332+
}
320333
}
334+
321335
protected exited(timeout: number): Promise<boolean> {
322336
return new Promise<boolean>(resolve => {
323337
const listener = this.on('exited', () => {
@@ -398,6 +412,7 @@ export class DebugSession implements CompositeTreeElement {
398412
}
399413
this.updateCurrentThread();
400414
}
415+
401416
protected clearThread(threadId: number): void {
402417
const thread = this._threads.get(threadId);
403418
if (thread) {
@@ -408,6 +423,7 @@ export class DebugSession implements CompositeTreeElement {
408423

409424
protected readonly scheduleUpdateThreads = debounce(() => this.updateThreads(undefined), 100);
410425
protected pendingThreads = Promise.resolve();
426+
411427
updateThreads(stoppedDetails: StoppedDetails | undefined): Promise<void> {
412428
return this.pendingThreads = this.pendingThreads.then(async () => {
413429
try {
@@ -420,6 +436,7 @@ export class DebugSession implements CompositeTreeElement {
420436
}
421437
});
422438
}
439+
423440
protected doUpdateThreads(threads: DebugProtocol.Thread[], stoppedDetails?: StoppedDetails): void {
424441
const existing = this._threads;
425442
this._threads = new Map();
@@ -515,7 +532,9 @@ export class DebugSession implements CompositeTreeElement {
515532
this.fireDidChangeBreakpoints(new URI(uri));
516533
}
517534
}
535+
518536
protected updatingBreakpoints = false;
537+
519538
protected updateBreakpoint(body: DebugProtocol.BreakpointEvent['body']): void {
520539
this.updatingBreakpoints = true;
521540
try {
@@ -687,6 +706,7 @@ export class DebugSession implements CompositeTreeElement {
687706
const distinct = this.dedupSourceBreakpoints(breakpoints);
688707
this.setBreakpoints(uri, distinct);
689708
}
709+
690710
protected dedupSourceBreakpoints(all: DebugSourceBreakpoint[]): DebugSourceBreakpoint[] {
691711
const positions = new Map<string, DebugSourceBreakpoint>();
692712
for (const breakpoint of all) {
@@ -702,6 +722,7 @@ export class DebugSession implements CompositeTreeElement {
702722
}
703723
return [...positions.values()];
704724
}
725+
705726
protected *getAffectedUris(uri?: URI): IterableIterator<URI> {
706727
if (uri) {
707728
yield uri;
@@ -764,4 +785,10 @@ export class DebugSession implements CompositeTreeElement {
764785
this.clearThread(threadId);
765786
}
766787
};
788+
789+
protected registerDebugContributions(configType: string, connection: DebugSessionConnection): void {
790+
for (const contrib of this.debugContributionProvider.getContributions()) {
791+
contrib.register(configType, connection);
792+
}
793+
};
767794
}

packages/debug/src/browser/view/debug-toolbar-widget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class DebugToolBar extends ReactWidget {
8282

8383
protected start = () => this.model.start();
8484
protected restart = () => this.model.restart();
85-
protected stop = () => this.model.currentSession && this.model.currentSession.terminate();
85+
protected stop = () => this.model.terminate();
8686
protected continue = () => this.model.currentThread && this.model.currentThread.continue();
8787
protected pause = () => this.model.currentThread && this.model.currentThread.pause();
8888
protected stepOver = () => this.model.currentThread && this.model.currentThread.stepOver();

packages/debug/src/browser/view/debug-view-model.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ export class DebugViewModel implements Disposable {
189189
this.fireDidChange();
190190
}
191191

192+
async terminate(): Promise<void> {
193+
this.manager.terminateSessions();
194+
}
195+
192196
get watchExpressions(): IterableIterator<DebugWatchExpression> {
193197
return this._watchExpressions.values();
194198
}

packages/plugin-dev/src/browser/hosted-plugin-controller.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export class HostedPluginController implements FrontendApplicationContribution {
229229
try {
230230
await this.hostedPluginServer.stopWatchCompilation(event.pluginLocation.toString());
231231
} catch (error) {
232-
this.messageService.error(this.getErrorMessage(error.message));
232+
this.messageService.error(this.getErrorMessage(error));
233233
}
234234
}
235235
}
@@ -246,8 +246,9 @@ export class HostedPluginController implements FrontendApplicationContribution {
246246
}
247247
}
248248

249-
private getErrorMessage(error: Error): string {
250-
return error.message.substring(error.message.indexOf(':') + 1);
249+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
250+
private getErrorMessage(error: any): string {
251+
return error?.message?.substring(error.message.indexOf(':') + 1) || '';
251252
}
252253

253254
/**

0 commit comments

Comments
 (0)