debugService.ts 56.7 KB
Newer Older
E
Erich Gamma 已提交
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 7
import * as nls from 'vs/nls';
import * as lifecycle from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
8
import Event, { Emitter } from 'vs/base/common/event';
I
isidor 已提交
9
import * as resources from 'vs/base/common/resources';
I
isidor 已提交
10
import * as strings from 'vs/base/common/strings';
I
isidor 已提交
11
import { generateUuid } from 'vs/base/common/uuid';
E
Erich Gamma 已提交
12
import uri from 'vs/base/common/uri';
I
isidor 已提交
13
import * as platform from 'vs/base/common/platform';
I
isidor 已提交
14 15 16
import { first, distinct } from 'vs/base/common/arrays';
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
E
Erich Gamma 已提交
17
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
18
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
19
import * as aria from 'vs/base/browser/ui/aria/aria';
20
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
J
Johannes Rieken 已提交
21 22 23
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
24
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
J
Johannes Rieken 已提交
25
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
26
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
I
isidor 已提交
27
import { IWindowService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
28 29 30 31
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
32
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
33
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
34
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
35 36
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
37
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
38
import Constants from 'vs/workbench/parts/markers/common/constants';
39
import { ITaskService, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
40
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
41
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
B
Benjamin Pasero 已提交
42
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
43
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
44
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
45 46
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
47
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
48
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
49
import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
50
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
51
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
52
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
53
import { TaskEvent, TaskEventKind } from 'vs/workbench/parts/tasks/common/tasks';
54
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
I
isidor 已提交
55 56
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IAction, Action } from 'vs/base/common/actions';
57
import { normalizeDriveLetter } from 'vs/base/common/labels';
58
import { RunOnceScheduler } from 'vs/base/common/async';
E
Erich Gamma 已提交
59

I
isidor 已提交
60 61 62 63 64
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
E
Erich Gamma 已提交
65

66
export class DebugService implements debug.IDebugService {
67
	public _serviceBrand: any;
E
Erich Gamma 已提交
68

I
isidor 已提交
69
	private sessionStates: Map<string, debug.State>;
70
	private _onDidChangeState: Emitter<debug.State>;
71
	private _onDidNewProcess: Emitter<debug.IProcess>;
I
isidor 已提交
72
	private _onDidEndProcess: Emitter<debug.IProcess>;
73
	private _onDidCustomEvent: Emitter<debug.DebugEvent>;
I
isidor 已提交
74 75
	private model: Model;
	private viewModel: ViewModel;
76
	private allProcesses: Map<string, debug.IProcess>;
77 78
	private configurationManager: ConfigurationManager;
	private toDispose: lifecycle.IDisposable[];
I
isidor 已提交
79
	private toDisposeOnSessionEnd: Map<string, lifecycle.IDisposable[]>;
A
Alex Dima 已提交
80
	private inDebugMode: IContextKey<boolean>;
I
isidor 已提交
81
	private debugType: IContextKey<string>;
I
isidor 已提交
82
	private debugState: IContextKey<string>;
I
isidor 已提交
83
	private breakpointsToSendOnResourceSaved: Set<string>;
84
	private launchJsonChanged: boolean;
I
isidor 已提交
85
	private firstSessionStart: boolean;
86
	private previousState: debug.State;
87
	private fetchThreadsSchedulers: Map<string, RunOnceScheduler>;
E
Erich Gamma 已提交
88 89 90 91

	constructor(
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
92 93
		@ITextFileService private textFileService: ITextFileService,
		@IViewletService private viewletService: IViewletService,
I
isidor 已提交
94
		@IPanelService private panelService: IPanelService,
95
		@INotificationService private notificationService: INotificationService,
96
		@IDialogService private dialogService: IDialogService,
E
Erich Gamma 已提交
97
		@IPartService private partService: IPartService,
98 99
		@IWindowService private windowService: IWindowService,
		@IBroadcastService private broadcastService: IBroadcastService,
E
Erich Gamma 已提交
100 101
		@ITelemetryService private telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
102
		@IContextKeyService contextKeyService: IContextKeyService,
103
		@ILifecycleService private lifecycleService: ILifecycleService,
I
isidor 已提交
104
		@IInstantiationService private instantiationService: IInstantiationService,
A
Alex Dima 已提交
105
		@IExtensionService private extensionService: IExtensionService,
106
		@IMarkerService private markerService: IMarkerService,
107
		@ITaskService private taskService: ITaskService,
108
		@IFileService private fileService: IFileService,
I
isidor 已提交
109
		@IConfigurationService private configurationService: IConfigurationService
E
Erich Gamma 已提交
110 111
	) {
		this.toDispose = [];
I
isidor 已提交
112 113
		this.toDisposeOnSessionEnd = new Map<string, lifecycle.IDisposable[]>();
		this.breakpointsToSendOnResourceSaved = new Set<string>();
114
		this._onDidChangeState = new Emitter<debug.State>();
115
		this._onDidNewProcess = new Emitter<debug.IProcess>();
I
isidor 已提交
116
		this._onDidEndProcess = new Emitter<debug.IProcess>();
117
		this._onDidCustomEvent = new Emitter<debug.DebugEvent>();
I
isidor 已提交
118
		this.sessionStates = new Map<string, debug.State>();
119
		this.allProcesses = new Map<string, debug.IProcess>();
120
		this.fetchThreadsSchedulers = new Map<string, RunOnceScheduler>();
E
Erich Gamma 已提交
121

122
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
123
		this.toDispose.push(this.configurationManager);
124
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
I
isidor 已提交
125
		this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
I
isidor 已提交
126
		this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
E
Erich Gamma 已提交
127

I
isidor 已提交
128
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
129
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
130
		this.toDispose.push(this.model);
131
		this.viewModel = new ViewModel(contextKeyService);
I
isidor 已提交
132
		this.firstSessionStart = true;
133

134
		this.registerListeners();
E
Erich Gamma 已提交
135 136
	}

137
	private registerListeners(): void {
138
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
139 140
		this.lifecycleService.onShutdown(this.store, this);
		this.lifecycleService.onShutdown(this.dispose, this);
141
		this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this));
142 143 144
	}

	private onBroadcast(broadcast: IBroadcast): void {
145

I
isidor 已提交
146
		// attach: PH is ready to be attached to
147 148
		const process = this.allProcesses.get(broadcast.payload.debugId);
		if (!process) {
149 150 151
			// Ignore attach events for sessions that never existed (wrong vscode windows)
			return;
		}
152
		const session = <RawDebugSession>process.session;
153

154
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
155
			this.onSessionEnd(session);
I
isidor 已提交
156

157 158
			process.configuration.request = 'attach';
			process.configuration.port = broadcast.payload.port;
159
			this.doCreateProcess(process.session.root, process.configuration, process.getId());
160 161
			return;
		}
