debugSession.ts 33.1 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

I
isidor 已提交
6
import { URI } from 'vs/base/common/uri';
7
import * as resources from 'vs/base/common/resources';
I
isidor 已提交
8 9 10 11
import * as nls from 'vs/nls';
import * as platform from 'vs/base/common/platform';
import severity from 'vs/base/common/severity';
import { Event, Emitter } from 'vs/base/common/event';
12
import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes';
I
isidor 已提交
13
import { Position, IPosition } from 'vs/editor/common/core/position';
I
isidor 已提交
14
import * as aria from 'vs/base/browser/ui/aria/aria';
15
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
16
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
17
import { mixin } from 'vs/base/common/objects';
18
import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel';
A
Andre Weinand 已提交
19
import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession';
20
import { IProductService } from 'vs/platform/product/common/productService';
I
isidor 已提交
21
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
I
isidor 已提交
22 23 24
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { generateUuid } from 'vs/base/common/uuid';
25 26
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
I
isidor 已提交
27
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
A
Andre Weinand 已提交
28
import { normalizeDriveLetter } from 'vs/base/common/labels';
29
import { Range } from 'vs/editor/common/core/range';
I
isidor 已提交
30 31
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
32
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
33
import { onUnexpectedError } from 'vs/base/common/errors';
I
isidor 已提交
34
import { INotificationService } from 'vs/platform/notification/common/notification';
35
import { IOpenerService } from 'vs/platform/opener/common/opener';
36
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
I
isidor 已提交
37
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
I
isidor 已提交
38
import { distinct } from 'vs/base/common/arrays';
I
isidor 已提交
39

40
export class DebugSession implements IDebugSession {
41

A
Andre Weinand 已提交
42
	private id: string;
43
	private _subId: string | undefined;
I
isidor 已提交
44
	private raw: RawDebugSession | undefined;
I
isidor 已提交
45
	private initialized = false;
46
	private _options: IDebugSessionOptions;
A
Andre Weinand 已提交
47

I
isidor 已提交
48 49
	private sources = new Map<string, Source>();
	private threads = new Map<number, Thread>();
I
isidor 已提交
50
	private cancellationMap = new Map<number, CancellationTokenSource[]>();
I
isidor 已提交
51
	private rawListeners: IDisposable[] = [];
I
isidor 已提交
52
	private fetchThreadsScheduler: RunOnceScheduler | undefined;
53
	private repl: ReplModel;
A
Andre Weinand 已提交
54

55
	private readonly _onDidChangeState = new Emitter<void>();
A
Andre Weinand 已提交
56 57
	private readonly _onDidEndAdapter = new Emitter<AdapterEndEvent>();

I
isidor 已提交
58
	private readonly _onDidLoadedSource = new Emitter<LoadedSourceEvent>();
A
Andre Weinand 已提交
59
	private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();
A
Andre Weinand 已提交
60

I
isidor 已提交
61
	private readonly _onDidChangeREPLElements = new Emitter<void>();
62

63 64 65
	private name: string | undefined;
	private readonly _onDidChangeName = new Emitter<string>();

I
isidor 已提交
66
	constructor(
I
isidor 已提交
67
		private _configuration: { resolved: IConfig, unresolved: IConfig | undefined },
I
isidor 已提交
68
		public root: IWorkspaceFolder,
69
		private model: DebugModel,
70
		options: IDebugSessionOptions | undefined,
71 72 73 74 75
		@IDebugService private readonly debugService: IDebugService,
		@ITelemetryService private readonly telemetryService: ITelemetryService,
		@IWindowService private readonly windowService: IWindowService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IViewletService private readonly viewletService: IViewletService,
A
Andre Weinand 已提交
76
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
I
isidor 已提交
77
		@INotificationService private readonly notificationService: INotificationService,
A
Andre Weinand 已提交
78
		@IProductService private readonly productService: IProductService,
79
		@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
80
		@IOpenerService private readonly openerService: IOpenerService
I
isidor 已提交
81
	) {
A
Andre Weinand 已提交
82
		this.id = generateUuid();
83 84 85 86 87 88
		this._options = options || {};
		if (this.hasSeparateRepl()) {
			this.repl = new ReplModel();
		} else {
			this.repl = (this.parentSession as DebugSession).repl;
		}
89 90
	}

A
Andre Weinand 已提交
91 92 93 94
	getId(): string {
		return this.id;
	}

95 96 97 98 99 100 101 102
	setSubId(subId: string | undefined) {
		this._subId = subId;
	}

	get subId(): string | undefined {
		return this._subId;
	}

I
isidor 已提交
103
	get configuration(): IConfig {
104 105 106
		return this._configuration.resolved;
	}

I
isidor 已提交
107
	get unresolvedConfiguration(): IConfig | undefined {
108 109 110
		return this._configuration.unresolved;
	}

I
isidor 已提交
111
	get parentSession(): IDebugSession | undefined {
112
		return this._options.parentSession;
I
isidor 已提交
113 114
	}

I
isidor 已提交
115
	setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }) {
I
isidor 已提交
116 117 118
		this._configuration = configuration;
	}

