debugService.ts 46.3 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';
9
import * as paths from 'vs/base/common/paths';
I
isidor 已提交
10
import { generateUuid } from 'vs/base/common/uuid';
E
Erich Gamma 已提交
11
import uri from 'vs/base/common/uri';
J
Johannes Rieken 已提交
12
import { Action } from 'vs/base/common/actions';
I
isidor 已提交
13 14 15
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 已提交
16
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
17
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
18
import * as aria from 'vs/base/browser/ui/aria/aria';
19
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
J
Johannes Rieken 已提交
20 21 22 23 24
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';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
25
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
26
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
27
import { IWindowsService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
28 29 30
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
31
import { ICommandService } from 'vs/platform/commands/common/commands';
J
Johannes Rieken 已提交
32
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
33
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
34
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
35
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, OutputNameValueElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
36 37
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
38
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
I
isidor 已提交
39
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
J
Johannes Rieken 已提交
40 41
import { ITaskService, TaskEvent, TaskType, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { TaskError, TaskErrors } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
42
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
B
Benjamin Pasero 已提交
43
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
44
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
45
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
46 47 48 49
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
J
Joao Moreno 已提交
50
import { IWindowIPCService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService';
S
Sandeep Somavarapu 已提交
51
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/electron-browser/extensionHost';
E
Erich Gamma 已提交
52

I
isidor 已提交
53 54 55 56 57 58
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';
const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
E
Erich Gamma 已提交
59

60 61 62 63 64
interface StartSessionResult {
	status: 'ok' | 'initialConfiguration' | 'saveConfiguration';
	content?: string;
};

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

I
isidor 已提交
68
	private sessionStates: Map<string, debug.State>;
69
	private _onDidChangeState: Emitter<debug.State>;
I
isidor 已提交
70 71
	private model: Model;
	private viewModel: ViewModel;
72
	private configurationManager: ConfigurationManager;
73
	private customTelemetryService: ITelemetryService;
E
Erich Gamma 已提交
74
	private lastTaskEvent: TaskEvent;
75
	private toDispose: lifecycle.IDisposable[];
I
isidor 已提交
76
	private toDisposeOnSessionEnd: Map<string, lifecycle.IDisposable[]>;
A
Alex Dima 已提交
77
	private inDebugMode: IContextKey<boolean>;
I
isidor 已提交
78
	private debugType: IContextKey<string>;
I
isidor 已提交
79
	private breakpointsToSendOnResourceSaved: Set<string>;
E
Erich Gamma 已提交
80 81 82 83

	constructor(
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
84 85
		@ITextFileService private textFileService: ITextFileService,
		@IViewletService private viewletService: IViewletService,
I
isidor 已提交
86
		@IPanelService private panelService: IPanelService,
E
Erich Gamma 已提交
87 88
		@IMessageService private messageService: IMessageService,
		@IPartService private partService: IPartService,
89
		@IWindowsService private windowsService: IWindowsService,
J
Joao Moreno 已提交
90
		@IWindowIPCService private windowService: IWindowIPCService,
E
Erich Gamma 已提交
91 92
		@ITelemetryService private telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
93
		@IContextKeyService contextKeyService: IContextKeyService,
I
isidor 已提交
94
		@ILifecycleService lifecycleService: ILifecycleService,
I
isidor 已提交
95
		@IInstantiationService private instantiationService: IInstantiationService,
A
Alex Dima 已提交
96
		@IExtensionService private extensionService: IExtensionService,
97
		@IMarkerService private markerService: IMarkerService,
98
		@ITaskService private taskService: ITaskService,
99
		@IFileService private fileService: IFileService,
100
		@IConfigurationService private configurationService: IConfigurationService,
101
		@ICommandService private commandService: ICommandService
E
Erich Gamma 已提交
102 103
	) {
		this.toDispose = [];
I
isidor 已提交
104 105
		this.toDisposeOnSessionEnd = new Map<string, lifecycle.IDisposable[]>();
		this.breakpointsToSendOnResourceSaved = new Set<string>();
106
		this._onDidChangeState = new Emitter<debug.State>();
I
isidor 已提交
107
		this.sessionStates = new Map<string, debug.State>();
E
Erich Gamma 已提交
108

109
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
110
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
I
isidor 已提交
111
		this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
E
Erich Gamma 已提交
112

I
isidor 已提交
113
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
114
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
115
		this.toDispose.push(this.model);
I
isidor 已提交
116
		this.viewModel = new ViewModel(this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, null));
117

118
		this.registerListeners(lifecycleService);
E
Erich Gamma 已提交
119 120
	}

121 122
	private registerListeners(lifecycleService: ILifecycleService): void {
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
E
Erich Gamma 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

		if (this.taskService) {
			this.toDispose.push(this.taskService.addListener2(TaskServiceEvents.Active, (e: TaskEvent) => {
				this.lastTaskEvent = e;
			}));
			this.toDispose.push(this.taskService.addListener2(TaskServiceEvents.Inactive, (e: TaskEvent) => {
				if (e.type === TaskType.SingleRun) {
					this.lastTaskEvent = null;
				}
			}));
			this.toDispose.push(this.taskService.addListener2(TaskServiceEvents.Terminated, (e: TaskEvent) => {
				this.lastTaskEvent = null;
			}));
		}

138 139
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
140

I
isidor 已提交
141
		this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this));
142
		this.toDispose.push(this.configurationService.onDidUpdateConfiguration((event) => {
I
isidor 已提交
143
			if (event.sourceConfig) {
144 145 146 147 148 149 150
				const names = this.configurationManager.getConfigurationNames();
				if (names.every(name => name !== this.viewModel.selectedConfigurationName)) {
					// Current selected configuration no longer exists - take the first configuration instead.
					this.viewModel.setSelectedConfigurationName(names.length ? names[0] : undefined);
				}
			}
		}));
151 152 153
	}

	private onBroadcast(broadcast: IBroadcast): void {
154

I
isidor 已提交
155
		// attach: PH is ready to be attached to
156 157 158
		// TODO@Isidor this is a hack to just get any 'extensionHost' session.
		// Optimally the broadcast would contain the id of the session
		// We are only intersted if we have an active debug session for extensionHost
159 160
		const process = this.model.getProcesses().filter(p => p.configuration.type === 'extensionHost').pop();
		const session = process ? <RawDebugSession>process.session : null;
161
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
162
			this.rawAttach(session, broadcast.payload.port);
163 164
			return;
		}