162

163
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
164
			this.onSessionEnd(session);
165 166 167
			return;
		}

B
Benjamin Pasero 已提交
168
		// an extension logged output, show it inside the REPL
169
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
170
			let extensionOutput: IRemoteConsoleLog = broadcast.payload.logEntry;
171 172
			let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
			const { args, stack } = parse(extensionOutput);
			let source: debug.IReplElementSource;
			if (stack) {
				const frame = getFirstFrame(stack);
				if (frame) {
					source = {
						column: frame.column,
						lineNumber: frame.line,
						source: process.getSource({
							name: resources.basenameOrAuthority(frame.uri),
							path: frame.uri.fsPath
						})
					};
				}
			}
188

I
isidor 已提交
189
			// add output for each argument logged
190 191 192 193
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

I
isidor 已提交
194
				// undefined gets printed as 'undefined'
195 196 197 198
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

I
isidor 已提交
199
				// null gets printed as 'null'
200 201 202 203
				else if (a === null) {
					simpleVals.push('null');
				}

I
isidor 已提交
204
				// objects & arrays are special because we want to inspect them in the REPL
I
isidor 已提交
205
				else if (isObject(a) || Array.isArray(a)) {
206

I
isidor 已提交
207
					// flush any existing simple values logged
208
					if (simpleVals.length) {
209
						this.logToRepl(simpleVals.join(' '), sev, source);
210 211 212
						simpleVals = [];
					}

I
isidor 已提交
213
					// show object
I
isidor 已提交
214
					this.logToRepl(new RawObjectReplElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source);
215 216
				}

I
isidor 已提交
217 218
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
219 220 221 222 223 224
				else if (typeof a === 'string') {
					let buf = '';

					for (let j = 0, len = a.length; j < len; j++) {
						if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd')) {
							i++; // read over substitution
I
isidor 已提交
225
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

				// number or boolean is joined together
				else {
					simpleVals.push(a);
				}
			}

I
isidor 已提交
241
			// flush simple values
J
jens1o 已提交
242
			// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
243
			if (simpleVals.length) {
244
				this.logToRepl(simpleVals.join(' ') + '\n', sev, source);
245 246
			}
		}
E
Erich Gamma 已提交
247 248
	}

249
	private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
I
isidor 已提交
250
		const callStack = thread.getCallStack();
I
isidor 已提交
251
		if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) {
I
isidor 已提交
252 253 254
			return TPromise.as(null);
		}

255
		// focus first stack frame from top that has source location if no other stack frame is focused
I
isidor 已提交
256 257 258 259 260
		const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, undefined);
		if (!stackFrameToFocus) {
			return TPromise.as(null);
		}

261
		this.focusStackFrame(stackFrameToFocus);
I
isidor 已提交
262
		if (thread.stoppedDetails) {
263
			this.windowService.focusWindow();
I
isidor 已提交
264 265
			aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
		}
I
isidor 已提交
266

I
isidor 已提交
267
		return stackFrameToFocus.openInEditor(this.editorService, true);
I
isidor 已提交
268 269
	}

I
isidor 已提交
270
	private registerSessionListeners(process: Process, session: RawDebugSession): void {
271

I
isidor 已提交
272
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
273

I
isidor 已提交
274
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => {
275
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
276
			const sendConfigurationDone = () => {
277
				if (session && session.capabilities.supportsConfigurationDoneRequest) {
278
					return session.configurationDone().done(null, e => {
279
						// Disconnect the debug session on configuration done error #10596
280 281
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
282
						}
283
						this.notificationService.error(e.message);
284
					});
I
isidor 已提交
285
				}
286
			};
287 288 289

			this.sendAllBreakpoints(process).then(sendConfigurationDone, sendConfigurationDone)
				.done(() => this.fetchThreads(session), errors.onUnexpectedError);
I
isidor 已提交
290
		}));
E
Erich Gamma 已提交
291

I
isidor 已提交
292
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
293
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
294 295
			this.fetchThreads(session, event.body).done(() => {
				const thread = process && process.getThread(event.body.threadId);
I
isidor 已提交
296
				if (thread) {
297 298 299
					// 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
					this.model.fetchCallStack(thread).then(() => {
300
						return this.tryToAutoFocusStackFrame(thread);
I
isidor 已提交
301 302
					});
				}
E
Erich Gamma 已提交
303 304 305
			}, errors.onUnexpectedError);
		}));