I
isidor 已提交
119 120
	getLabel(): string {
		const includeRoot = this.workspaceContextService.getWorkspace().folders.length > 1;
121 122 123 124 125 126 127
		const name = this.name || this.configuration.name;
		return includeRoot && this.root ? `${name} (${resources.basenameOrAuthority(this.root.uri)})` : name;
	}

	setName(name: string): void {
		this.name = name;
		this._onDidChangeName.fire(name);
I
isidor 已提交
128 129 130
	}

	get state(): State {
I
isidor 已提交
131 132 133
		if (!this.initialized) {
			return State.Initializing;
		}
134 135 136 137
		if (!this.raw) {
			return State.Inactive;
		}

138
		const focusedThread = this.debugService.getViewModel().focusedThread;
I
isidor 已提交
139
		if (focusedThread && focusedThread.session === this) {
140 141 142 143
			return focusedThread.stopped ? State.Stopped : State.Running;
		}
		if (this.getAllThreads().some(t => t.stopped)) {
			return State.Stopped;
A
Andre Weinand 已提交
144
		}
145

146
		return State.Running;
A
Andre Weinand 已提交
147 148 149
	}

	get capabilities(): DebugProtocol.Capabilities {
150
		return this.raw ? this.raw.capabilities : Object.create(null);
151 152
	}

A
Andre Weinand 已提交
153
	//---- events
154
	get onDidChangeState(): Event<void> {
I
isidor 已提交
155
		return this._onDidChangeState.event;
156 157
	}

A
Andre Weinand 已提交
158 159 160 161
	get onDidEndAdapter(): Event<AdapterEndEvent> {
		return this._onDidEndAdapter.event;
	}

I
isidor 已提交
162 163 164 165
	get onDidChangeReplElements(): Event<void> {
		return this._onDidChangeREPLElements.event;
	}

166 167 168 169
	get onDidChangeName(): Event<string> {
		return this._onDidChangeName.event;
	}

A
Andre Weinand 已提交
170 171
	//---- DAP events

A
Andre Weinand 已提交
172
	get onDidCustomEvent(): Event<DebugProtocol.Event> {
I
isidor 已提交
173 174 175
		return this._onDidCustomEvent.event;
	}

A
Andre Weinand 已提交
176 177
	get onDidLoadedSource(): Event<LoadedSourceEvent> {
		return this._onDidLoadedSource.event;
178 179
	}

A
Andre Weinand 已提交
180 181 182 183 184
	//---- DAP requests

	/**
	 * create and initialize a new debug adapter for this session
	 */
J
Johannes Rieken 已提交
185
	initialize(dbgr: IDebugger): Promise<void> {
186

187
		if (this.raw) {
188
			// if there was already a connection make sure to remove old listeners
189
			this.shutdown();
I
isidor 已提交
190
		}
191

I
isidor 已提交
192
		return dbgr.getCustomTelemetryService().then(customTelemetryService => {
193

A
Andre Weinand 已提交
194
			return dbgr.createDebugAdapter(this).then(debugAdapter => {
A
Andre Weinand 已提交
195

196
				this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
A
Andre Weinand 已提交
197

I
isidor 已提交
198
				return this.raw.start().then(() => {
A
Andre Weinand 已提交
199 200 201

					this.registerListeners();

I
isidor 已提交
202
					return this.raw!.initialize({
A
Andre Weinand 已提交
203
						clientID: 'vscode',
204
						clientName: this.productService.nameLong,
A
Andre Weinand 已提交
205 206 207 208 209 210 211 212
						adapterID: this.configuration.type,
						pathFormat: 'path',
						linesStartAt1: true,
						columnsStartAt1: true,
						supportsVariableType: true, // #8858
						supportsVariablePaging: true, // #9537
						supportsRunInTerminalRequest: true, // #10574
						locale: platform.locale
213
					}).then(() => {
I
isidor 已提交
214
						this.initialized = true;
215
						this._onDidChangeState.fire();
I
isidor 已提交
216
						this.model.setExceptionBreakpoints(this.raw!.capabilities.exceptionBreakpointFilters || []);
A
Andre Weinand 已提交
217 218
					});
				});
I
isidor 已提交
219
			});
I
isidor 已提交
220 221 222 223
		}).then(undefined, err => {
			this.initialized = true;
			this._onDidChangeState.fire();
			return Promise.reject(err);
I
isidor 已提交
224 225 226
		});
	}

A
Andre Weinand 已提交
227 228 229
	/**
	 * launch or attach to the debuggee
	 */
230
	launchOrAttach(config: IConfig): Promise<void> {
231
		if (this.raw) {
A
Andre Weinand 已提交
232 233 234 235

			// __sessionID only used for EH debugging (but we add it always for now...)
			config.__sessionId = this.getId();

236
			return this.raw.launchOrAttach(config).then(result => {
R
Rob Lourens 已提交
237
				return undefined;
A
Andre Weinand 已提交
238 239
			});
		}
240
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
241 242 243 244 245
	}

	/**
	 * end the current debug adapter session
	 */