165

166
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
167
			this.onSessionEnd(session);
168 169 170
			return;
		}

I
isidor 已提交
171
		// from this point on we require an active session
172 173
		if (!session) {
			return;
174 175
		}

B
Benjamin Pasero 已提交
176
		// an extension logged output, show it inside the REPL
177
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
178 179 180 181 182 183 184 185 186 187 188
			let extensionOutput: ILogEntry = broadcast.payload;
			let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;

			let args: any[] = [];
			try {
				let parsed = JSON.parse(extensionOutput.arguments);
				args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
			} catch (error) {
				args.push(extensionOutput.arguments);
			}

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) {
I
isidor 已提交
209
						this.model.appendToRepl(simpleVals.join(' '), sev);
210 211 212
						simpleVals = [];
					}

I
isidor 已提交
213
					// show object
I
isidor 已提交
214
					this.model.appendToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
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
242
			if (simpleVals.length) {
I
isidor 已提交
243
				this.model.appendToRepl(simpleVals.join(' '), sev);
244 245
			}
		}
E
Erich Gamma 已提交
246 247
	}

I
isidor 已提交
248
	private registerSessionListeners(process: Process, session: RawDebugSession): void {
I
isidor 已提交
249 250
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => {
251
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
252
			const sendConfigurationDone = () => {
253
				if (session && session.capabilities.supportsConfigurationDoneRequest) {
254
					return session.configurationDone().done(null, e => {
255
						// Disconnect the debug session on configuration done error #10596
256 257
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
258 259 260
						}
						this.messageService.show(severity.Error, e.message);
					});
I
isidor 已提交
261
				}
262
			};
263 264 265

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

I
isidor 已提交
268
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
269
			this.setStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
270
			const threadId = event.body.threadId;
E
Erich Gamma 已提交
271

272 273 274 275 276
			session.threads().then(response => {
				if (!response || !response.body || !response.body.threads) {
					return;
				}

I
isidor 已提交
277
				const rawThread = response.body.threads.filter(t => t.id === threadId).pop();
278
				this.model.rawUpdate({
279
					sessionId: session.getId(),
I
isidor 已提交
280
					thread: rawThread,
I
isidor 已提交
281
					threadId,
282 283 284
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
285

I
isidor 已提交
286 287
				const thread = process && process.getThread(threadId);
				if (thread) {
288
					thread.fetchCallStack().then(callStack => {
289
						if (callStack.length > 0 && !this.viewModel.focusedStackFrame) {
290
							// focus first stack frame from top that has source location if no other stack frame is focussed
I
isidor 已提交
291
							const stackFrameToFocus = first(callStack, sf => sf.source && !sf.source.deemphasize, callStack[0]);
292
							this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
I
isidor 已提交
293 294 295
							this.windowService.getWindow().focus();
							aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.lineNumber));

I
isidor 已提交
296
							return stackFrameToFocus.openInEditor(this.editorService);
I
isidor 已提交
297
						}
298
						return undefined;
I
isidor 已提交
299 300
					});
				}
E
Erich Gamma 已提交
301 302 303
			}, errors.onUnexpectedError);
		}));