I
isidor 已提交
306
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => {
E
Erich Gamma 已提交
307
			if (event.body.reason === 'started') {
308 309 310 311 312 313 314 315 316 317 318 319
				// debounce to reduce threadsRequest frequency and improve performance
				let scheduler = this.fetchThreadsSchedulers.get(session.getId());
				if (!scheduler) {
					scheduler = new RunOnceScheduler(() => {
						this.fetchThreads(session).done(undefined, errors.onUnexpectedError);
					}, 100);
					this.fetchThreadsSchedulers.set(session.getId(), scheduler);
					this.toDisposeOnSessionEnd.get(session.getId()).push(scheduler);
				}
				if (!scheduler.isScheduled()) {
					scheduler.schedule();
				}
E
Erich Gamma 已提交
320
			} else if (event.body.reason === 'exited') {
321
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
322 323 324
			}
		}));

I
isidor 已提交
325
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => {
326
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
327
			if (session && session.getId() === event.sessionId) {
328
				if (event.body && event.body.restart && process) {
329
					this.restartProcess(process, event.body.restart).done(null, err => this.notificationService.error(err.message));
330
				} else {
331
					session.disconnect().done(null, errors.onUnexpectedError);
332
				}
E
Erich Gamma 已提交
333 334 335
			}
		}));

I
isidor 已提交
336
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => {
I
isidor 已提交
337
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
338 339
			this.model.clearThreads(session.getId(), false, threadId);
			if (this.viewModel.focusedProcess.getId() === session.getId()) {
I
isidor 已提交
340
				this.focusStackFrame(undefined, this.viewModel.focusedThread, this.viewModel.focusedProcess);
341
			}
I
isidor 已提交
342
			this.updateStateAndEmit(session.getId(), debug.State.Running);
I
isidor 已提交
343 344
		}));

345
		let outputPromises: TPromise<void>[] = [];
I
isidor 已提交
346
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
347 348 349 350
			if (!event.body) {
				return;
			}

351
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
352
			if (event.body.category === 'telemetry') {
353
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
354
				// and the user opted in telemetry
355
				if (session.customTelemetryService && this.telemetryService.isOptedIn) {
K
kieferrm 已提交
356
					// __GDPR__TODO__ We're sending events in the name of the debug adapter and we can not ensure that those are declared correctly.
357
					session.customTelemetryService.publicLog(event.body.output, event.body.data);
358
				}
I
isidor 已提交
359 360 361 362

				return;
			}

363 364
			// Make sure to append output in the correct order by properly waiting on preivous promises #33822
			const waitFor = outputPromises.slice();
I
isidor 已提交
365 366 367 368 369
			const source = event.body.source ? {
				lineNumber: event.body.line,
				column: event.body.column,
				source: process.getSource(event.body.source)
			} : undefined;
I
isidor 已提交
370
			if (event.body.variablesReference) {
371
				const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid());
372 373
				outputPromises.push(container.getChildren().then(children => {
					return TPromise.join(waitFor).then(() => children.forEach(child => {
374 375
						// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
						child.name = null;
I
isidor 已提交
376
						this.logToRepl(child, outputSeverity, source);
377 378
					}));
				}));
379
			} else if (typeof event.body.output === 'string') {
380
				TPromise.join(waitFor).then(() => this.logToRepl(event.body.output, outputSeverity, source));
E
Erich Gamma 已提交
381
			}
382
			TPromise.join(outputPromises).then(() => outputPromises = []);
E
Erich Gamma 已提交
383 384
		}));

I
isidor 已提交
385
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
386
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
387
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
388 389 390
			const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();

			if (event.body.reason === 'new' && event.body.breakpoint.source) {
I
isidor 已提交
391
				const source = process.getSource(event.body.breakpoint.source);
I
isidor 已提交
392
				const bps = this.model.addBreakpoints(source.uri, [{
I
isidor 已提交
393 394
					column: event.body.breakpoint.column,
					enabled: true,
I
isidor 已提交
395 396 397 398 399
					lineNumber: event.body.breakpoint.line,
				}], false);
				if (bps.length === 1) {
					this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint });
				}
I
isidor 已提交
400 401 402 403 404 405 406 407 408 409 410
			}

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

I
isidor 已提交
411
			if (event.body.reason === 'changed') {
I
isidor 已提交
412 413 414 415 416
				if (breakpoint) {
					if (!breakpoint.column) {
						event.body.breakpoint.column = undefined;
					}
					this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
I
isidor 已提交
417
				}
I
isidor 已提交
418 419 420 421 422 423
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
424
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
425
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
426
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
427
				process && process.session.root && process.configuration.noDebug) {
428 429
				this.broadcastService.broadcast({
					channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
I
isidor 已提交
430
					payload: [process.session.root.uri.fsPath]
431
				});
432
			}
433
			if (session && session.getId() === event.sessionId) {
434
				this.onSessionEnd(session);
435
			}
E
Erich Gamma 已提交
436
		}));
437 438 439 440

		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidCustomEvent(event => {
			this._onDidCustomEvent.fire(event);
		}));
E
Erich Gamma 已提交
441 442
	}

I
isidor 已提交
443
	private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> {
444 445
		return session.threads().then(response => {
			if (response && response.body && response.body.threads) {
446
				response.body.threads.forEach(thread => {
447 448 449
					this.model.rawUpdate({
						sessionId: session.getId(),
						threadId: thread.id,
I
isidor 已提交
450
						thread,
451 452 453
						stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined
					});
				});
454 455
			}
		});
E
Erich Gamma 已提交
456 457
	}

458 459
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
460
		try {
461
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
462
				return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.adapterData);
E
Erich Gamma 已提交
463
			});
464 465 466
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
467 468
	}