246
	terminate(restart = false): Promise<void> {
247
		if (this.raw) {
I
isidor 已提交
248
			this.cancelAllRequests();
249 250
			if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') {
				return this.raw.terminate(restart).then(response => {
R
Rob Lourens 已提交
251
					return undefined;
A
Andre Weinand 已提交
252 253
				});
			}
254
			return this.raw.disconnect(restart).then(response => {
R
Rob Lourens 已提交
255
				return undefined;
A
Andre Weinand 已提交
256 257
			});
		}
258
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
259 260 261 262 263
	}

	/**
	 * end the current debug adapter session
	 */
264
	disconnect(restart = false): Promise<void> {
265
		if (this.raw) {
I
isidor 已提交
266
			this.cancelAllRequests();
267
			return this.raw.disconnect(restart).then(response => {
R
Rob Lourens 已提交
268
				return undefined;
A
Andre Weinand 已提交
269 270
			});
		}
271
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
272 273 274 275 276
	}

	/**
	 * restart debug adapter session
	 */
277
	restart(): Promise<void> {
278
		if (this.raw) {
I
isidor 已提交
279
			this.cancelAllRequests();
280
			return this.raw.restart().then(() => undefined);
A
Andre Weinand 已提交
281
		}
282
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
283 284
	}

285
	sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): Promise<void> {
A
Andre Weinand 已提交
286

287
		if (!this.raw) {
288
			return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
289 290
		}

291
		if (!this.raw.readyForBreakpoints) {
I
isidor 已提交
292
			return Promise.resolve(undefined);
A
Andre Weinand 已提交
293 294
		}

I
isidor 已提交
295
		const rawSource = this.getRawSource(modelUri);
A
Andre Weinand 已提交
296 297 298 299
		if (breakpointsToSend.length && !rawSource.adapterData) {
			rawSource.adapterData = breakpointsToSend[0].adapterData;
		}
		// Normalize all drive letters going out from vscode to debug adapters so we are consistent with our resolving #43959
I
isidor 已提交
300 301 302
		if (rawSource.path) {
			rawSource.path = normalizeDriveLetter(rawSource.path);
		}
A
Andre Weinand 已提交
303

304
		return this.raw.setBreakpoints({
A
Andre Weinand 已提交
305
			source: rawSource,
306 307
			lines: breakpointsToSend.map(bp => bp.sessionAgnosticData.lineNumber),
			breakpoints: breakpointsToSend.map(bp => ({ line: bp.sessionAgnosticData.lineNumber, column: bp.sessionAgnosticData.column, condition: bp.condition, hitCondition: bp.hitCondition, logMessage: bp.logMessage })),
A
Andre Weinand 已提交
308 309 310
			sourceModified
		}).then(response => {
			if (response && response.body) {
I
isidor 已提交
311
				const data = new Map<string, DebugProtocol.Breakpoint>();
A
Andre Weinand 已提交
312
				for (let i = 0; i < breakpointsToSend.length; i++) {
I
isidor 已提交
313
					data.set(breakpointsToSend[i].getId(), response.body.breakpoints[i]);
A
Andre Weinand 已提交
314
				}
315

316
				this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
A
Andre Weinand 已提交
317 318 319 320
			}
		});
	}

321
	sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): Promise<void> {
322 323 324
		if (this.raw) {
			if (this.raw.readyForBreakpoints) {
				return this.raw.setFunctionBreakpoints({ breakpoints: fbpts }).then(response => {
A
Andre Weinand 已提交
325
					if (response && response.body) {
I
isidor 已提交
326
						const data = new Map<string, DebugProtocol.Breakpoint>();
A
Andre Weinand 已提交
327
						for (let i = 0; i < fbpts.length; i++) {
I
isidor 已提交
328
							data.set(fbpts[i].getId(), response.body.breakpoints[i]);
A
Andre Weinand 已提交
329
						}
330
						this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
A
Andre Weinand 已提交
331 332 333
					}
				});
			}
334

335
			return Promise.resolve(undefined);
A
Andre Weinand 已提交
336
		}
337

338
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
339 340
	}

341
	sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> {
342
		if (this.raw) {
I
isidor 已提交
343
			if (this.raw.readyForBreakpoints) {
344
				return this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }).then(() => undefined);
A
Andre Weinand 已提交
345
			}
R
Rob Lourens 已提交
346
			return Promise.resolve(undefined);
A
Andre Weinand 已提交
347
		}
348
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
349 350
	}