I
isidor 已提交
304
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => {
E
Erich Gamma 已提交
305
			if (event.body.reason === 'started') {
306
				this.fetchThreads(session).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
307
			} else if (event.body.reason === 'exited') {
308
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
309 310 311
			}
		}));

I
isidor 已提交
312
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => {
313
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
314
			if (session && session.getId() === event.body.sessionId) {
315
				if (event.body && typeof event.body.restart === 'boolean' && event.body.restart && process) {
I
isidor 已提交
316
					this.restartProcess(process).done(null, err => this.messageService.show(severity.Error, err.message));
317
				} else {
318
					session.disconnect().done(null, errors.onUnexpectedError);
319
				}
E
Erich Gamma 已提交
320 321 322
			}
		}));

I
isidor 已提交
323
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => {
I
isidor 已提交
324
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
325 326 327 328
			this.model.clearThreads(session.getId(), false, threadId);
			if (this.viewModel.focusedProcess.getId() === session.getId()) {
				this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
329
			this.setStateAndEmit(session.getId(), debug.State.Running);
I
isidor 已提交
330 331
		}));

I
isidor 已提交
332
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
333 334 335 336
			if (!event.body) {
				return;
			}

337
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
338
			if (event.body.category === 'telemetry') {
339
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
340
				// and the user opted in telemetry
341 342
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
343
				}
344 345 346 347 348 349
			} else if (event.body.variablesReference) {
				const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid());
				container.getChildren().then(children => {
					children.forEach(child => {
						// 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;
350
						this.model.appendToRepl(child, outputSeverity);
I
isidor 已提交
351
					});
352 353 354
				});
			} else if (typeof event.body.output === 'string') {
				this.model.appendToRepl(event.body.output, outputSeverity);
E
Erich Gamma 已提交
355 356 357
			}
		}));

I
isidor 已提交
358
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
359
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
360
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
361 362 363
			if (breakpoint) {
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
364
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
365 366 367 368 369 370
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
371
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
372
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
373
			const process = this.viewModel.focusedProcess;
374
			if (process && session && process.getId() === session.getId() && process.configuration.type === 'extensionHost' && this.sessionStates.get(session.getId()) === debug.State.Running &&
I
isidor 已提交
375
				process && this.contextService.getWorkspace() && process.configuration.noDebug) {
376
				this.windowsService.closeExtensionHostWindow(this.contextService.getWorkspace().resource.fsPath);
377
			}
378
			if (session && session.getId() === event.body.sessionId) {
379
				this.onSessionEnd(session);
380
			}
E
Erich Gamma 已提交
381 382 383
		}));
	}

384 385 386 387 388 389 390 391 392 393 394
	private fetchThreads(session: RawDebugSession): TPromise<any> {
		return session.threads().then(response => {
			if (response && response.body && response.body.threads) {
				response.body.threads.forEach(thread =>
					this.model.rawUpdate({
						sessionId: session.getId(),
						threadId: thread.id,
						thread
					}));
			}
		});
E
Erich Gamma 已提交
395 396
	}

397 398
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
399
		try {
400
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
401
				return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.respectColumn ? breakpoint.column : undefined, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.respectColumn);
E
Erich Gamma 已提交
402
			});
403 404 405
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
406 407
	}

408 409
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
410
		try {
411
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
412
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
413
			});
414 415 416
		} catch (e) { }

		return result || [];
I
isidor 已提交
417 418
	}

419 420
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
421 422
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
423
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
424
			});
425
		} catch (e) { }
E
Erich Gamma 已提交
426

427
		return result || [];