469 470
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
471
		try {
472
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
473
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
474
			});
475 476 477
		} catch (e) { }

		return result || [];
I
isidor 已提交
478 479
	}

480 481
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
482 483
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
484
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
485
			});
486
		} catch (e) { }
E
Erich Gamma 已提交
487

488
		return result || [];
E
Erich Gamma 已提交
489 490
	}

I
isidor 已提交
491 492
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
493
		try {
J
Johannes Rieken 已提交
494
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
495
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
496
			});
497 498 499
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
500 501
	}

I
isidor 已提交
502
	public get state(): debug.State {
503 504 505 506
		const focusedThread = this.viewModel.focusedThread;
		if (focusedThread && focusedThread.stopped) {
			return debug.State.Stopped;
		}
I
isidor 已提交
507
		const focusedProcess = this.viewModel.focusedProcess;
I
isidor 已提交
508
		if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) {
I
isidor 已提交
509
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
510
		}
I
isidor 已提交
511 512
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
513 514 515
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
516 517
	}

518
	public get onDidChangeState(): Event<debug.State> {
519 520 521
		return this._onDidChangeState.event;
	}

522 523 524 525
	public get onDidNewProcess(): Event<debug.IProcess> {
		return this._onDidNewProcess.event;
	}

I
isidor 已提交
526 527 528 529
	public get onDidEndProcess(): Event<debug.IProcess> {
		return this._onDidEndProcess.event;
	}

530
	public get onDidCustomEvent(): Event<debug.DebugEvent> {
531 532 533
		return this._onDidCustomEvent.event;
	}

I
isidor 已提交
534 535 536 537 538 539 540
	private updateStateAndEmit(sessionId?: string, newState?: debug.State): void {
		if (sessionId) {
			if (newState === debug.State.Inactive) {
				this.sessionStates.delete(sessionId);
			} else {
				this.sessionStates.set(sessionId, newState);
			}
I
isidor 已提交
541
		}
I
isidor 已提交
542 543

		const state = this.state;
544
		if (this.previousState !== state) {
545 546 547 548
			const stateLabel = debug.State[state];
			if (stateLabel) {
				this.debugState.set(stateLabel.toLowerCase());
			}
549
			this.previousState = state;
550
			this._onDidChangeState.fire(state);
I
isidor 已提交
551
		}
E
Erich Gamma 已提交
552 553
	}

I
isidor 已提交
554
	public focusStackFrame(stackFrame: debug.IStackFrame, thread?: debug.IThread, process?: debug.IProcess, explicit?: boolean): void {
I
isidor 已提交
555
		if (!process) {
I
isidor 已提交
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
			if (stackFrame || thread) {
				process = stackFrame ? stackFrame.thread.process : thread.process;
			} else {
				const processes = this.model.getProcesses();
				process = processes.length ? processes[0] : undefined;
			}
		}

		if (!thread) {
			if (stackFrame) {
				thread = stackFrame.thread;
			} else {
				const threads = process ? process.getAllThreads() : undefined;
				thread = threads && threads.length ? threads[0] : undefined;
			}
571
		}
I
isidor 已提交
572

573
		if (!stackFrame) {
I
isidor 已提交
574 575 576 577
			if (thread) {
				const callStack = thread.getCallStack();
				stackFrame = callStack && callStack.length ? callStack[0] : null;
			}
I
isidor 已提交
578
		}
I
isidor 已提交
579

I
isidor 已提交
580
		this.viewModel.setFocus(stackFrame, thread, process, explicit);
I
isidor 已提交
581
		this.updateStateAndEmit();
E
Erich Gamma 已提交
582 583
	}

I
isidor 已提交
584
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
585 586
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
587
			if (breakpoint instanceof Breakpoint) {
588
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
589
			} else if (breakpoint instanceof FunctionBreakpoint) {
590 591
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
592

593
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
594 595
		}

596 597
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
598 599
	}

I
isidor 已提交
600
	public addBreakpoints(uri: uri, rawBreakpoints: debug.IBreakpointData[]): TPromise<void> {
601
		this.model.addBreakpoints(uri, rawBreakpoints);
602
		rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath)));
603

604
		return this.sendBreakpoints(uri);
605 606
	}

607
	public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }, sendOnResourceSaved: boolean): void {
608
		this.model.updateBreakpoints(data);
609 610 611 612 613
		if (sendOnResourceSaved) {
			this.breakpointsToSendOnResourceSaved.add(uri.toString());
		} else {
			this.sendBreakpoints(uri);
		}
614 615
	}

616
	public removeBreakpoints(id?: string): TPromise<any> {
617
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
618 619
		toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath)));
		const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri);
E
Erich Gamma 已提交
620

621
		this.model.removeBreakpoints(toRemove);
622

I
isidor 已提交
623
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
624 625
	}

626 627
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
628 629 630
		return this.sendAllBreakpoints();
	}

631 632
	public addFunctionBreakpoint(name?: string, id?: string): void {
		const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id);
I
isidor 已提交
633
		this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint);
634 635
	}

I
isidor 已提交
636
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
637
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
638
		return this.sendFunctionBreakpoints();
I
isidor 已提交
639 640
	}

I
isidor 已提交
641
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
642
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
643
		return this.sendFunctionBreakpoints();
I
isidor 已提交
644 645
	}

I
isidor 已提交
646
	public addReplExpression(name: string): TPromise<void> {
647
		return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
648
			// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
I
isidor 已提交
649
			.then(() => this.focusStackFrame(this.viewModel.focusedStackFrame, this.viewModel.focusedThread, this.viewModel.focusedProcess));
E
Erich Gamma 已提交
650 651
	}

652 653
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
654 655
	}

