Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/common/extHostDebugService.ts
5221 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type * as vscode from 'vscode';
7
import { coalesce } from '../../../base/common/arrays.js';
8
import { asPromise } from '../../../base/common/async.js';
9
import { CancellationToken } from '../../../base/common/cancellation.js';
10
import { Emitter, Event } from '../../../base/common/event.js';
11
import { Disposable as DisposableCls, toDisposable } from '../../../base/common/lifecycle.js';
12
import { ThemeIcon as ThemeIconUtils } from '../../../base/common/themables.js';
13
import { URI, UriComponents } from '../../../base/common/uri.js';
14
import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
15
import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
16
import { ISignService } from '../../../platform/sign/common/sign.js';
17
import { IWorkspaceFolderData } from '../../../platform/workspace/common/workspace.js';
18
import { AbstractDebugAdapter } from '../../contrib/debug/common/abstractDebugAdapter.js';
19
import { DebugVisualizationType, IAdapterDescriptor, IConfig, IDebugAdapter, IDebugAdapterExecutable, IDebugAdapterImpl, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebuggerContribution, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from '../../contrib/debug/common/debug.js';
20
import { convertToDAPaths, convertToVSCPaths, isDebuggerMainContribution } from '../../contrib/debug/common/debugUtils.js';
21
import { ExtensionDescriptionRegistry } from '../../services/extensions/common/extensionDescriptionRegistry.js';
22
import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
23
import { DebugSessionUUID, ExtHostDebugServiceShape, IBreakpointsDeltaDto, IDebugSessionDto, IFunctionBreakpointDto, ISourceMultiBreakpointDto, IStackFrameFocusDto, IThreadFocusDto, MainContext, MainThreadDebugServiceShape, MainThreadTelemetryShape } from './extHost.protocol.js';
24
import { IExtHostCommands } from './extHostCommands.js';
25
import { IExtHostConfiguration } from './extHostConfiguration.js';
26
import { IExtHostEditorTabs } from './extHostEditorTabs.js';
27
import { IExtHostExtensionService } from './extHostExtensionService.js';
28
import { IExtHostRpcService } from './extHostRpcService.js';
29
import { IExtHostTesting } from './extHostTesting.js';
30
import * as Convert from './extHostTypeConverters.js';
31
import { Breakpoint, DataBreakpoint, DebugAdapterExecutable, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, DebugAdapterServer, DebugConsoleMode, DebugStackFrame, DebugThread, Disposable, FunctionBreakpoint, Location, Position, setBreakpointId, SourceBreakpoint, ThemeIcon } from './extHostTypes.js';
32
import { IExtHostVariableResolverProvider } from './extHostVariableResolverService.js';
33
import { IExtHostWorkspace } from './extHostWorkspace.js';
34
35
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
36
37
export interface IExtHostDebugService extends ExtHostDebugServiceShape {
38
39
readonly _serviceBrand: undefined;
40
41
readonly onDidStartDebugSession: Event<vscode.DebugSession>;
42
readonly onDidTerminateDebugSession: Event<vscode.DebugSession>;
43
readonly onDidChangeActiveDebugSession: Event<vscode.DebugSession | undefined>;
44
activeDebugSession: vscode.DebugSession | undefined;
45
activeDebugConsole: vscode.DebugConsole;
46
readonly onDidReceiveDebugSessionCustomEvent: Event<vscode.DebugSessionCustomEvent>;
47
readonly onDidChangeBreakpoints: Event<vscode.BreakpointsChangeEvent>;
48
breakpoints: vscode.Breakpoint[];
49
readonly onDidChangeActiveStackItem: Event<vscode.DebugThread | vscode.DebugStackFrame | undefined>;
50
activeStackItem: vscode.DebugThread | vscode.DebugStackFrame | undefined;
51
52
addBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise<void>;
53
removeBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise<void>;
54
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean>;
55
stopDebugging(session?: vscode.DebugSession): Promise<void>;
56
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable;
57
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
58
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
59
registerDebugVisualizationProvider<T extends vscode.DebugVisualization>(extension: IExtensionDescription, id: string, provider: vscode.DebugVisualizationProvider<T>): vscode.Disposable;
60
registerDebugVisualizationTree<T extends vscode.DebugTreeItem>(extension: IExtensionDescription, id: string, provider: vscode.DebugVisualizationTree<T>): vscode.Disposable;
61
asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri;
62
}
63
64
export abstract class ExtHostDebugServiceBase extends DisposableCls implements IExtHostDebugService, ExtHostDebugServiceShape {
65
66
declare readonly _serviceBrand: undefined;
67
68
private _configProviderHandleCounter: number;
69
private _configProviders: ConfigProviderTuple[];
70
71
private _adapterFactoryHandleCounter: number;
72
private _adapterFactories: DescriptorFactoryTuple[];
73
74
private _trackerFactoryHandleCounter: number;
75
private _trackerFactories: TrackerFactoryTuple[];
76
77
private _debugServiceProxy: MainThreadDebugServiceShape;
78
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
79
80
private readonly _onDidStartDebugSession: Emitter<vscode.DebugSession>;
81
get onDidStartDebugSession(): Event<vscode.DebugSession> { return this._onDidStartDebugSession.event; }
82
83
private readonly _onDidTerminateDebugSession: Emitter<vscode.DebugSession>;
84
get onDidTerminateDebugSession(): Event<vscode.DebugSession> { return this._onDidTerminateDebugSession.event; }
85
86
private readonly _onDidChangeActiveDebugSession: Emitter<vscode.DebugSession | undefined>;
87
get onDidChangeActiveDebugSession(): Event<vscode.DebugSession | undefined> { return this._onDidChangeActiveDebugSession.event; }
88
89
private _activeDebugSession: ExtHostDebugSession | undefined;
90
get activeDebugSession(): vscode.DebugSession | undefined { return this._activeDebugSession?.api; }
91
92
private readonly _onDidReceiveDebugSessionCustomEvent: Emitter<vscode.DebugSessionCustomEvent>;
93
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
94
95
private _activeDebugConsole: ExtHostDebugConsole;
96
get activeDebugConsole(): vscode.DebugConsole { return this._activeDebugConsole.value; }
97
98
private _breakpoints: Map<string, vscode.Breakpoint>;
99
100
private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
101
102
private _activeStackItem: vscode.DebugThread | vscode.DebugStackFrame | undefined;
103
private readonly _onDidChangeActiveStackItem: Emitter<vscode.DebugThread | vscode.DebugStackFrame | undefined>;
104
105
private _debugAdapters: Map<number, IDebugAdapter>;
106
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
107
108
private _debugVisualizationTreeItemIdsCounter = 0;
109
private readonly _debugVisualizationProviders = new Map<string, vscode.DebugVisualizationProvider>();
110
private readonly _debugVisualizationTrees = new Map<string, vscode.DebugVisualizationTree>();
111
private readonly _debugVisualizationTreeItemIds = new WeakMap<vscode.DebugTreeItem, number>();
112
private readonly _debugVisualizationElements = new Map<number, { provider: string; item: vscode.DebugTreeItem; children?: number[] }>();
113
114
private _signService: ISignService | undefined;
115
116
private readonly _visualizers = new Map<number, { v: vscode.DebugVisualization; provider: vscode.DebugVisualizationProvider; extensionId: string }>();
117
private _visualizerIdCounter = 0;
118
119
private _telemetryProxy: MainThreadTelemetryShape;
120
121
constructor(
122
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
123
@IExtHostWorkspace protected readonly _workspaceService: IExtHostWorkspace,
124
@IExtHostExtensionService private readonly _extensionService: IExtHostExtensionService,
125
@IExtHostConfiguration protected readonly _configurationService: IExtHostConfiguration,
126
@IExtHostEditorTabs protected readonly _editorTabs: IExtHostEditorTabs,
127
@IExtHostVariableResolverProvider private readonly _variableResolver: IExtHostVariableResolverProvider,
128
@IExtHostCommands private readonly _commands: IExtHostCommands,
129
@IExtHostTesting private readonly _testing: IExtHostTesting,
130
) {
131
super();
132
133
this._configProviderHandleCounter = 0;
134
this._configProviders = [];
135
136
this._adapterFactoryHandleCounter = 0;
137
this._adapterFactories = [];
138
139
this._trackerFactoryHandleCounter = 0;
140
this._trackerFactories = [];
141
142
this._debugAdapters = new Map();
143
this._debugAdaptersTrackers = new Map();
144
145
this._onDidStartDebugSession = this._register(new Emitter<vscode.DebugSession>());
146
this._onDidTerminateDebugSession = this._register(new Emitter<vscode.DebugSession>());
147
this._onDidChangeActiveDebugSession = this._register(new Emitter<vscode.DebugSession | undefined>());
148
this._onDidReceiveDebugSessionCustomEvent = this._register(new Emitter<vscode.DebugSessionCustomEvent>());
149
150
this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService);
151
152
this._onDidChangeBreakpoints = this._register(new Emitter<vscode.BreakpointsChangeEvent>());
153
154
this._onDidChangeActiveStackItem = this._register(new Emitter<vscode.DebugThread | vscode.DebugStackFrame | undefined>());
155
156
this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy);
157
158
this._breakpoints = new Map<string, vscode.Breakpoint>();
159
160
this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => {
161
this._register(extensionRegistry.onDidChange(_ => {
162
this.registerAllDebugTypes(extensionRegistry);
163
}));
164
this.registerAllDebugTypes(extensionRegistry);
165
});
166
167
this._telemetryProxy = extHostRpcService.getProxy(MainContext.MainThreadTelemetry);
168
}
169
170
public async $getVisualizerTreeItem(treeId: string, element: IDebugVisualizationContext): Promise<IDebugVisualizationTreeItem | undefined> {
171
const context = this.hydrateVisualizationContext(element);
172
if (!context) {
173
return undefined;
174
}
175
176
const item = await this._debugVisualizationTrees.get(treeId)?.getTreeItem?.(context);
177
return item ? this.convertVisualizerTreeItem(treeId, item) : undefined;
178
}
179
180
public registerDebugVisualizationTree<T extends vscode.DebugTreeItem>(manifest: IExtensionDescription, id: string, provider: vscode.DebugVisualizationTree<T>): vscode.Disposable {
181
const extensionId = ExtensionIdentifier.toKey(manifest.identifier);
182
const key = this.extensionVisKey(extensionId, id);
183
if (this._debugVisualizationProviders.has(key)) {
184
throw new Error(`A debug visualization provider with id '${id}' is already registered`);
185
}
186
187
this._debugVisualizationTrees.set(key, provider);
188
this._debugServiceProxy.$registerDebugVisualizerTree(key, !!provider.editItem);
189
return toDisposable(() => {
190
this._debugServiceProxy.$unregisterDebugVisualizerTree(key);
191
this._debugVisualizationTrees.delete(id);
192
});
193
}
194
195
public async $getVisualizerTreeItemChildren(treeId: string, element: number): Promise<IDebugVisualizationTreeItem[]> {
196
const item = this._debugVisualizationElements.get(element)?.item;
197
if (!item) {
198
return [];
199
}
200
201
const children = await this._debugVisualizationTrees.get(treeId)?.getChildren?.(item);
202
return children?.map(i => this.convertVisualizerTreeItem(treeId, i)) || [];
203
}
204
205
public async $editVisualizerTreeItem(element: number, value: string): Promise<IDebugVisualizationTreeItem | undefined> {
206
const e = this._debugVisualizationElements.get(element);
207
if (!e) { return undefined; }
208
209
const r = await this._debugVisualizationTrees.get(e.provider)?.editItem?.(e.item, value);
210
return this.convertVisualizerTreeItem(e.provider, r || e.item);
211
}
212
213
public $disposeVisualizedTree(element: number): void {
214
const root = this._debugVisualizationElements.get(element);
215
if (!root) {
216
return;
217
}
218
219
const queue = [root.children];
220
for (const children of queue) {
221
if (children) {
222
for (const child of children) {
223
queue.push(this._debugVisualizationElements.get(child)?.children);
224
this._debugVisualizationElements.delete(child);
225
}
226
}
227
}
228
}
229
230
private convertVisualizerTreeItem(treeId: string, item: vscode.DebugTreeItem): IDebugVisualizationTreeItem {
231
let id = this._debugVisualizationTreeItemIds.get(item);
232
if (!id) {
233
id = this._debugVisualizationTreeItemIdsCounter++;
234
this._debugVisualizationTreeItemIds.set(item, id);
235
this._debugVisualizationElements.set(id, { provider: treeId, item });
236
}
237
238
return Convert.DebugTreeItem.from(item, id);
239
}
240
241
public asDebugSourceUri(src: vscode.DebugProtocolSource, session?: vscode.DebugSession): URI {
242
243
// eslint-disable-next-line local/code-no-any-casts
244
const source = <any>src;
245
246
if (typeof source.sourceReference === 'number' && source.sourceReference > 0) {
247
// src can be retrieved via DAP's "source" request
248
249
let debug = `debug:${encodeURIComponent(source.path || '')}`;
250
let sep = '?';
251
252
if (session) {
253
debug += `${sep}session=${encodeURIComponent(session.id)}`;
254
sep = '&';
255
}
256
257
debug += `${sep}ref=${source.sourceReference}`;
258
259
return URI.parse(debug);
260
} else if (source.path) {
261
// src is just a local file path
262
return URI.file(source.path);
263
} else {
264
throw new Error(`cannot create uri from DAP 'source' object; properties 'path' and 'sourceReference' are both missing.`);
265
}
266
}
267
268
private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) {
269
270
const debugTypes: string[] = [];
271
272
for (const ed of extensionRegistry.getAllExtensionDescriptions()) {
273
if (ed.contributes) {
274
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
275
if (debuggers && debuggers.length > 0) {
276
for (const dbg of debuggers) {
277
if (isDebuggerMainContribution(dbg)) {
278
debugTypes.push(dbg.type);
279
}
280
}
281
}
282
}
283
}
284
285
this._debugServiceProxy.$registerDebugTypes(debugTypes);
286
}
287
288
// extension debug API
289
290
291
get activeStackItem(): vscode.DebugThread | vscode.DebugStackFrame | undefined {
292
return this._activeStackItem;
293
}
294
295
get onDidChangeActiveStackItem(): Event<vscode.DebugThread | vscode.DebugStackFrame | undefined> {
296
return this._onDidChangeActiveStackItem.event;
297
}
298
299
get onDidChangeBreakpoints(): Event<vscode.BreakpointsChangeEvent> {
300
return this._onDidChangeBreakpoints.event;
301
}
302
303
get breakpoints(): vscode.Breakpoint[] {
304
const result: vscode.Breakpoint[] = [];
305
this._breakpoints.forEach(bp => result.push(bp));
306
return result;
307
}
308
309
public async $resolveDebugVisualizer(id: number, token: CancellationToken): Promise<MainThreadDebugVisualization> {
310
const visualizer = this._visualizers.get(id);
311
if (!visualizer) {
312
throw new Error(`No debug visualizer found with id '${id}'`);
313
}
314
315
let { v, provider, extensionId } = visualizer;
316
if (!v.visualization) {
317
v = await provider.resolveDebugVisualization?.(v, token) || v;
318
visualizer.v = v;
319
}
320
321
if (!v.visualization) {
322
throw new Error(`No visualization returned from resolveDebugVisualization in '${provider}'`);
323
}
324
325
return this.serializeVisualization(extensionId, v.visualization)!;
326
}
327
328
public async $executeDebugVisualizerCommand(id: number): Promise<void> {
329
const visualizer = this._visualizers.get(id);
330
if (!visualizer) {
331
throw new Error(`No debug visualizer found with id '${id}'`);
332
}
333
334
const command = visualizer.v.visualization;
335
if (command && 'command' in command) {
336
this._commands.executeCommand(command.command, ...(command.arguments || []));
337
}
338
}
339
340
private hydrateVisualizationContext(context: IDebugVisualizationContext): vscode.DebugVisualizationContext | undefined {
341
const session = this._debugSessions.get(context.sessionId);
342
return session && {
343
session: session.api,
344
variable: context.variable,
345
containerId: context.containerId,
346
frameId: context.frameId,
347
threadId: context.threadId,
348
};
349
}
350
351
public async $provideDebugVisualizers(extensionId: string, id: string, context: IDebugVisualizationContext, token: CancellationToken): Promise<IDebugVisualization.Serialized[]> {
352
const contextHydrated = this.hydrateVisualizationContext(context);
353
const key = this.extensionVisKey(extensionId, id);
354
const provider = this._debugVisualizationProviders.get(key);
355
if (!contextHydrated || !provider) {
356
return []; // probably ended in the meantime
357
}
358
359
const visualizations = await provider.provideDebugVisualization(contextHydrated, token);
360
361
if (!visualizations) {
362
return [];
363
}
364
365
return visualizations.map(v => {
366
const id = ++this._visualizerIdCounter;
367
this._visualizers.set(id, { v, provider, extensionId });
368
const icon = v.iconPath ? this.getIconPathOrClass(v.iconPath) : undefined;
369
return {
370
id,
371
name: v.name,
372
iconClass: icon?.iconClass,
373
iconPath: icon?.iconPath,
374
visualization: this.serializeVisualization(extensionId, v.visualization),
375
};
376
});
377
}
378
379
public $disposeDebugVisualizers(ids: number[]): void {
380
for (const id of ids) {
381
this._visualizers.delete(id);
382
}
383
}
384
385
public registerDebugVisualizationProvider<T extends vscode.DebugVisualization>(manifest: IExtensionDescription, id: string, provider: vscode.DebugVisualizationProvider<T>): vscode.Disposable {
386
if (!manifest.contributes?.debugVisualizers?.some(r => r.id === id)) {
387
throw new Error(`Extensions may only call registerDebugVisualizationProvider() for renderers they contribute (got ${id})`);
388
}
389
390
const extensionId = ExtensionIdentifier.toKey(manifest.identifier);
391
const key = this.extensionVisKey(extensionId, id);
392
if (this._debugVisualizationProviders.has(key)) {
393
throw new Error(`A debug visualization provider with id '${id}' is already registered`);
394
}
395
396
this._debugVisualizationProviders.set(key, provider);
397
this._debugServiceProxy.$registerDebugVisualizer(extensionId, id);
398
return toDisposable(() => {
399
this._debugServiceProxy.$unregisterDebugVisualizer(extensionId, id);
400
this._debugVisualizationProviders.delete(id);
401
});
402
}
403
404
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void> {
405
// filter only new breakpoints
406
const breakpoints = breakpoints0.filter(bp => {
407
const id = bp.id;
408
if (!this._breakpoints.has(id)) {
409
this._breakpoints.set(id, bp);
410
return true;
411
}
412
return false;
413
});
414
415
// send notification for added breakpoints
416
this.fireBreakpointChanges(breakpoints, [], []);
417
418
// convert added breakpoints to DTOs
419
const dtos: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto> = [];
420
const map = new Map<string, ISourceMultiBreakpointDto>();
421
for (const bp of breakpoints) {
422
if (bp instanceof SourceBreakpoint) {
423
let dto = map.get(bp.location.uri.toString());
424
if (!dto) {
425
dto = {
426
type: 'sourceMulti',
427
uri: bp.location.uri,
428
lines: []
429
} satisfies ISourceMultiBreakpointDto;
430
map.set(bp.location.uri.toString(), dto);
431
dtos.push(dto);
432
}
433
dto.lines.push({
434
id: bp.id,
435
enabled: bp.enabled,
436
condition: bp.condition,
437
hitCondition: bp.hitCondition,
438
logMessage: bp.logMessage,
439
line: bp.location.range.start.line,
440
character: bp.location.range.start.character,
441
mode: bp.mode,
442
});
443
} else if (bp instanceof FunctionBreakpoint) {
444
dtos.push({
445
type: 'function',
446
id: bp.id,
447
enabled: bp.enabled,
448
hitCondition: bp.hitCondition,
449
logMessage: bp.logMessage,
450
condition: bp.condition,
451
functionName: bp.functionName,
452
mode: bp.mode,
453
});
454
}
455
}
456
457
// send DTOs to VS Code
458
return this._debugServiceProxy.$registerBreakpoints(dtos);
459
}
460
461
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void> {
462
// remove from array
463
const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id));
464
465
// send notification
466
this.fireBreakpointChanges([], breakpoints, []);
467
468
// unregister with VS Code
469
const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id);
470
const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id);
471
const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id);
472
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids);
473
}
474
475
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean> {
476
const testRunMeta = options.testRun && this._testing.getMetadataForRun(options.testRun);
477
478
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, {
479
parentSessionID: options.parentSession ? options.parentSession.id : undefined,
480
lifecycleManagedByParent: options.lifecycleManagedByParent,
481
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate',
482
noDebug: options.noDebug,
483
compact: options.compact,
484
suppressSaveBeforeStart: options.suppressSaveBeforeStart,
485
testRun: testRunMeta && {
486
runId: testRunMeta.runId,
487
taskId: testRunMeta.taskId,
488
},
489
490
// Check debugUI for back-compat, #147264
491
// eslint-disable-next-line local/code-no-any-casts
492
suppressDebugStatusbar: options.suppressDebugStatusbar ?? (options as any).debugUI?.simple,
493
// eslint-disable-next-line local/code-no-any-casts
494
suppressDebugToolbar: options.suppressDebugToolbar ?? (options as any).debugUI?.simple,
495
// eslint-disable-next-line local/code-no-any-casts
496
suppressDebugView: options.suppressDebugView ?? (options as any).debugUI?.simple,
497
});
498
}
499
500
public stopDebugging(session?: vscode.DebugSession): Promise<void> {
501
return this._debugServiceProxy.$stopDebugging(session ? session.id : undefined);
502
}
503
504
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable {
505
506
if (!provider) {
507
return new Disposable(() => { });
508
}
509
510
const handle = this._configProviderHandleCounter++;
511
this._configProviders.push({ type, handle, provider });
512
513
this._debugServiceProxy.$registerDebugConfigurationProvider(type, trigger,
514
!!provider.provideDebugConfigurations,
515
!!provider.resolveDebugConfiguration,
516
!!provider.resolveDebugConfigurationWithSubstitutedVariables,
517
handle);
518
519
return new Disposable(() => {
520
this._configProviders = this._configProviders.filter(p => p.provider !== provider); // remove
521
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
522
});
523
}
524
525
public registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable {
526
527
if (!factory) {
528
return new Disposable(() => { });
529
}
530
531
// a DebugAdapterDescriptorFactory can only be registered in the extension that contributes the debugger
532
if (!this.definesDebugType(extension, type)) {
533
throw new Error(`a DebugAdapterDescriptorFactory can only be registered from the extension that defines the '${type}' debugger.`);
534
}
535
536
// make sure that only one factory for this type is registered
537
if (this.getAdapterDescriptorFactoryByType(type)) {
538
throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`);
539
}
540
541
const handle = this._adapterFactoryHandleCounter++;
542
this._adapterFactories.push({ type, handle, factory });
543
544
this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle);
545
546
return new Disposable(() => {
547
this._adapterFactories = this._adapterFactories.filter(p => p.factory !== factory); // remove
548
this._debugServiceProxy.$unregisterDebugAdapterDescriptorFactory(handle);
549
});
550
}
551
552
public registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable {
553
554
if (!factory) {
555
return new Disposable(() => { });
556
}
557
558
const handle = this._trackerFactoryHandleCounter++;
559
this._trackerFactories.push({ type, handle, factory });
560
561
return new Disposable(() => {
562
this._trackerFactories = this._trackerFactories.filter(p => p.factory !== factory); // remove
563
});
564
}
565
566
// RPC methods (ExtHostDebugServiceShape)
567
568
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
569
return Promise.resolve(undefined);
570
}
571
572
public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise<IConfig> {
573
let ws: IWorkspaceFolderData | undefined;
574
const folder = await this.getFolder(folderUri);
575
if (folder) {
576
ws = {
577
uri: folder.uri,
578
name: folder.name,
579
index: folder.index,
580
};
581
}
582
const variableResolver = await this._variableResolver.getResolver();
583
return variableResolver.resolveAsync(ws, config);
584
}
585
586
protected createDebugAdapter(adapter: vscode.DebugAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
587
if (adapter instanceof DebugAdapterInlineImplementation) {
588
return new DirectDebugAdapter(adapter.implementation);
589
}
590
return undefined;
591
}
592
593
protected createSignService(): ISignService | undefined {
594
return undefined;
595
}
596
597
public async $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise<void> {
598
const mythis = this;
599
600
const session = await this.getSession(sessionDto);
601
602
return this.getAdapterDescriptor(this.getAdapterDescriptorFactoryByType(session.type), session).then(daDescriptor => {
603
604
if (!daDescriptor) {
605
throw new Error(`Couldn't find a debug adapter descriptor for debug type '${session.type}' (extension might have failed to activate)`);
606
}
607
608
const da = this.createDebugAdapter(daDescriptor, session);
609
if (!da) {
610
throw new Error(`Couldn't create a debug adapter for type '${session.type}'.`);
611
}
612
613
const debugAdapter = da;
614
615
this._debugAdapters.set(debugAdapterHandle, debugAdapter);
616
617
return this.getDebugAdapterTrackers(session).then(tracker => {
618
619
if (tracker) {
620
this._debugAdaptersTrackers.set(debugAdapterHandle, tracker);
621
}
622
623
debugAdapter.onMessage(async message => {
624
625
if (message.type === 'request' && (<DebugProtocol.Request>message).command === 'handshake') {
626
627
const request = <DebugProtocol.Request>message;
628
629
const response: DebugProtocol.Response = {
630
type: 'response',
631
seq: 0,
632
command: request.command,
633
request_seq: request.seq,
634
success: true
635
};
636
637
if (!this._signService) {
638
this._signService = this.createSignService();
639
}
640
641
try {
642
if (this._signService) {
643
const signature = await this._signService.sign(request.arguments.value);
644
response.body = {
645
signature: signature
646
};
647
debugAdapter.sendResponse(response);
648
} else {
649
throw new Error('no signer');
650
}
651
} catch (e) {
652
response.success = false;
653
response.message = e.message;
654
debugAdapter.sendResponse(response);
655
}
656
} else {
657
if (tracker && tracker.onDidSendMessage) {
658
tracker.onDidSendMessage(message);
659
}
660
661
// DA -> VS Code
662
try {
663
// Try to catch details for #233167
664
message = convertToVSCPaths(message, true);
665
} catch (e) {
666
// eslint-disable-next-line local/code-no-any-casts
667
const type = message.type + '_' + ((message as any).command ?? (message as any).event ?? '');
668
this._telemetryProxy.$publicLog2<DebugProtocolMessageErrorEvent, DebugProtocolMessageErrorClassification>('debugProtocolMessageError', { type, from: session.type });
669
throw e;
670
}
671
672
mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message);
673
}
674
});
675
debugAdapter.onError(err => {
676
if (tracker && tracker.onError) {
677
tracker.onError(err);
678
}
679
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
680
});
681
debugAdapter.onExit((code: number | null) => {
682
if (tracker && tracker.onExit) {
683
tracker.onExit(code ?? undefined, undefined);
684
}
685
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code ?? undefined, undefined);
686
});
687
688
if (tracker && tracker.onWillStartSession) {
689
tracker.onWillStartSession();
690
}
691
692
return debugAdapter.startSession();
693
});
694
});
695
}
696
697
public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): void {
698
699
// VS Code -> DA
700
message = convertToDAPaths(message, false);
701
702
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle?
703
if (tracker && tracker.onWillReceiveMessage) {
704
tracker.onWillReceiveMessage(message);
705
}
706
707
const da = this._debugAdapters.get(debugAdapterHandle);
708
da?.sendMessage(message);
709
}
710
711
public $stopDASession(debugAdapterHandle: number): Promise<void> {
712
713
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle);
714
this._debugAdaptersTrackers.delete(debugAdapterHandle);
715
if (tracker && tracker.onWillStopSession) {
716
tracker.onWillStopSession();
717
}
718
719
const da = this._debugAdapters.get(debugAdapterHandle);
720
this._debugAdapters.delete(debugAdapterHandle);
721
if (da) {
722
return da.stopSession();
723
} else {
724
return Promise.resolve(void 0);
725
}
726
}
727
728
public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {
729
730
const a: vscode.Breakpoint[] = [];
731
const r: vscode.Breakpoint[] = [];
732
const c: vscode.Breakpoint[] = [];
733
734
if (delta.added) {
735
for (const bpd of delta.added) {
736
const id = bpd.id;
737
if (id && !this._breakpoints.has(id)) {
738
let bp: Breakpoint;
739
if (bpd.type === 'function') {
740
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode);
741
} else if (bpd.type === 'data') {
742
bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage, bpd.mode);
743
} else {
744
const uri = URI.revive(bpd.uri);
745
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode);
746
}
747
setBreakpointId(bp, id);
748
this._breakpoints.set(id, bp);
749
a.push(bp);
750
}
751
}
752
}
753
754
if (delta.removed) {
755
for (const id of delta.removed) {
756
const bp = this._breakpoints.get(id);
757
if (bp) {
758
this._breakpoints.delete(id);
759
r.push(bp);
760
}
761
}
762
}
763
764
if (delta.changed) {
765
for (const bpd of delta.changed) {
766
if (bpd.id) {
767
const bp = this._breakpoints.get(bpd.id);
768
if (bp) {
769
if (bp instanceof FunctionBreakpoint && bpd.type === 'function') {
770
// eslint-disable-next-line local/code-no-any-casts
771
const fbp = <any>bp;
772
fbp.enabled = bpd.enabled;
773
fbp.condition = bpd.condition;
774
fbp.hitCondition = bpd.hitCondition;
775
fbp.logMessage = bpd.logMessage;
776
fbp.functionName = bpd.functionName;
777
} else if (bp instanceof SourceBreakpoint && bpd.type === 'source') {
778
// eslint-disable-next-line local/code-no-any-casts
779
const sbp = <any>bp;
780
sbp.enabled = bpd.enabled;
781
sbp.condition = bpd.condition;
782
sbp.hitCondition = bpd.hitCondition;
783
sbp.logMessage = bpd.logMessage;
784
sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character));
785
}
786
c.push(bp);
787
}
788
}
789
}
790
}
791
792
this.fireBreakpointChanges(a, r, c);
793
}
794
795
public async $acceptStackFrameFocus(focusDto: IThreadFocusDto | IStackFrameFocusDto | undefined): Promise<void> {
796
let focus: vscode.DebugThread | vscode.DebugStackFrame | undefined;
797
if (focusDto) {
798
const session = await this.getSession(focusDto.sessionId);
799
if (focusDto.kind === 'thread') {
800
focus = new DebugThread(session.api, focusDto.threadId);
801
} else {
802
focus = new DebugStackFrame(session.api, focusDto.threadId, focusDto.frameId);
803
}
804
}
805
806
this._activeStackItem = focus;
807
this._onDidChangeActiveStackItem.fire(this._activeStackItem);
808
}
809
810
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise<vscode.DebugConfiguration[]> {
811
return asPromise(async () => {
812
const provider = this.getConfigProviderByHandle(configProviderHandle);
813
if (!provider) {
814
throw new Error('no DebugConfigurationProvider found');
815
}
816
if (!provider.provideDebugConfigurations) {
817
throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations');
818
}
819
const folder = await this.getFolder(folderUri);
820
return provider.provideDebugConfigurations(folder, token);
821
}).then(debugConfigurations => {
822
if (!debugConfigurations) {
823
throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations');
824
}
825
return debugConfigurations;
826
});
827
}
828
829
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise<vscode.DebugConfiguration | null | undefined> {
830
return asPromise(async () => {
831
const provider = this.getConfigProviderByHandle(configProviderHandle);
832
if (!provider) {
833
throw new Error('no DebugConfigurationProvider found');
834
}
835
if (!provider.resolveDebugConfiguration) {
836
throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration');
837
}
838
const folder = await this.getFolder(folderUri);
839
return provider.resolveDebugConfiguration(folder, debugConfiguration, token);
840
});
841
}
842
843
public $resolveDebugConfigurationWithSubstitutedVariables(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise<vscode.DebugConfiguration | null | undefined> {
844
return asPromise(async () => {
845
const provider = this.getConfigProviderByHandle(configProviderHandle);
846
if (!provider) {
847
throw new Error('no DebugConfigurationProvider found');
848
}
849
if (!provider.resolveDebugConfigurationWithSubstitutedVariables) {
850
throw new Error('DebugConfigurationProvider has no method resolveDebugConfigurationWithSubstitutedVariables');
851
}
852
const folder = await this.getFolder(folderUri);
853
return provider.resolveDebugConfigurationWithSubstitutedVariables(folder, debugConfiguration, token);
854
});
855
}
856
857
public async $provideDebugAdapter(adapterFactoryHandle: number, sessionDto: IDebugSessionDto): Promise<Dto<IAdapterDescriptor>> {
858
const adapterDescriptorFactory = this.getAdapterDescriptorFactoryByHandle(adapterFactoryHandle);
859
if (!adapterDescriptorFactory) {
860
return Promise.reject(new Error('no adapter descriptor factory found for handle'));
861
}
862
const session = await this.getSession(sessionDto);
863
return this.getAdapterDescriptor(adapterDescriptorFactory, session).then(adapterDescriptor => {
864
if (!adapterDescriptor) {
865
throw new Error(`Couldn't find a debug adapter descriptor for debug type '${session.type}'`);
866
}
867
return this.convertToDto(adapterDescriptor);
868
});
869
}
870
871
public async $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): Promise<void> {
872
const session = await this.getSession(sessionDto);
873
this._onDidStartDebugSession.fire(session.api);
874
}
875
876
public async $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): Promise<void> {
877
const session = await this.getSession(sessionDto);
878
if (session) {
879
this._onDidTerminateDebugSession.fire(session.api);
880
this._debugSessions.delete(session.id);
881
}
882
}
883
884
public async $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto | undefined): Promise<void> {
885
this._activeDebugSession = sessionDto ? await this.getSession(sessionDto) : undefined;
886
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession?.api);
887
}
888
889
public async $acceptDebugSessionNameChanged(sessionDto: IDebugSessionDto, name: string): Promise<void> {
890
const session = await this.getSession(sessionDto);
891
session?._acceptNameChanged(name);
892
}
893
894
public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise<void> {
895
const session = await this.getSession(sessionDto);
896
const ee: vscode.DebugSessionCustomEvent = {
897
session: session.api,
898
event: event.event,
899
body: event.body
900
};
901
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
902
}
903
904
// private & dto helpers
905
906
private convertToDto(x: vscode.DebugAdapterDescriptor): Dto<IAdapterDescriptor> {
907
if (x instanceof DebugAdapterExecutable) {
908
return this.convertExecutableToDto(x);
909
} else if (x instanceof DebugAdapterServer) {
910
return this.convertServerToDto(x);
911
} else if (x instanceof DebugAdapterNamedPipeServer) {
912
return this.convertPipeServerToDto(x);
913
} else if (x instanceof DebugAdapterInlineImplementation) {
914
return this.convertImplementationToDto(x);
915
} else {
916
throw new Error('convertToDto unexpected type');
917
}
918
}
919
920
protected convertExecutableToDto(x: DebugAdapterExecutable): IDebugAdapterExecutable {
921
return {
922
type: 'executable',
923
command: x.command,
924
args: x.args,
925
options: x.options
926
};
927
}
928
929
protected convertServerToDto(x: DebugAdapterServer): IDebugAdapterServer {
930
return {
931
type: 'server',
932
port: x.port,
933
host: x.host
934
};
935
}
936
937
protected convertPipeServerToDto(x: DebugAdapterNamedPipeServer): IDebugAdapterNamedPipeServer {
938
return {
939
type: 'pipeServer',
940
path: x.path
941
};
942
}
943
944
protected convertImplementationToDto(x: DebugAdapterInlineImplementation): IDebugAdapterImpl {
945
return {
946
type: 'implementation',
947
};
948
}
949
950
private getAdapterDescriptorFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined {
951
const results = this._adapterFactories.filter(p => p.type === type);
952
if (results.length > 0) {
953
return results[0].factory;
954
}
955
return undefined;
956
}
957
958
private getAdapterDescriptorFactoryByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined {
959
const results = this._adapterFactories.filter(p => p.handle === handle);
960
if (results.length > 0) {
961
return results[0].factory;
962
}
963
return undefined;
964
}
965
966
private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider | undefined {
967
const results = this._configProviders.filter(p => p.handle === handle);
968
if (results.length > 0) {
969
return results[0].provider;
970
}
971
return undefined;
972
}
973
974
private definesDebugType(ed: IExtensionDescription, type: string) {
975
if (ed.contributes) {
976
const debuggers = ed.contributes['debuggers'];
977
if (debuggers && debuggers.length > 0) {
978
for (const dbg of debuggers) {
979
// only debugger contributions with a "label" are considered a "defining" debugger contribution
980
if (dbg.label && dbg.type) {
981
if (dbg.type === type) {
982
return true;
983
}
984
}
985
}
986
}
987
}
988
return false;
989
}
990
991
private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise<vscode.DebugAdapterTracker | undefined> {
992
993
const config = session.configuration;
994
const type = config.type;
995
996
const promises = this._trackerFactories
997
.filter(tuple => tuple.type === type || tuple.type === '*')
998
.map(tuple => asPromise<vscode.ProviderResult<vscode.DebugAdapterTracker>>(() => tuple.factory.createDebugAdapterTracker(session.api)).then(p => p, err => null));
999
1000
return Promise.race([
1001
Promise.all(promises).then(result => {
1002
const trackers = coalesce(result); // filter null
1003
if (trackers.length > 0) {
1004
return new MultiTracker(trackers);
1005
}
1006
return undefined;
1007
}),
1008
new Promise<undefined>(resolve => setTimeout(() => resolve(undefined), 1000)),
1009
]).catch(err => {
1010
// ignore errors
1011
return undefined;
1012
});
1013
}
1014
1015
private async getAdapterDescriptor(adapterDescriptorFactory: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor | undefined> {
1016
1017
// a "debugServer" attribute in the launch config takes precedence
1018
const serverPort = session.configuration.debugServer;
1019
if (typeof serverPort === 'number') {
1020
return Promise.resolve(new DebugAdapterServer(serverPort));
1021
}
1022
1023
if (adapterDescriptorFactory) {
1024
const extensionRegistry = await this._extensionService.getExtensionRegistry();
1025
return asPromise(() => adapterDescriptorFactory.createDebugAdapterDescriptor(session.api, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => {
1026
if (daDescriptor) {
1027
return daDescriptor;
1028
}
1029
return undefined;
1030
});
1031
}
1032
1033
// fallback: use executable information from package.json
1034
const extensionRegistry = await this._extensionService.getExtensionRegistry();
1035
return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry));
1036
}
1037
1038
protected daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
1039
return undefined;
1040
}
1041
1042
private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) {
1043
if (added.length > 0 || removed.length > 0 || changed.length > 0) {
1044
this._onDidChangeBreakpoints.fire(Object.freeze({
1045
added,
1046
removed,
1047
changed,
1048
}));
1049
}
1050
}
1051
1052
private async getSession(dto: IDebugSessionDto): Promise<ExtHostDebugSession> {
1053
if (dto) {
1054
if (typeof dto === 'string') {
1055
const ds = this._debugSessions.get(dto);
1056
if (ds) {
1057
return ds;
1058
}
1059
} else {
1060
let ds = this._debugSessions.get(dto.id);
1061
if (!ds) {
1062
const folder = await this.getFolder(dto.folderUri);
1063
const parent = dto.parent ? this._debugSessions.get(dto.parent) : undefined;
1064
ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration, parent?.api);
1065
this._debugSessions.set(ds.id, ds);
1066
this._debugServiceProxy.$sessionCached(ds.id);
1067
}
1068
return ds;
1069
}
1070
}
1071
throw new Error('cannot find session');
1072
}
1073
1074
private getFolder(_folderUri: UriComponents | undefined): Promise<vscode.WorkspaceFolder | undefined> {
1075
if (_folderUri) {
1076
const folderURI = URI.revive(_folderUri);
1077
return this._workspaceService.resolveWorkspaceFolder(folderURI);
1078
}
1079
return Promise.resolve(undefined);
1080
}
1081
1082
private extensionVisKey(extensionId: string, id: string) {
1083
return `${extensionId}\0${id}`;
1084
}
1085
1086
private serializeVisualization(extensionId: string, viz: vscode.DebugVisualization['visualization']): MainThreadDebugVisualization | undefined {
1087
if (!viz) {
1088
return undefined;
1089
}
1090
1091
if ('title' in viz && 'command' in viz) {
1092
return { type: DebugVisualizationType.Command };
1093
}
1094
1095
if ('treeId' in viz) {
1096
return { type: DebugVisualizationType.Tree, id: `${extensionId}\0${viz.treeId}` };
1097
}
1098
1099
throw new Error('Unsupported debug visualization type');
1100
}
1101
1102
private getIconPathOrClass(icon: vscode.DebugVisualization['iconPath']) {
1103
const iconPathOrIconClass = this.getIconUris(icon);
1104
let iconPath: { dark: URI; light?: URI | undefined } | undefined;
1105
let iconClass: string | undefined;
1106
if ('id' in iconPathOrIconClass) {
1107
iconClass = ThemeIconUtils.asClassName(iconPathOrIconClass);
1108
} else {
1109
iconPath = iconPathOrIconClass;
1110
}
1111
1112
return {
1113
iconPath,
1114
iconClass
1115
};
1116
}
1117
1118
private getIconUris(iconPath: vscode.DebugVisualization['iconPath']): { dark: URI; light?: URI } | { id: string } {
1119
if (iconPath instanceof ThemeIcon) {
1120
return { id: iconPath.id };
1121
}
1122
const dark = typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath;
1123
const light = typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath;
1124
return {
1125
dark: (typeof dark === 'string' ? URI.file(dark) : dark) as URI,
1126
light: (typeof light === 'string' ? URI.file(light) : light) as URI,
1127
};
1128
}
1129
}
1130
1131
export class ExtHostDebugSession {
1132
private apiSession?: vscode.DebugSession;
1133
constructor(
1134
private _debugServiceProxy: MainThreadDebugServiceShape,
1135
private _id: DebugSessionUUID,
1136
private _type: string,
1137
private _name: string,
1138
private _workspaceFolder: vscode.WorkspaceFolder | undefined,
1139
private _configuration: vscode.DebugConfiguration,
1140
private _parentSession: vscode.DebugSession | undefined) {
1141
}
1142
1143
public get api(): vscode.DebugSession {
1144
const that = this;
1145
return this.apiSession ??= Object.freeze({
1146
id: that._id,
1147
type: that._type,
1148
get name() {
1149
return that._name;
1150
},
1151
set name(name: string) {
1152
that._name = name;
1153
that._debugServiceProxy.$setDebugSessionName(that._id, name);
1154
},
1155
parentSession: that._parentSession,
1156
workspaceFolder: that._workspaceFolder,
1157
configuration: that._configuration,
1158
customRequest(command: string, args: any): Promise<any> {
1159
return that._debugServiceProxy.$customDebugAdapterRequest(that._id, command, args);
1160
},
1161
getDebugProtocolBreakpoint(breakpoint: vscode.Breakpoint): Promise<vscode.DebugProtocolBreakpoint | undefined> {
1162
return that._debugServiceProxy.$getDebugProtocolBreakpoint(that._id, breakpoint.id);
1163
}
1164
});
1165
}
1166
1167
public get id(): string {
1168
return this._id;
1169
}
1170
1171
public get type(): string {
1172
return this._type;
1173
}
1174
1175
_acceptNameChanged(name: string) {
1176
this._name = name;
1177
}
1178
1179
public get configuration(): vscode.DebugConfiguration {
1180
return this._configuration;
1181
}
1182
}
1183
1184
export class ExtHostDebugConsole {
1185
1186
readonly value: vscode.DebugConsole;
1187
1188
constructor(proxy: MainThreadDebugServiceShape) {
1189
1190
this.value = Object.freeze({
1191
append(value: string): void {
1192
proxy.$appendDebugConsole(value);
1193
},
1194
appendLine(value: string): void {
1195
this.append(value + '\n');
1196
}
1197
});
1198
}
1199
}
1200
1201
interface ConfigProviderTuple {
1202
type: string;
1203
handle: number;
1204
provider: vscode.DebugConfigurationProvider;
1205
}
1206
1207
interface DescriptorFactoryTuple {
1208
type: string;
1209
handle: number;
1210
factory: vscode.DebugAdapterDescriptorFactory;
1211
}
1212
1213
interface TrackerFactoryTuple {
1214
type: string;
1215
handle: number;
1216
factory: vscode.DebugAdapterTrackerFactory;
1217
}
1218
1219
class MultiTracker implements vscode.DebugAdapterTracker {
1220
1221
constructor(private trackers: vscode.DebugAdapterTracker[]) {
1222
}
1223
1224
onWillStartSession(): void {
1225
this.trackers.forEach(t => t.onWillStartSession ? t.onWillStartSession() : undefined);
1226
}
1227
1228
onWillReceiveMessage(message: any): void {
1229
this.trackers.forEach(t => t.onWillReceiveMessage ? t.onWillReceiveMessage(message) : undefined);
1230
}
1231
1232
onDidSendMessage(message: any): void {
1233
this.trackers.forEach(t => t.onDidSendMessage ? t.onDidSendMessage(message) : undefined);
1234
}
1235
1236
onWillStopSession(): void {
1237
this.trackers.forEach(t => t.onWillStopSession ? t.onWillStopSession() : undefined);
1238
}
1239
1240
onError(error: Error): void {
1241
this.trackers.forEach(t => t.onError ? t.onError(error) : undefined);
1242
}
1243
1244
onExit(code: number, signal: string): void {
1245
this.trackers.forEach(t => t.onExit ? t.onExit(code, signal) : undefined);
1246
}
1247
}
1248
1249
/*
1250
* Call directly into a debug adapter implementation
1251
*/
1252
class DirectDebugAdapter extends AbstractDebugAdapter {
1253
1254
constructor(private implementation: vscode.DebugAdapter) {
1255
super();
1256
1257
implementation.onDidSendMessage((message: vscode.DebugProtocolMessage) => {
1258
this.acceptMessage(message as DebugProtocol.ProtocolMessage);
1259
});
1260
}
1261
1262
startSession(): Promise<void> {
1263
return Promise.resolve(undefined);
1264
}
1265
1266
sendMessage(message: DebugProtocol.ProtocolMessage): void {
1267
this.implementation.handleMessage(message);
1268
}
1269
1270
stopSession(): Promise<void> {
1271
this.implementation.dispose();
1272
return Promise.resolve(undefined);
1273
}
1274
}
1275
1276
1277
export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
1278
constructor(
1279
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
1280
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
1281
@IExtHostExtensionService extensionService: IExtHostExtensionService,
1282
@IExtHostConfiguration configurationService: IExtHostConfiguration,
1283
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
1284
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider,
1285
@IExtHostCommands commands: IExtHostCommands,
1286
@IExtHostTesting testing: IExtHostTesting,
1287
) {
1288
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands, testing);
1289
}
1290
}
1291
1292
// Collecting info for #233167 specifically
1293
type DebugProtocolMessageErrorClassification = {
1294
from: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The type of the debug adapter that the event is from.' };
1295
type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The type of the event that was malformed.' };
1296
owner: 'roblourens';
1297
comment: 'Sent to collect details about misbehaving debug extensions.';
1298
};
1299
1300
type DebugProtocolMessageErrorEvent = {
1301
from: string;
1302
type: string;
1303
};
1304
1305