E
Erich Gamma 已提交
428 429
	}

I
isidor 已提交
430 431
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
432
		try {
J
Johannes Rieken 已提交
433
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
434
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
435
			});
436 437 438
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
439 440
	}

I
isidor 已提交
441
	public get state(): debug.State {
I
isidor 已提交
442 443
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess) {
I
isidor 已提交
444
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
445
		}
I
isidor 已提交
446 447
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
448 449 450
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
451 452
	}

453
	public get onDidChangeState(): Event<debug.State> {
454 455 456
		return this._onDidChangeState.event;
	}

I
isidor 已提交
457
	private setStateAndEmit(sessionId: string, newState: debug.State): void {
I
isidor 已提交
458 459 460 461 462
		if (newState === debug.State.Inactive) {
			this.sessionStates.delete(sessionId);
		} else {
			this.sessionStates.set(sessionId, newState);
		}
463
		this._onDidChangeState.fire(this.state);
E
Erich Gamma 已提交
464 465 466
	}

	public get enabled(): boolean {
B
Benjamin Pasero 已提交
467
		return this.contextService.hasWorkspace();
E
Erich Gamma 已提交
468 469
	}

470
	public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess): TPromise<void> {
I
isidor 已提交
471
		if (!process) {
472 473 474 475 476 477 478
			const processes = this.model.getProcesses();
			process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null;
		}
		if (!stackFrame) {
			const threads = process ? process.getAllThreads() : null;
			const callStack = threads && threads.length ? threads[0].getCallStack() : null;
			stackFrame = callStack && callStack.length ? callStack[0] : null;
I
isidor 已提交
479
		}
I
isidor 已提交
480

481
		this.viewModel.setFocusedStackFrame(stackFrame, process);
482
		this._onDidChangeState.fire(this.state);
483

484
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
485 486
	}

I
isidor 已提交
487
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
488 489
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
490
			if (breakpoint instanceof Breakpoint) {
491
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
492
			} else if (breakpoint instanceof FunctionBreakpoint) {
493 494
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
495

496
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
497 498
		}

499 500
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
501 502
	}

503 504 505
	public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
		this.model.addBreakpoints(uri, rawBreakpoints);
		rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath)));
506

507
		return this.sendBreakpoints(uri);
508 509
	}

510 511
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
512 513
		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 已提交
514

515
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
516
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
517 518
	}

519 520
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
521 522 523
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
524 525
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
526 527
	}

I
isidor 已提交
528
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
529
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
530
		return this.sendFunctionBreakpoints();
I
isidor 已提交
531 532
	}

I
isidor 已提交
533
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
534
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
535
		return this.sendFunctionBreakpoints();
I
isidor 已提交
536 537
	}

I
isidor 已提交
538
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
539
		this.telemetryService.publicLog('debugService/addReplExpression');
540
		return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
541
			// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
542
			.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
E
Erich Gamma 已提交
543 544
	}

545 546
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
547 548
	}

549 550
	public logToRepl(value: string, sev = severity.Info): void {
		this.model.appendToRepl(value, sev);
551 552
	}

I
isidor 已提交
553
	public addWatchExpression(name: string): TPromise<void> {
554
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
555 556
	}

I
isidor 已提交
557
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
558
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
559 560
	}

I
isidor 已提交
561 562 563 564
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

565 566
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
567 568
	}

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
	public startDebugging(configName?: string, noDebug = false): TPromise<any> {
		return this.textFileService.saveAll().then(() => {
			if (this.model.getProcesses().length === 0) {
				this.removeReplExpressions();
			}
			const manager = this.getConfigurationManager();
			configName = configName || this.viewModel.selectedConfigurationName;
			const config = manager.getConfiguration(configName);
			const compound = manager.getCompound(configName);
			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.")));
				}

				return TPromise.join(compound.configurations.map(name => this.startDebugging(name)));
			}
			if (configName && !config) {
				return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configName)));
			}

			return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => {
				if (noDebug && config) {
					config.noDebug = true;
				}
				if (commandAndType && commandAndType.command) {
					const defaultConfig = noDebug ? { noDebug: true } : {};
					return this.commandService.executeCommand(commandAndType.command, config || defaultConfig).then((result: StartSessionResult) => {
						if (this.contextService.getWorkspace()) {
							if (result && result.status === 'initialConfiguration') {
								return manager.openConfigFile(false, commandAndType.type);
							}

							if (result && result.status === 'saveConfiguration') {
								return this.fileService.updateContent(manager.configFileUri, result.content).then(() => manager.openConfigFile(false));
							}
I
isidor 已提交
605
						}
606 607 608
						return undefined;
					});
				}
609

610 611 612 613 614 615 616 617 618 619 620
				if (config) {
					return this.createProcess(config);
				}
				if (this.contextService.getWorkspace() && commandAndType) {
					return manager.openConfigFile(false, commandAndType.type);
				}

				return undefined;
			});
		});
	}