I
isidor 已提交
656
	public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void {
657 658 659 660
		if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) {
			// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
			this.model.removeReplExpressions();
		} else {
I
isidor 已提交
661
			this.model.appendToRepl(value, sev, source);
662
		}
663 664
	}

665
	public addWatchExpression(name: string): void {
666 667
		const we = this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
		this.viewModel.setSelectedExpression(we);
E
Erich Gamma 已提交
668 669
	}

670
	public renameWatchExpression(id: string, newName: string): void {
671
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
672 673
	}

I
isidor 已提交
674 675 676 677
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

678 679
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
680 681
	}

682
	public startDebugging(launch: debug.ILaunch, configOrName?: debug.IConfig | string, noDebug = false): TPromise<any> {
683

684
		// make sure to save all files and that the configuration is up to date
I
isidor 已提交
685
		return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() =>
686
			this.extensionService.whenInstalledExtensionsRegistered().then(() => {
687 688
				if (this.model.getProcesses().length === 0) {
					this.removeReplExpressions();
689
					this.allProcesses.clear();
690
					this.model.getBreakpoints().forEach(bp => bp.verified = false);
691
				}
692
				this.launchJsonChanged = false;
693

694
				let config: debug.IConfig, compound: debug.ICompound;
695
				if (!configOrName) {
696
					configOrName = this.configurationManager.selectedConfiguration.name;
697
				}
698 699 700
				if (typeof configOrName === 'string' && launch) {
					config = launch.getConfiguration(configOrName);
					compound = launch.getCompound(configOrName);
701
				} else if (typeof configOrName !== 'string') {
702 703 704
					config = configOrName;
				}

705 706 707 708 709
				if (compound) {
					if (!compound.configurations) {
						return TPromise.wrapError(new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] },
							"Compound must have \"configurations\" attribute set in order to start multiple configurations.")));
					}
710

711 712
					return TPromise.join(compound.configurations.map(configData => {
						const name = typeof configData === 'string' ? configData : configData.name;
I
isidor 已提交
713 714 715 716
						if (name === compound.name) {
							return TPromise.as(null);
						}

717
						let launchForName: debug.ILaunch;
718 719 720
						if (typeof configData === 'string') {
							const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
							if (launchesContainingName.length === 1) {
721
								launchForName = launchesContainingName[0];
722 723
							} else if (launchesContainingName.length > 1 && launchesContainingName.indexOf(launch) >= 0) {
								// If there are multiple launches containing the configuration give priority to the configuration in the current launch
724
								launchForName = launch;
725 726
							} else {
								return TPromise.wrapError(new Error(launchesContainingName.length === 0 ? nls.localize('noConfigurationNameInWorkspace', "Could not find launch configuration '{0}' in the workspace.", name)
727
									: nls.localize('multipleConfigurationNamesInWorkspace', "There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name)));
728 729
							}
						} else if (configData.folder) {
730 731 732
							const launchesMatchingConfigData = this.configurationManager.getLaunches().filter(l => l.workspace && l.workspace.name === configData.folder && !!l.getConfiguration(configData.name));
							if (launchesMatchingConfigData.length === 1) {
								launchForName = launchesMatchingConfigData[0];
733 734 735
							} else {
								return TPromise.wrapError(new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound.name)));
							}
I
isidor 已提交
736 737
						}

738
						return this.startDebugging(launchForName, name, noDebug);
I
isidor 已提交
739
					}));
740
				}
741
				if (configOrName && !config) {
I
isidor 已提交
742
					const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName) :
I
isidor 已提交
743 744
						nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist.");
					return TPromise.wrapError(new Error(message));
745 746
				}

I
isidor 已提交
747 748 749 750 751 752 753 754 755 756 757 758
				// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
				// Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config.
				let type: string;
				if (config) {
					type = config.type;
				} else {
					// a no-folder workspace has no launch.config
					config = <debug.IConfig>{};
				}
				if (noDebug) {
					config.noDebug = true;
				}
759

I
isidor 已提交
760 761
				const sessionId = generateUuid();
				this.updateStateAndEmit(sessionId, debug.State.Initializing);
762 763 764 765 766 767
				const wrapUpState = () => {
					if (this.sessionStates.get(sessionId) === debug.State.Initializing) {
						this.updateStateAndEmit(sessionId, debug.State.Inactive);
					}
				};

I
isidor 已提交
768
				return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() =>
I
isidor 已提交
769
					(type ? this.extensionService.activateByEvent(`onDebugResolve:${type}`) : TPromise.as(null)).then(() =>
I
isidor 已提交
770
						this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config).then(config => {
I
isidor 已提交
771 772
							// a falsy config indicates an aborted launch
							if (config && config.type) {
773
								return this.createProcess(launch, config, sessionId);
I
isidor 已提交
774 775
							}

I
isidor 已提交
776 777 778
							if (launch) {
								return launch.openConfigFile(false, type).done(undefined, errors.onUnexpectedError);
							}
I
isidor 已提交
779 780 781 782 783
						})
					).then(() => wrapUpState(), err => {
						wrapUpState();
						return <any>TPromise.wrapError(err);
					}));
784
			})
B
Benjamin Pasero 已提交
785
		)));
786
	}
787