I
isidor 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
	dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> {
		if (this.raw) {
			if (this.raw.readyForBreakpoints) {
				return this.raw.dataBreakpointInfo({ name, variablesReference }).then(response => response.body);
			}
			return Promise.reject(new Error(nls.localize('sessionNotReadyForBreakpoints', "Session is not ready for breakpoints")));
		}
		return Promise.reject(new Error('no debug adapter'));
	}

	sendDataBreakpoints(dataBreakpoints: IDataBreakpoint[]): Promise<void> {
		if (this.raw) {
			if (this.raw.readyForBreakpoints) {
				return this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints }).then(response => {
					if (response && response.body) {
						const data = new Map<string, DebugProtocol.Breakpoint>();
						for (let i = 0; i < dataBreakpoints.length; i++) {
							data.set(dataBreakpoints[i].getId(), response.body.breakpoints[i]);
						}
370
						this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
I
isidor 已提交
371 372 373 374 375 376 377 378
					}
				});
			}
			return Promise.resolve(undefined);
		}
		return Promise.reject(new Error('no debug adapter'));
	}

I
isidor 已提交
379 380 381 382 383 384
	async breakpointsLocations(uri: URI, lineNumber: number): Promise<IPosition[]> {
		if (this.raw) {
			const source = this.getRawSource(uri);
			const response = await this.raw.breakpointLocations({ source, line: lineNumber });
			const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 }));

385
			return distinct(positions, p => `${p.lineNumber}:${p.column}`);
I
isidor 已提交
386 387 388 389
		}
		return Promise.reject(new Error('no debug adapter'));
	}

390
	customRequest(request: string, args: any): Promise<DebugProtocol.Response> {
391 392
		if (this.raw) {
			return this.raw.custom(request, args);
A
Andre Weinand 已提交
393
		}
394
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
395 396
	}

397
	stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse> {
398
		if (this.raw) {
I
isidor 已提交
399 400
			const token = this.getNewCancellationToken(threadId);
			return this.raw.stackTrace({ threadId, startFrame, levels }, token);
A
Andre Weinand 已提交
401
		}
402
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
403 404
	}

I
isidor 已提交
405
	exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined> {
406 407
		if (this.raw) {
			return this.raw.exceptionInfo({ threadId }).then(response => {
A
Andre Weinand 已提交
408 409 410 411 412 413 414 415
				if (response) {
					return {
						id: response.body.exceptionId,
						description: response.body.description,
						breakMode: response.body.breakMode,
						details: response.body.details
					};
				}
I
isidor 已提交
416
				return undefined;
A
Andre Weinand 已提交
417 418
			});
		}
419
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
420 421
	}

I
isidor 已提交
422
	scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse> {
423
		if (this.raw) {
I
isidor 已提交
424 425
			const token = this.getNewCancellationToken(threadId);
			return this.raw.scopes({ frameId }, token);
A
Andre Weinand 已提交
426
		}
427
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
428 429
	}

I
isidor 已提交
430
	variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse> {
431
		if (this.raw) {
I
isidor 已提交
432 433
			const token = threadId ? this.getNewCancellationToken(threadId) : undefined;
			return this.raw.variables({ variablesReference, filter, start, count }, token);
A
Andre Weinand 已提交
434
		}
I
isidor 已提交
435
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
436 437
	}

438
	evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> {
439 440
		if (this.raw) {
			return this.raw.evaluate({ expression, frameId, context });
A
Andre Weinand 已提交
441
		}
442
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
443 444
	}

445
	restartFrame(frameId: number, threadId: number): Promise<void> {
446
		if (this.raw) {
447
			return this.raw.restartFrame({ frameId }, threadId).then(() => undefined);
A
Andre Weinand 已提交
448
		}
449
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
450 451
	}