621

622 623 624 625
	public createProcess(config: debug.IConfig): TPromise<any> {
		return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration())	// make sure configuration is up to date
			.then(() => this.extensionService.onReady()
				.then(() => {
626 627 628
					return this.configurationManager.resloveConfiguration(config).then(resolvedConfig => {
						if (!resolvedConfig) {
							// User canceled resolving of interactive variables, silently return
629
							return undefined;
630
						}
I
isidor 已提交
631

632
						if (!this.configurationManager.getAdapter(resolvedConfig.type)) {
633 634 635
							const message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
								nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
							return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
636
						}
I
isidor 已提交
637

638 639 640 641 642
						return this.runPreLaunchTask(resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
							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)) {
643
								return this.doCreateProcess(resolvedConfig);
J
Johannes Rieken 已提交
644 645
							}

646 647 648 649 650 651 652
							this.messageService.show(severity.Error, {
								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),
								actions: [
									new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
										this.messageService.hideAll();
653
										return this.doCreateProcess(resolvedConfig);
654 655 656 657
									}),
									this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
									CloseAction
								]
J
Johannes Rieken 已提交
658
							});
659
							return undefined;
660 661 662
						}, (err: TaskError) => {
							if (err.code !== TaskErrors.NotConfigured) {
								throw err;
I
isidor 已提交
663 664
							}

665 666 667
							this.messageService.show(err.severity, {
								message: err.message,
								actions: [this.taskService.configureAction(), CloseAction]
668
							});
I
isidor 已提交
669
						});
670 671 672 673 674 675 676 677 678 679 680 681
					}, err => {
						if (!this.contextService.getWorkspace()) {
							return this.messageService.show(severity.Error, 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."));
						}

						return this.configurationManager.openConfigFile(false).then(openend => {
							if (openend) {
								this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message));
							}
						});
					});
				}));
I
isidor 已提交
682
	}
E
Erich Gamma 已提交
683

684 685 686 687

	private doCreateProcess(configuration: debug.IConfig): TPromise<any> {
		const sessionId = generateUuid();
		this.setStateAndEmit(sessionId, debug.State.Initializing);
688

J
Joao Moreno 已提交
689
		return this.telemetryService.getTelemetryInfo().then(info => {
690
			const telemetryInfo: { [key: string]: string } = Object.create(null);
691 692
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
693 694
			return telemetryInfo;
		}).then(data => {
695 696 697
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
698
			this.customTelemetryService = null;
699
			let client: TelemetryClient;
700 701

			if (aiKey) {
702
				client = new TelemetryClient(
703 704 705 706
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
707
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
708
						env: {
B
Benjamin Pasero 已提交
709
							ELECTRON_RUN_AS_NODE: 1,
710 711 712
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
713
					}
714
				);
715

716 717
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
718

719
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
720 721
			}

722
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService);
723
			const process = this.model.addProcess(configuration, session);
724

I
isidor 已提交
725
			this.toDisposeOnSessionEnd.set(session.getId(), []);
726
			if (client) {
I
isidor 已提交
727
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
728
			}
I
isidor 已提交
729
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
730

731
			return session.initialize({
732
				clientID: 'vscode',
J
Joao Moreno 已提交
733 734 735
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
736
				columnsStartAt1: true,
I
isidor 已提交
737
				supportsVariableType: true, // #8858
738 739
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
740
			}).then((result: DebugProtocol.InitializeResponse) => {
741
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
742
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
743
			}).then((result: DebugProtocol.Response) => {
744
				if (session.disconnected) {
745 746
					return TPromise.as(null);
				}
747 748 749
				if (!this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, process);
				}
J
Joao Moreno 已提交
750 751 752
				if (configuration.internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && configuration.internalConsoleOptions !== 'neverOpen')) {
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
753

I
isidor 已提交
754
				if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.getWorkspace())) {
J
Joao Moreno 已提交
755 756 757 758
					// We only want to change the workbench view state on the first debug session #5738 and if the side bar is not hidden
					this.viewModel.changedWorkbenchViewState = true;
					this.viewletService.openViewlet(debug.VIEWLET_ID);
				}
759

J
Joao Moreno 已提交
760 761 762 763 764 765
				// Do not change status bar to orange if we are just running without debug.
				if (!configuration.noDebug) {
					this.partService.addClass('debugging');
				}
				this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
				this.inDebugMode.set(true);
I
isidor 已提交
766
				this.debugType.set(configuration.type);
767 768 769
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
770
				this.setStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
771

772
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
773 774 775 776
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
777
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
778 779
					isBuiltin: adapter.extensionDescription.isBuiltin,
					launchJsonExists: !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch')
J
Joao Moreno 已提交
780 781
				});
			}).then(undefined, (error: any) => {
782 783 784 785 786
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

787 788
				const errorMessage = error instanceof Error ? error.message : error;
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
789
				this.setStateAndEmit(session.getId(), debug.State.Inactive);
790 791
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
792 793 794 795 796
				}
				// 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);
				}