788
	private createProcess(launch: debug.ILaunch, config: debug.IConfig, sessionId: string): TPromise<void> {
I
isidor 已提交
789
		return this.textFileService.saveAll().then(() =>
I
isidor 已提交
790
			(launch ? launch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
791 792 793 794
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
795

796 797 798
				if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
					let message: string;
					if (config.request !== 'attach' && config.request !== 'launch') {
799
						message = config.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
A
Andre Weinand 已提交
800
							: nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request');
801 802 803

					} else {
						message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
804
							nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
805 806
					}

I
isidor 已提交
807
					return this.showError(message);
808
				}
J
Johannes Rieken 已提交
809

I
isidor 已提交
810
				this.toDisposeOnSessionEnd.set(sessionId, []);
811

812
				const workspace = launch ? launch.workspace : undefined;
I
isidor 已提交
813 814 815 816
				const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => {
					return this.doCreateProcess(workspace, resolvedConfig, sessionId);
				});

817
				return this.runPreLaunchTask(sessionId, workspace, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
818 819 820 821
					const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
					const successExitCode = taskSummary && taskSummary.exitCode === 0;
					const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
					if (successExitCode || (errorCount === 0 && !failureExitCode)) {
822
						return this.doCreateProcess(workspace, resolvedConfig, sessionId);
823
					}
I
isidor 已提交
824

825 826 827 828
					const message = errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
						errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
							nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode);

I
isidor 已提交
829 830
					const showErrorsAction = new Action('debug.showErrors', nls.localize('showErrors', "Show Errors"), undefined, true, () => {
						return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => undefined);
831
					});
I
isidor 已提交
832 833

					return this.showError(message, [debugAnywayAction, showErrorsAction]);
834
				}, (err: TaskError) => {
I
isidor 已提交
835
					return this.showError(err.message, [debugAnywayAction, this.taskService.configureAction()]);
836 837
				});
			}, err => {
838
				if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
I
isidor 已提交
839
					return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
840
				}
E
Erich Gamma 已提交
841

I
isidor 已提交
842
				return launch && launch.openConfigFile(false).then(editor => void 0);
843
			})
I
isidor 已提交
844
		);
845
	}
846

847
	private doCreateProcess(root: IWorkspaceFolder, configuration: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
848
		configuration.__sessionId = sessionId;
849
		this.inDebugMode.set(true);
850

J
Joao Moreno 已提交
851
		return this.telemetryService.getTelemetryInfo().then(info => {
852
			const telemetryInfo: { [key: string]: string } = Object.create(null);
853 854
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
855 856
			return telemetryInfo;
		}).then(data => {
857 858 859
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
860
			let client: TelemetryClient;
861

862
			let customTelemetryService: TelemetryService;
863
			if (aiKey) {
864
				client = new TelemetryClient(
865 866 867 868
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
869
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
870
						env: {
B
Benjamin Pasero 已提交
871
							ELECTRON_RUN_AS_NODE: 1,
872 873 874
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
875
					}
876
				);
877

878 879
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
880

881
				customTelemetryService = new TelemetryService({ appender }, this.configurationService);
882 883
			}

884
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, customTelemetryService, root);
885
			const process = this.model.addProcess(configuration, session);
886
			this.allProcesses.set(process.getId(), process);
887

888
			if (client) {
I
isidor 已提交
889
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
890
			}
I
isidor 已提交
891
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
892

893
			return session.initialize({
894
				clientID: 'vscode',
J
Joao Moreno 已提交
895 896 897
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
898
				columnsStartAt1: true,
I
isidor 已提交
899
				supportsVariableType: true, // #8858
900
				supportsVariablePaging: true, // #9537
I
isidor 已提交
901 902
				supportsRunInTerminalRequest: true, // #10574
				locale: platform.locale
J
Joao Moreno 已提交
903
			}).then((result: DebugProtocol.InitializeResponse) => {
904
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
905
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
906
			}).then((result: DebugProtocol.Response) => {
907
				if (session.disconnected) {
908 909
					return TPromise.as(null);
				}
I
isidor 已提交
910
				this.focusStackFrame(undefined, undefined, process);
911
				this._onDidNewProcess.fire(process);
912

913
				const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getValue<debug.IDebugConfiguration>('debug').internalConsoleOptions;
I
isidor 已提交
914
				if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
J
Joao Moreno 已提交
915 916
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
917

918
				const openDebugOptions = this.configurationService.getValue<debug.IDebugConfiguration>('debug').openDebug;
A
Anton Vildyaev 已提交
919
				// Open debug viewlet based on the visibility of the side bar and openDebug setting
I
isidor 已提交
920
				if (openDebugOptions === 'openOnSessionStart' || (openDebugOptions === 'openOnFirstSessionStart' && this.firstSessionStart)) {
J
Joao Moreno 已提交
921 922
					this.viewletService.openViewlet(debug.VIEWLET_ID);
				}
I
isidor 已提交
923
				this.firstSessionStart = false;
924

I
isidor 已提交
925
				this.debugType.set(configuration.type);
926 927 928
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
929
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
930

K
kieferrm 已提交
931
				/* __GDPR__
K
kieferrm 已提交
932 933 934 935 936 937 938 939 940 941
					"debugSessionStart" : {
						"type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"exceptionBreakpoints": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
						"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"extensionName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
						"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"launchJsonExists": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
942
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
943 944 945 946
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
947
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
948
					isBuiltin: adapter.extensionDescription.isBuiltin,
949
					launchJsonExists: root && !!this.configurationService.getValue<debug.IGlobalConfig>('launch', { resource: root.uri })
I
isidor 已提交
950
				});
951 952
			}).then(() => process, (error: Error | string) => {
				if (errors.isPromiseCanceledError(error)) {
953 954 955 956
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

957
				const errorMessage = error instanceof Error ? error.message : error;
K
kieferrm 已提交
958
				/* __GDPR__
K
kieferrm 已提交
959 960
					"debugMisconfiguration" : {
						"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
961
						"error": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
K
kieferrm 已提交
962 963
					}
				*/
964
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
965
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
966 967
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
968
				}
I
isidor 已提交
969 970 971
				if (process) {
					this.model.removeProcess(process.getId());
				}
J
Joao Moreno 已提交
972 973 974
				// Show the repl if some error got logged there #5870
				if (this.model.getReplElements().length > 0) {
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
975 976 977
				}
				if (this.model.getReplElements().length === 0) {
					this.inDebugMode.reset();
J
Joao Moreno 已提交
978
				}
I
isidor 已提交
979

I
isidor 已提交
980
				this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []);
981
				return undefined;
J
Joao Moreno 已提交
982
			});