452
	next(threadId: number): Promise<void> {
453
		if (this.raw) {
454
			return this.raw.next({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
455
		}
456
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
457 458
	}

459
	stepIn(threadId: number): Promise<void> {
460
		if (this.raw) {
461
			return this.raw.stepIn({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
462
		}
463
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
464 465
	}

466
	stepOut(threadId: number): Promise<void> {
467
		if (this.raw) {
468
			return this.raw.stepOut({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
469
		}
470
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
471 472
	}

473
	stepBack(threadId: number): Promise<void> {
474
		if (this.raw) {
475
			return this.raw.stepBack({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
476
		}
477
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
478 479
	}

480
	continue(threadId: number): Promise<void> {
481
		if (this.raw) {
482
			return this.raw.continue({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
483
		}
484
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
485 486
	}

487
	reverseContinue(threadId: number): Promise<void> {
488
		if (this.raw) {
489
			return this.raw.reverseContinue({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
490
		}
491
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
492 493
	}

494
	pause(threadId: number): Promise<void> {
495
		if (this.raw) {
496
			return this.raw.pause({ threadId }).then(() => undefined);
A
Andre Weinand 已提交
497
		}
498
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
499 500
	}

501
	terminateThreads(threadIds?: number[]): Promise<void> {
502
		if (this.raw) {
503
			return this.raw.terminateThreads({ threadIds }).then(() => undefined);
A
Andre Weinand 已提交
504
		}
505
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
506 507
	}

508
	setVariable(variablesReference: number, name: string, value: string): Promise<DebugProtocol.SetVariableResponse> {
509 510
		if (this.raw) {
			return this.raw.setVariable({ variablesReference, name, value });
A
Andre Weinand 已提交
511
		}
512
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
513 514
	}

I
isidor 已提交
515 516 517 518 519 520 521 522 523 524 525 526 527 528
	gotoTargets(source: DebugProtocol.Source, line: number, column?: number): Promise<DebugProtocol.GotoTargetsResponse> {
		if (this.raw) {
			return this.raw.gotoTargets({ source, line, column });
		}
		return Promise.reject(new Error('no debug adapter'));
	}

	goto(threadId: number, targetId: number): Promise<DebugProtocol.GotoResponse> {
		if (this.raw) {
			return this.raw.goto({ threadId, targetId });
		}
		return Promise.reject(new Error('no debug adapter'));
	}

529
	loadSource(resource: URI): Promise<DebugProtocol.SourceResponse> {
A
Andre Weinand 已提交
530

531
		if (!this.raw) {
532
			return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
533 534 535 536 537 538 539 540
		}

		const source = this.getSourceForUri(resource);
		let rawSource: DebugProtocol.Source;
		if (source) {
			rawSource = source.raw;
		} else {
			// create a Source
541 542
			const data = Source.getEncodedDebugData(resource);
			rawSource = { path: data.path, sourceReference: data.sourceReference };
A
Andre Weinand 已提交
543 544
		}

I
isidor 已提交
545
		return this.raw.source({ sourceReference: rawSource.sourceReference || 0, source: rawSource });
A
Andre Weinand 已提交
546 547
	}

548
	getLoadedSources(): Promise<Source[]> {
549 550
		if (this.raw) {
			return this.raw.loadedSources({}).then(response => {
551 552 553 554 555
				if (response.body && response.body.sources) {
					return response.body.sources.map(src => this.getSource(src));
				} else {
					return [];
				}
A
Andre Weinand 已提交
556 557 558 559
			}, () => {
				return [];
			});
		}
560
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
561 562
	}

I
isidor 已提交
563
	completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]> {
564 565
		if (this.raw) {
			return this.raw.completions({
A
Andre Weinand 已提交
566 567 568
				frameId,
				text,
				column: position.column,
I
isidor 已提交
569 570
				line: position.lineNumber,
			}, token).then(response => {
A
Andre Weinand 已提交
571

572
				const result: CompletionItem[] = [];
A
Andre Weinand 已提交
573 574 575 576 577 578
				if (response && response.body && response.body.targets) {
					response.body.targets.forEach(item => {
						if (item && item.label) {
							result.push({
								label: item.label,
								insertText: item.text || item.label,
I
isidor 已提交
579 580
								kind: completionKindFromString(item.type || 'property'),
								filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined,
I
isidor 已提交
581 582
								range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position),
								sortText: item.sortText
A
Andre Weinand 已提交
583 584 585 586 587 588 589 590
							});
						}
					});
				}

				return result;
			});
		}
591
		return Promise.reject(new Error('no debug adapter'));
A
Andre Weinand 已提交
592 593 594 595
	}

	//---- threads

I
isidor 已提交
596
	getThread(threadId: number): Thread | undefined {
A
Andre Weinand 已提交
597 598 599 600 601 602 603 604 605
		return this.threads.get(threadId);
	}

	getAllThreads(): IThread[] {
		const result: IThread[] = [];
		this.threads.forEach(t => result.push(t));
		return result;
	}

606
	clearThreads(removeThreads: boolean, reference: number | undefined = undefined): void {
A
Andre Weinand 已提交
607
		if (reference !== undefined && reference !== null) {
608 609
			const thread = this.threads.get(reference);
			if (thread) {
A
Andre Weinand 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
				thread.clearCallStack();
				thread.stoppedDetails = undefined;
				thread.stopped = false;

				if (removeThreads) {
					this.threads.delete(reference);
				}
			}
		} else {
			this.threads.forEach(thread => {
				thread.clearCallStack();
				thread.stoppedDetails = undefined;
				thread.stopped = false;
			});

			if (removeThreads) {
				this.threads.clear();
				ExpressionContainer.allValues.clear();
			}
		}
	}

	rawUpdate(data: IRawModelUpdate): void {
633
		const threadIds: number[] = [];
I
isidor 已提交
634
		data.threads.forEach(thread => {
635
			threadIds.push(thread.id);
I
isidor 已提交
636 637 638 639 640 641 642 643 644
			if (!this.threads.has(thread.id)) {
				// A new thread came in, initialize it.
				this.threads.set(thread.id, new Thread(this, thread.name, thread.id));
			} else if (thread.name) {
				// Just the thread name got updated #18244
				const oldThread = this.threads.get(thread.id);
				if (oldThread) {
					oldThread.name = thread.name;
				}
I
isidor 已提交
645
			}
I
isidor 已提交
646
		});
647 648 649 650 651 652
		this.threads.forEach(t => {
			// Remove all old threads which are no longer part of the update #75980
			if (threadIds.indexOf(t.threadId) === -1) {
				this.threads.delete(t.threadId);
			}
		});
A
Andre Weinand 已提交
653

I
isidor 已提交
654 655
		const stoppedDetails = data.stoppedDetails;
		if (stoppedDetails) {
A
Andre Weinand 已提交
656 657
			// Set the availability of the threads' callstacks depending on
			// whether the thread is stopped or not
I
isidor 已提交
658
			if (stoppedDetails.allThreadsStopped) {
A
Andre Weinand 已提交
659
				this.threads.forEach(thread => {
I
isidor 已提交
660
					thread.stoppedDetails = thread.threadId === stoppedDetails.threadId ? stoppedDetails : { reason: undefined };
A
Andre Weinand 已提交
661 662 663
					thread.stopped = true;
					thread.clearCallStack();
				});
I
isidor 已提交
664
			} else {
I
isidor 已提交
665
				const thread = typeof stoppedDetails.threadId === 'number' ? this.threads.get(stoppedDetails.threadId) : undefined;
I
isidor 已提交
666 667
				if (thread) {
					// One thread is stopped, only update that thread.
I
isidor 已提交
668
					thread.stoppedDetails = stoppedDetails;
I
isidor 已提交
669 670 671
					thread.clearCallStack();
					thread.stopped = true;
				}
A
Andre Weinand 已提交
672 673 674 675
			}
		}
	}

676
	private fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise<void> {
I
isidor 已提交
677
		return this.raw ? this.raw.threads().then(response => {
A
Andre Weinand 已提交
678
			if (response && response.body && response.body.threads) {
I
isidor 已提交
679 680 681 682
				this.model.rawUpdate({
					sessionId: this.getId(),
					threads: response.body.threads,
					stoppedDetails
A
Andre Weinand 已提交
683 684
				});
			}
685
		}) : Promise.resolve(undefined);
A
Andre Weinand 已提交
686 687 688 689
	}

	//---- private

I
isidor 已提交
690
	private registerListeners(): void {
I
isidor 已提交
691 692 693 694
		if (!this.raw) {
			return;
		}

695
		this.rawListeners.push(this.raw.onDidInitialize(() => {
I
isidor 已提交
696 697
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
			const sendConfigurationDone = () => {
698
				if (this.raw && this.raw.capabilities.supportsConfigurationDoneRequest) {
R
Rob Lourens 已提交
699
					return this.raw.configurationDone().then(undefined, e => {
I
isidor 已提交
700
						// Disconnect the debug session on configuration done error #10596
701 702
						if (this.raw) {
							this.raw.disconnect();
I
isidor 已提交
703
						}
704
						if (e.command !== 'canceled' && e.message !== 'canceled') {
I
isidor 已提交
705
							this.notificationService.error(e);
706
						}
I
isidor 已提交
707 708
					});
				}
709 710

				return undefined;
I
isidor 已提交
711 712 713
			};

			// Send all breakpoints
714
			this.debugService.sendAllBreakpoints(this).then(sendConfigurationDone, sendConfigurationDone)
715
				.then(() => this.fetchThreads());
I
isidor 已提交
716 717
		}));

718
		this.rawListeners.push(this.raw.onDidStop(event => {
719
			this.fetchThreads(event.body).then(() => {
I
isidor 已提交
720
				const thread = typeof event.body.threadId === 'number' ? this.getThread(event.body.threadId) : undefined;
I
isidor 已提交
721 722 723
				if (thread) {
					// Call fetch call stack twice, the first only return the top stack frame.
					// Second retrieves the rest of the call stack. For performance reasons #25605
724 725
					const promises = this.model.fetchCallStack(<Thread>thread);
					const focus = () => {
I
isidor 已提交
726 727 728 729 730 731
						if (!event.body.preserveFocusHint && thread.getCallStack().length) {
							this.debugService.focusStackFrame(undefined, thread);
							if (thread.stoppedDetails) {
								if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') {
									this.viewletService.openViewlet(VIEWLET_ID);
								}
732

I
isidor 已提交
733
								if (this.configurationService.getValue<IDebugConfiguration>('debug').focusWindowOnBreak) {
734 735
									this.windowService.focusWindow();
								}
I
isidor 已提交
736 737
							}
						}
738 739 740 741 742 743 744 745
					};

					promises.topCallStack.then(focus);
					promises.wholeCallStack.then(() => {
						if (!this.debugService.getViewModel().focusedStackFrame) {
							// The top stack frame can be deemphesized so try to focus again #68616
							focus();
						}
I
isidor 已提交
746 747
					});
				}
748
			}).then(() => this._onDidChangeState.fire());
I
isidor 已提交
749 750
		}));

751
		this.rawListeners.push(this.raw.onDidThread(event => {
I
isidor 已提交
752 753 754 755
			if (event.body.reason === 'started') {
				// debounce to reduce threadsRequest frequency and improve performance
				if (!this.fetchThreadsScheduler) {
					this.fetchThreadsScheduler = new RunOnceScheduler(() => {
756
						this.fetchThreads();
I
isidor 已提交
757 758 759 760 761 762 763 764 765 766 767
					}, 100);
					this.rawListeners.push(this.fetchThreadsScheduler);
				}
				if (!this.fetchThreadsScheduler.isScheduled()) {
					this.fetchThreadsScheduler.schedule();
				}
			} else if (event.body.reason === 'exited') {
				this.model.clearThreads(this.getId(), true, event.body.threadId);
			}
		}));

768
		this.rawListeners.push(this.raw.onDidTerminateDebugee(event => {
I
isidor 已提交
769 770
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
			if (event.body && event.body.restart) {
R
Rob Lourens 已提交
771
				this.debugService.restartSession(this, event.body.restart).then(undefined, onUnexpectedError);
I
isidor 已提交
772
			} else if (this.raw) {
773
				this.raw.disconnect();
I
isidor 已提交
774 775 776
			}
		}));

777
		this.rawListeners.push(this.raw.onDidContinued(event => {
I
isidor 已提交
778
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
I
isidor 已提交
779 780 781 782 783 784 785 786 787 788
			if (threadId) {
				const tokens = this.cancellationMap.get(threadId);
				this.cancellationMap.delete(threadId);
				if (tokens) {
					tokens.forEach(t => t.cancel());
				}
			} else {
				this.cancelAllRequests();
			}

I
isidor 已提交
789
			this.model.clearThreads(this.getId(), false, threadId);
790
			this._onDidChangeState.fire();
I
isidor 已提交
791 792
		}));

793
		let outpuPromises: Promise<void>[] = [];
794
		this.rawListeners.push(this.raw.onDidOutput(event => {
I
isidor 已提交
795
			if (!event.body || !this.raw) {
I
isidor 已提交
796 797 798 799 800 801 802
				return;
			}

			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
			if (event.body.category === 'telemetry') {
				// only log telemetry events from debug adapter if the debug extension provided the telemetry key
				// and the user opted in telemetry
803
				if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
I
isidor 已提交
804
					// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
805
					this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
I
isidor 已提交
806 807 808 809 810 811
				}

				return;
			}

			// Make sure to append output in the correct order by properly waiting on preivous promises #33822
812
			const waitFor = outpuPromises.slice();
I
isidor 已提交
813
			const source = event.body.source && event.body.line ? {
I
isidor 已提交
814 815 816 817 818
				lineNumber: event.body.line,
				column: event.body.column ? event.body.column : 1,
				source: this.getSource(event.body.source)
			} : undefined;
			if (event.body.variablesReference) {
I
isidor 已提交
819
				const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
820
				outpuPromises.push(container.getChildren().then(children => {
I
isidor 已提交
821
					return Promise.all(waitFor).then(() => children.forEach(child => {
I
isidor 已提交
822
						// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
I
isidor 已提交
823
						(<any>child).name = null;
I
isidor 已提交
824
						this.appendToRepl(child, outputSeverity, source);
I
isidor 已提交
825 826 827
					}));
				}));
			} else if (typeof event.body.output === 'string') {
I
isidor 已提交
828
				Promise.all(waitFor).then(() => this.appendToRepl(event.body.output, outputSeverity, source));
I
isidor 已提交
829
			}
830
			Promise.all(outpuPromises).then(() => outpuPromises = []);
I
isidor 已提交
831 832
		}));

833
		this.rawListeners.push(this.raw.onDidBreakpoint(event => {
I
isidor 已提交
834
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
D
Dmitry Gozman 已提交
835 836
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.getIdFromAdapter(this.getId()) === id).pop();
			const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.getIdFromAdapter(this.getId()) === id).pop();
I
isidor 已提交
837

I
isidor 已提交
838
			if (event.body.reason === 'new' && event.body.breakpoint.source && event.body.breakpoint.line) {
I
isidor 已提交
839 840 841 842 843 844 845
				const source = this.getSource(event.body.breakpoint.source);
				const bps = this.model.addBreakpoints(source.uri, [{
					column: event.body.breakpoint.column,
					enabled: true,
					lineNumber: event.body.breakpoint.line,
				}], false);
				if (bps.length === 1) {
I
isidor 已提交
846
					const data = new Map<string, DebugProtocol.Breakpoint>([[bps[0].getId(), event.body.breakpoint]]);
847
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
I
isidor 已提交
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
				}
			}

			if (event.body.reason === 'removed') {
				if (breakpoint) {
					this.model.removeBreakpoints([breakpoint]);
				}
				if (functionBreakpoint) {
					this.model.removeFunctionBreakpoints(functionBreakpoint.getId());
				}
			}

			if (event.body.reason === 'changed') {
				if (breakpoint) {
					if (!breakpoint.column) {
						event.body.breakpoint.column = undefined;
					}
I
isidor 已提交
865
					const data = new Map<string, DebugProtocol.Breakpoint>([[breakpoint.getId(), event.body.breakpoint]]);
866
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
I
isidor 已提交
867 868
				}
				if (functionBreakpoint) {
I
isidor 已提交
869
					const data = new Map<string, DebugProtocol.Breakpoint>([[functionBreakpoint.getId(), event.body.breakpoint]]);
870
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
I
isidor 已提交
871 872 873 874
				}
			}
		}));

875
		this.rawListeners.push(this.raw.onDidLoadedSource(event => {
I
isidor 已提交
876 877 878 879 880 881
			this._onDidLoadedSource.fire({
				reason: event.body.reason,
				source: this.getSource(event.body.source)
			});
		}));

882
		this.rawListeners.push(this.raw.onDidCustomEvent(event => {
I
isidor 已提交
883 884 885
			this._onDidCustomEvent.fire(event);
		}));

886
		this.rawListeners.push(this.raw.onDidExitAdapter(event => {
I
isidor 已提交
887
			this.initialized = true;
888
			this.model.setBreakpointSessionData(this.getId(), this.capabilities, undefined);
A
Andre Weinand 已提交
889
			this._onDidEndAdapter.fire(event);
890 891 892
		}));
	}

893
	shutdown(): void {
894
		dispose(this.rawListeners);
895 896
		if (this.raw) {
			this.raw.disconnect();
I
isidor 已提交
897
			this.raw.dispose();
898
		}
899
		this.raw = undefined;
900 901
		this.model.clearThreads(this.getId(), true);
		this._onDidChangeState.fire();
902 903 904 905
	}

	//---- sources

I
isidor 已提交
906
	getSourceForUri(uri: URI): Source | undefined {
I
isidor 已提交
907
		return this.sources.get(this.getUriKey(uri));
908 909
	}

I
isidor 已提交
910
	getSource(raw?: DebugProtocol.Source): Source {
911
		let source = new Source(raw, this.getId());
I
isidor 已提交
912
		const uriKey = this.getUriKey(source.uri);
A
Andre Weinand 已提交
913 914 915 916
		const found = this.sources.get(uriKey);
		if (found) {
			source = found;
			// merge attributes of new into existing
917 918 919 920 921 922
			source.raw = mixin(source.raw, raw);
			if (source.raw && raw) {
				// Always take the latest presentation hint from adapter #42139
				source.raw.presentationHint = raw.presentationHint;
			}
		} else {
I
isidor 已提交
923
			this.sources.set(uriKey, source);
924 925 926 927
		}

		return source;
	}
I
isidor 已提交
928

I
isidor 已提交
929 930 931 932 933 934 935 936 937 938
	private getRawSource(uri: URI): DebugProtocol.Source {
		const source = this.getSourceForUri(uri);
		if (source) {
			return source.raw;
		} else {
			const data = Source.getEncodedDebugData(uri);
			return { name: data.name, path: data.path, sourceReference: data.sourceReference };
		}
	}

I
isidor 已提交
939 940 941 942 943 944 945 946 947 948 949 950 951 952
	private getNewCancellationToken(threadId: number): CancellationToken {
		const tokenSource = new CancellationTokenSource();
		const tokens = this.cancellationMap.get(threadId) || [];
		tokens.push(tokenSource);
		this.cancellationMap.set(threadId, tokens);

		return tokenSource.token;
	}

	private cancelAllRequests(): void {
		this.cancellationMap.forEach(tokens => tokens.forEach(t => t.cancel()));
		this.cancellationMap.clear();
	}

I
isidor 已提交
953
	private getUriKey(uri: URI): string {
A
Andre Weinand 已提交
954
		// TODO: the following code does not make sense if uri originates from a different platform
I
isidor 已提交
955 956
		return platform.isLinux ? uri.toString() : uri.toString().toLowerCase();
	}
I
isidor 已提交
957 958 959

	// REPL

I
isidor 已提交
960
	getReplElements(): IReplElement[] {
961 962 963
		return this.repl.getReplElements();
	}

964 965 966 967
	hasSeparateRepl(): boolean {
		return !this.parentSession || this._options.repl !== 'mergeWithParent';
	}

968 969 970
	removeReplExpressions(): void {
		this.repl.removeReplExpressions();
		this._onDidChangeREPLElements.fire();
I
isidor 已提交
971 972
	}

973
	async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
974
		const expressionEvaluated = this.repl.addReplExpression(this, stackFrame, name);
975 976
		this._onDidChangeREPLElements.fire();
		await expressionEvaluated;
977 978 979
		this._onDidChangeREPLElements.fire();
		// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
		variableSetEmitter.fire();
I
isidor 已提交
980 981
	}

982
	appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {
D
Dmitry Gozman 已提交
983
		this.repl.appendToRepl(this, data, severity, source);
I
isidor 已提交
984 985 986
		this._onDidChangeREPLElements.fire();
	}

987
	logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) {
988
		this.repl.logToRepl(this, sev, args, frame);
989
		this._onDidChangeREPLElements.fire();
I
isidor 已提交
990
	}
991
}