I
isidor 已提交
797

J
Joao Moreno 已提交
798 799
				const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
				const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction];
800
				this.messageService.show(severity.Error, { message: errorMessage, actions });
801
				return undefined;
J
Joao Moreno 已提交
802
			});
E
Erich Gamma 已提交
803 804 805
		});
	}

I
isidor 已提交
806
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
807
		if (!taskName) {
I
isidor 已提交
808
			return TPromise.as(null);
E
Erich Gamma 已提交
809 810
		}

811
		// run a task before starting a debug session
E
Erich Gamma 已提交
812
		return this.taskService.tasks().then(descriptions => {
813
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
814
			if (filteredTasks.length !== 1) {
815 816
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName), {
					actions: [
817
						this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
818
						this.taskService.configureAction(),
819
						CloseAction
820 821
					]
				}));
E
Erich Gamma 已提交
822 823
			}

I
isidor 已提交
824
			// task is already running - nothing to do.
825
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
826
				return TPromise.as(null);
E
Erich Gamma 已提交
827 828 829
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
830
				// there is a different task running currently.
I
isidor 已提交
831
				return TPromise.wrapError(errors.create(nls.localize('differentTaskRunning', "There is a task {0} running. Can not run pre launch task {1}.", this.lastTaskEvent.taskName, taskName)));
E
Erich Gamma 已提交
832 833
			}

I
isidor 已提交
834
			// no task running, execute the preLaunchTask.
835
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
836
				this.lastTaskEvent = null;
I
isidor 已提交
837
				return result;
E
Erich Gamma 已提交
838 839 840
			}, err => {
				this.lastTaskEvent = null;
			});
841

842
			if (filteredTasks[0].isBackground) {
A
Alex Dima 已提交
843
				return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
844 845 846
			}

			return taskPromise;
E
Erich Gamma 已提交
847 848 849
		});
	}

850 851 852
	private rawAttach(session: RawDebugSession, port: number): TPromise<any> {
		if (session) {
			return session.attach({ port });
I
isidor 已提交
853 854
		}

855 856 857 858
		const config = this.configurationManager.getConfiguration(this.viewModel.selectedConfigurationName);
		return this.configurationManager.resloveConfiguration(config).then(resolvedConfig => {
			resolvedConfig.request = 'attach';
			resolvedConfig.port = port;
I
isidor 已提交
859
			this.doCreateProcess(resolvedConfig);
I
isidor 已提交
860
		});
I
isidor 已提交
861 862
	}

I
isidor 已提交
863 864 865 866
	public deemphasizeSource(uri: uri): void {
		this.model.deemphasizeSource(uri);
	}

I
isidor 已提交
867
	public restartProcess(process: debug.IProcess): TPromise<any> {
868
		if (process.session.capabilities.supportsRestartRequest) {
869 870
			return process.session.custom('restart', null);
		}
I
isidor 已提交
871
		const preserveFocus = process.getId() === this.viewModel.focusedProcess.getId();
872 873

		return process.session.disconnect(true).then(() =>
874
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
875
				setTimeout(() => {
876 877
					// Read the configuration again if a launch.json exists, if not just use the inmemory configuration #19366
					const config = this.configurationManager.getConfiguration(process.configuration.name);
878 879 880 881
					if (config) {
						// Take the type from the process since the debug extension might overwrite it #21316
						config.type = process.configuration.type;
					}
882
					this.createProcess(config || process.configuration).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
883
				}, 300);
I
isidor 已提交
884
			})