E
Erich Gamma 已提交
983 984 985
		});
	}

I
isidor 已提交
986 987 988
	private showError(message: string, actions: IAction[] = []): TPromise<any> {
		const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
		actions.push(configureAction);
989
		return this.dialogService.show(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), { cancelId: actions.length }).then(choice => {
I
isidor 已提交
990 991 992 993 994 995 996 997
			if (choice < actions.length) {
				return actions[choice].run();
			}

			return TPromise.as(null);
		});
	}

I
isidor 已提交
998
	private runPreLaunchTask(sessionId: string, root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
999
		if (!taskName) {
I
isidor 已提交
1000
			return TPromise.as(null);
E
Erich Gamma 已提交
1001 1002
		}

1003
		// run a task before starting a debug session
1004
		return this.taskService.getTask(root, taskName).then(task => {
1005
			if (!task) {
1006
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
E
Erich Gamma 已提交
1007 1008
			}

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
			function once(kind: TaskEventKind, event: Event<TaskEvent>): Event<TaskEvent> {
				return (listener, thisArgs = null, disposables?) => {
					const result = event(e => {
						if (e.kind === kind) {
							result.dispose();
							return listener.call(thisArgs, e);
						}
					}, null, disposables);
					return result;
				};
			}
1020 1021
			// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
			let taskStarted = false;
1022
			const promise = this.taskService.getActiveTasks().then(tasks => {
I
isidor 已提交
1023 1024 1025 1026
				if (tasks.filter(t => t._id === task._id).length) {
					// task is already running - nothing to do.
					return TPromise.as(null);
				}
1027 1028 1029 1030 1031
				this.toDisposeOnSessionEnd.get(sessionId).push(
					once(TaskEventKind.Active, this.taskService.onDidStateChange)(() => {
						taskStarted = true;
					})
				);
I
isidor 已提交
1032 1033
				const taskPromise = this.taskService.run(task);
				if (task.isBackground) {
1034 1035 1036
					return new TPromise((c, e) => this.toDisposeOnSessionEnd.get(sessionId).push(
						once(TaskEventKind.Inactive, this.taskService.onDidStateChange)(() => c(null)))
					);
I
isidor 已提交
1037
				}
E
Erich Gamma 已提交
1038

I
isidor 已提交
1039
				return taskPromise;
E
Erich Gamma 已提交
1040
			});
1041 1042 1043 1044 1045 1046 1047 1048 1049

			return new TPromise((c, e) => {
				promise.then(result => {
					taskStarted = true;
					c(result);
				}, error => e(error));

				setTimeout(() => {
					if (!taskStarted) {
A
Andre Weinand 已提交
1050
						e({ severity: severity.Error, message: nls.localize('taskNotTracked', "The preLaunchTask '{0}' cannot be tracked.", taskName) });
1051 1052 1053
					}
				}, 10000);
			});
E
Erich Gamma 已提交
1054 1055 1056
		});
	}

1057 1058
	public sourceIsNotAvailable(uri: uri): void {
		this.model.sourceIsNotAvailable(uri);
I
isidor 已提交
1059 1060
	}

1061
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
1062 1063 1064
		return this.textFileService.saveAll().then(() => {
			if (process.session.capabilities.supportsRestartRequest) {
				return <TPromise>process.session.custom('restart', null);
1065
			}
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
			const focusedProcess = this.viewModel.focusedProcess;
			const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();

			return process.session.disconnect(true).then(() => {
				if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost') && process.session.root) {
					return this.broadcastService.broadcast({
						channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
						payload: [process.session.root.uri.fsPath]
					});
				}
1076

1077 1078 1079 1080
				return new TPromise<void>((c, e) => {
					setTimeout(() => {
						// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
						let config = process.configuration;
1081

1082
						const launch = process.session.root ? this.configurationManager.getLaunch(process.session.root.uri) : undefined;
1083
						if (this.launchJsonChanged && launch) {
1084
							this.launchJsonChanged = false;
1085
							config = launch.getConfiguration(process.configuration.name) || config;
1086 1087 1088 1089 1090
							// Take the type from the process since the debug extension might overwrite it #21316
							config.type = process.configuration.type;
							config.noDebug = process.configuration.noDebug;
						}
						config.__restart = restartData;
1091
						this.startDebugging(launch, config).then(() => c(null), err => e(err));
1092 1093 1094 1095 1096 1097 1098 1099
					}, 300);
				});
			}).then(() => {
				if (preserveFocus) {
					// Restart should preserve the focused process
					const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
					if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
						this.focusStackFrame(undefined, undefined, restartedProcess);
1100
					}
1101
				}
1102
			});
1103
		});
E
Erich Gamma 已提交
1104 1105
	}

1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
	public stopProcess(process: debug.IProcess): TPromise<any> {
		if (process) {
			return process.session.disconnect(false, true);
		}

		const processes = this.model.getProcesses();
		if (processes.length) {
			return TPromise.join(processes.map(p => p.session.disconnect(false, true)));
		}

		this.sessionStates.clear();
		this._onDidChangeState.fire();
		return undefined;
	}

I
isidor 已提交
1121
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
1122
		const bpsExist = this.model.getBreakpoints().length > 0;
1123
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
K
kieferrm 已提交
1124
		/* __GDPR__
K
kieferrm 已提交
1125 1126 1127 1128 1129 1130 1131 1132
			"debugSessionStop" : {
				"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"success": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
I
isidor 已提交
1133
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
1134
			type: process && process.configuration.type,
I
isidor 已提交
1135 1136 1137 1138 1139
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
1140

I
isidor 已提交
1141
		this.model.removeProcess(session.getId());
I
isidor 已提交
1142
		if (process) {
I
isidor 已提交
1143
			process.inactive = true;
I
isidor 已提交
1144 1145
			this._onDidEndProcess.fire(process);
		}
I
isidor 已提交
1146

I
isidor 已提交
1147
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
1148 1149
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
1150
			this.focusStackFrame(null);
1151
		}
I
isidor 已提交
1152
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
1153

1154 1155
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
1156
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
1157
			this.model.getBreakpoints().forEach(bp => {
1158
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
1159 1160
			});
			this.model.updateBreakpoints(data);
1161

1162
			this.inDebugMode.reset();
I
isidor 已提交
1163
			this.debugType.reset();
1164
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
1165

1166
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
1167 1168
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
1169
		}
E
Erich Gamma 已提交
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
	}

	public getModel(): debug.IModel {
		return this.model;
	}

	public getViewModel(): debug.IViewModel {
		return this.viewModel;
	}

1180 1181
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
1182 1183
	}

1184 1185 1186
	private sendAllBreakpoints(process?: debug.IProcess): TPromise<any> {
		return TPromise.join(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, process)))
			.then(() => this.sendFunctionBreakpoints(process))
I
isidor 已提交
1187
			// send exception breakpoints at the end since some debug adapters rely on the order
1188
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
1189 1190
	}

1191 1192 1193 1194
	private sendBreakpoints(modelUri: uri, sourceModified = false, targetProcess?: debug.IProcess): TPromise<void> {

		const sendBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
I
isidor 已提交
1195 1196 1197
			if (!session.readyForBreakpoints) {
				return TPromise.as(null);
			}
1198

1199
			const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString());
1200

1201
			const source = process.sources.get(modelUri.toString());
I
isidor 已提交
1202 1203 1204 1205 1206 1207 1208 1209
			let rawSource: DebugProtocol.Source;
			if (source) {
				rawSource = source.raw;
			} else {
				const data = Source.getEncodedDebugData(modelUri);
				rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference };
			}

I
isidor 已提交
1210
			if (breakpointsToSend.length && !rawSource.adapterData) {
1211 1212
				rawSource.adapterData = breakpointsToSend[0].adapterData;
			}
1213 1214
			// Normalize all drive letters going out from vscode to debug adapters so we are consistent with our resolving #43959
			rawSource.path = normalizeDriveLetter(rawSource.path);
1215

I
isidor 已提交
1216 1217
			return session.setBreakpoints({
				source: rawSource,
1218
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1219
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1220 1221 1222 1223 1224
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1225

1226
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1227 1228
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1229 1230 1231 1232
					if (!breakpointsToSend[i].column) {
						// If there was no column sent ignore the breakpoint column response from the adapter
						data[breakpointsToSend[i].getId()].column = undefined;
					}
I
isidor 已提交
1233
				}
1234

I
isidor 已提交
1235 1236 1237
				this.model.updateBreakpoints(data);
			});
		};
1238

1239
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1240 1241
	}

1242 1243 1244
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1245
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1246
				return TPromise.as(null);
1247 1248
			}

I
isidor 已提交
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
			const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
			return session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => {
				if (!response || !response.body) {
					return;
				}

				const data: { [id: string]: { name?: string, verified?: boolean } } = {};
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
				}

				this.model.updateFunctionBreakpoints(data);
			});
		};

1264
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1265 1266
	}

1267 1268 1269 1270
	private sendExceptionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendExceptionBreakpointsToProcess = (process: debug.IProcess): TPromise<any> => {
			const session = <RawDebugSession>process.session;
			if (!session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
I
isidor 已提交
1271
				return TPromise.as(null);
I
isidor 已提交
1272 1273
			}

I
isidor 已提交
1274 1275 1276 1277
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1278
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1279 1280
	}

1281 1282 1283
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1284
		}
I
isidor 已提交
1285

1286
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1287 1288 1289
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
I
isidor 已提交
1290 1291 1292 1293 1294
		const toRemove = this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED));
		if (toRemove.length) {
			this.model.removeBreakpoints(toRemove);
		}
1295 1296

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1297 1298
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1299 1300
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
1301 1302 1303
			if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) {
				this.launchJsonChanged = true;
			}
1304
		});
E
Erich Gamma 已提交
1305 1306 1307
	}

	private store(): void {
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340
		const breakpoints = this.model.getBreakpoints();
		if (breakpoints.length) {
			this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		if (!this.model.areBreakpointsActivated()) {
			this.storageService.store(DEBUG_BREAKPOINTS_ACTIVATED_KEY, 'false', StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE);
		}

		const functionBreakpoints = this.model.getFunctionBreakpoints();
		if (functionBreakpoints.length) {
			this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		const exceptionBreakpoints = this.model.getExceptionBreakpoints();
		if (exceptionBreakpoints.length) {
			this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		const watchExpressions = this.model.getWatchExpressions();
		if (watchExpressions.length) {
			this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE);
		}
E
Erich Gamma 已提交
1341 1342 1343
	}

	public dispose(): void {
I
isidor 已提交
1344
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1345
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1346 1347
	}
}