885
		).then(() => {
I
isidor 已提交
886 887
			if (preserveFocus) {
				// Restart should preserve the focused process
888
				const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
I
isidor 已提交
889 890 891
				if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, restartedProcess);
				}
892 893
			}
		});
E
Erich Gamma 已提交
894 895
	}

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
	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 已提交
911
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
912
		const bpsExist = this.model.getBreakpoints().length > 0;
913
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
I
isidor 已提交
914
		this.telemetryService.publicLog('debugSessionStop', {
915
			type: process.configuration.type,
I
isidor 已提交
916 917 918 919 920
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
921

I
isidor 已提交
922
		try {
I
isidor 已提交
923
			this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
924 925 926 927
		} catch (e) {
			// an internal module might be open so the dispose can throw -> ignore and continue with stop session.
		}

928
		this.model.removeProcess(process.getId());
I
isidor 已提交
929 930
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
931 932
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
933
		this.setStateAndEmit(session.getId(), debug.State.Inactive);
934

935 936 937
		if (this.model.getProcesses().length === 0) {
			this.partService.removeClass('debugging');
			// set breakpoints back to unverified since the session ended.
938
			const data: { [id: string]: { line: number, verified: boolean } } = {};
939
			this.model.getBreakpoints().forEach(bp => {
940
				data[bp.getId()] = { line: bp.lineNumber, verified: false };
941 942
			});
			this.model.updateBreakpoints(data);
943

944
			this.inDebugMode.reset();
I
isidor 已提交
945
			this.debugType.reset();
946
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
947

948
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
949 950
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
951
		}
E
Erich Gamma 已提交
952 953 954 955 956 957 958 959 960 961
	}

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

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

962 963
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
964 965
	}

966 967 968
	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 已提交
969
			// send exception breakpoints at the end since some debug adapters rely on the order
970
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
971 972
	}

973 974 975 976
	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 已提交
977 978 979 980 981
			if (!session.readyForBreakpoints) {
				return TPromise.as(null);
			}
			if (this.textFileService.isDirty(modelUri)) {
				// Only send breakpoints for a file once it is not dirty #8077
I
isidor 已提交
982
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
983 984
				return TPromise.as(null);
			}
985

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

988
			const source = process.sources.get(modelUri.toString());
989
			const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true) };
I
isidor 已提交
990 991 992

			return session.setBreakpoints({
				source: rawSource,
993
				lines: breakpointsToSend.map(bp => bp.lineNumber),
994
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
995 996 997 998 999
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1000

I
isidor 已提交
1001
				const data: { [id: string]: { line?: number, column?: number, verified: boolean } } = {};
I
isidor 已提交
1002 1003
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1004 1005 1006 1007
					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 已提交
1008
				}
1009

I
isidor 已提交
1010 1011 1012
				this.model.updateBreakpoints(data);
			});
		};
1013

1014
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1015 1016
	}

1017 1018 1019
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1020
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1021
				return TPromise.as(null);
1022 1023
			}

I
isidor 已提交
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
			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);
			});
		};

1039
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1040 1041
	}

1042 1043 1044 1045
	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 已提交
1046
				return TPromise.as(null);
I
isidor 已提交
1047 1048
			}

I
isidor 已提交
1049 1050 1051 1052
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1053
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1054 1055
	}

1056 1057 1058
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1059
		}
I
isidor 已提交
1060

1061
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1062 1063 1064
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1065
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1066
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1067 1068

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1069 1070
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1071 1072 1073
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
		});
E
Erich Gamma 已提交
1074 1075 1076 1077 1078
	}

	private store(): void {
		this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(this.model.getBreakpoints()), StorageScope.WORKSPACE);
		this.storageService.store(DEBUG_BREAKPOINTS_ACTIVATED_KEY, this.model.areBreakpointsActivated() ? 'true' : 'false', StorageScope.WORKSPACE);
1079
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1080
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1081
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.viewModel.selectedConfigurationName, StorageScope.WORKSPACE);
1082
		this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(this.model.getWatchExpressions().map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1083 1084 1085
	}

	public dispose(): void {
I
isidor 已提交
1086
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1087
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1088 1089
	}
}