debugService.ts 49.8 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 * 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';
J
Johannes Rieken 已提交
13
import { Action } from 'vs/base/common/actions';
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 24 25
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';
26
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
27
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
28
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
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';
32
import { ICommandService } from 'vs/platform/commands/common/commands';
J
Johannes Rieken 已提交
33
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
34
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
35
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
36
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, OutputNameValueElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
37 38
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
39
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
I
isidor 已提交
40
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
I
isidor 已提交
41
import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
42
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
43
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
B
Benjamin Pasero 已提交
44
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
45
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
46
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
47 48 49 50
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';
51
import { ILogEntry, 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';
52
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
E
Erich Gamma 已提交
53

I
isidor 已提交
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';
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>;
70
	private _onDidNewProcess: Emitter<debug.IProcess>;
I
isidor 已提交
71
	private _onDidEndProcess: Emitter<debug.IProcess>;
72
	private _onDidCustomEvent: Emitter<debug.DebugEvent>;
I
isidor 已提交
73 74
	private model: Model;
	private viewModel: ViewModel;
75
	private allProcesses: Map<string, debug.IProcess>;
76
	private configurationManager: ConfigurationManager;
77
	private customTelemetryService: ITelemetryService;
78
	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;
E
Erich Gamma 已提交
85 86 87 88

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

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

I
isidor 已提交
125
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
126
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
127
		this.toDispose.push(this.model);
128
		this.viewModel = new ViewModel();
129

130
		this.registerListeners(lifecycleService);
E
Erich Gamma 已提交
131 132
	}

133 134
	private registerListeners(lifecycleService: ILifecycleService): void {
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
135 136
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
137
		this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this));
138 139 140
	}

	private onBroadcast(broadcast: IBroadcast): void {
141

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

150
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
151
			this.onSessionEnd(session);
I
isidor 已提交
152

153 154 155
			process.configuration.request = 'attach';
			process.configuration.port = broadcast.payload.port;
			this.doCreateProcess(process.session.root, process.configuration, process.getId());
156 157
			return;
		}
158

159
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
160
			this.onSessionEnd(session);
161 162 163
			return;
		}

B
Benjamin Pasero 已提交
164
		// an extension logged output, show it inside the REPL
165
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
166
			let extensionOutput: ILogEntry = broadcast.payload.logEntry;
167 168 169 170 171 172 173 174 175 176
			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 已提交
177
			// add output for each argument logged
178 179 180 181
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

I
isidor 已提交
182
				// undefined gets printed as 'undefined'
183 184 185 186
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

I
isidor 已提交
187
				// null gets printed as 'null'
188 189 190 191
				else if (a === null) {
					simpleVals.push('null');
				}

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

I
isidor 已提交
195
					// flush any existing simple values logged
196
					if (simpleVals.length) {
197
						this.logToRepl(simpleVals.join(' '), sev);
198 199 200
						simpleVals = [];
					}

I
isidor 已提交
201
					// show object
202
					this.logToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
203 204
				}

I
isidor 已提交
205 206
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
207 208 209 210 211 212
				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 已提交
213
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

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

I
isidor 已提交
229
			// flush simple values
J
jens1o 已提交
230
			// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
231
			if (simpleVals.length) {
232
				this.logToRepl(simpleVals.join(' ') + '\n', sev);
233 234
			}
		}
E
Erich Gamma 已提交
235 236
	}

I
isidor 已提交
237 238 239 240 241 242
	private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
		const callStack = thread.getCallStack();
		if (!callStack.length || this.viewModel.focusedStackFrame) {
			return TPromise.as(null);
		}

243
		// focus first stack frame from top that has source location if no other stack frame is focused
I
isidor 已提交
244 245 246 247 248 249
		const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, undefined);
		if (!stackFrameToFocus) {
			return TPromise.as(null);
		}

		this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
I
isidor 已提交
250
		if (thread.stoppedDetails) {
251
			this.windowService.focusWindow();
I
isidor 已提交
252 253
			aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
		}
I
isidor 已提交
254 255 256 257

		return stackFrameToFocus.openInEditor(this.editorService);
	}

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

I
isidor 已提交
260
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
261

I
isidor 已提交
262
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => {
263
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
264
			const sendConfigurationDone = () => {
265
				if (session && session.capabilities.supportsConfigurationDoneRequest) {
266
					return session.configurationDone().done(null, e => {
267
						// Disconnect the debug session on configuration done error #10596
268 269
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
270 271 272
						}
						this.messageService.show(severity.Error, e.message);
					});
I
isidor 已提交
273
				}
274
			};
275 276 277

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

I
isidor 已提交
280
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
281
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
282 283
			this.fetchThreads(session, event.body).done(() => {
				const thread = process && process.getThread(event.body.threadId);
I
isidor 已提交
284
				if (thread) {
285 286 287
					// 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(() => {
I
isidor 已提交
288
						return this.tryToAutoFocusStackFrame(thread);
I
isidor 已提交
289 290
					});
				}
E
Erich Gamma 已提交
291 292 293
			}, errors.onUnexpectedError);
		}));

I
isidor 已提交
294
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => {
E
Erich Gamma 已提交
295
			if (event.body.reason === 'started') {
296
				this.fetchThreads(session).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
297
			} else if (event.body.reason === 'exited') {
298
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
299 300 301
			}
		}));

I
isidor 已提交
302
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => {
303
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
304
			if (session && session.getId() === event.sessionId) {
305 306
				if (event.body && event.body.restart && process) {
					this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message));
307
				} else {
308
					session.disconnect().done(null, errors.onUnexpectedError);
309
				}
E
Erich Gamma 已提交
310 311 312
			}
		}));

I
isidor 已提交
313
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => {
I
isidor 已提交
314
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
315 316 317 318
			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 已提交
319
			this.updateStateAndEmit(session.getId(), debug.State.Running);
I
isidor 已提交
320 321
		}));

I
isidor 已提交
322
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
323 324 325 326
			if (!event.body) {
				return;
			}

327
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
328
			if (event.body.category === 'telemetry') {
329
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
330
				// and the user opted in telemetry
331 332
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
333
				}
334 335 336 337 338 339
			} 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;
340
						this.logToRepl(child, outputSeverity);
I
isidor 已提交
341
					});
342 343
				});
			} else if (typeof event.body.output === 'string') {
344
				this.logToRepl(event.body.output, outputSeverity);
E
Erich Gamma 已提交
345 346 347
			}
		}));

I
isidor 已提交
348
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
349
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
350
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
351
			if (breakpoint) {
I
isidor 已提交
352 353 354
				if (!breakpoint.column) {
					event.body.breakpoint.column = undefined;
				}
I
isidor 已提交
355 356
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
357
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
358 359 360 361 362 363
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
364
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
365
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
366
			const process = this.viewModel.focusedProcess;
I
isidor 已提交
367
			if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
B
Benjamin Pasero 已提交
368
				process && this.contextService.hasWorkspace() && process.configuration.noDebug) {
369 370
				this.broadcastService.broadcast({
					channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
371
					payload: [process.session.root.fsPath]
372
				});
373
			}
374
			if (session && session.getId() === event.sessionId) {
375
				this.onSessionEnd(session);
376
			}
E
Erich Gamma 已提交
377
		}));
378 379 380 381

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

I
isidor 已提交
384
	private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> {
385 386 387 388 389 390
		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,
I
isidor 已提交
391 392 393
						thread,
						stoppedDetails,
						allThreadsStopped: stoppedDetails ? stoppedDetails.allThreadsStopped : undefined
394 395 396
					}));
			}
		});
E
Erich Gamma 已提交
397 398
	}

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

		return result || [];
E
Erich Gamma 已提交
408 409
	}

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

		return result || [];
I
isidor 已提交
419 420
	}

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

429
		return result || [];
E
Erich Gamma 已提交
430 431
	}

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

		return result || [];
E
Erich Gamma 已提交
441 442
	}

I
isidor 已提交
443
	public get state(): debug.State {
444 445 446 447
		const focusedThread = this.viewModel.focusedThread;
		if (focusedThread && focusedThread.stopped) {
			return debug.State.Stopped;
		}
I
isidor 已提交
448
		const focusedProcess = this.viewModel.focusedProcess;
I
isidor 已提交
449
		if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) {
I
isidor 已提交
450
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
451
		}
I
isidor 已提交
452 453
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
454 455 456
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
457 458
	}

459
	public get onDidChangeState(): Event<debug.State> {
460 461 462
		return this._onDidChangeState.event;
	}

463 464 465 466
	public get onDidNewProcess(): Event<debug.IProcess> {
		return this._onDidNewProcess.event;
	}

I
isidor 已提交
467 468 469 470
	public get onDidEndProcess(): Event<debug.IProcess> {
		return this._onDidEndProcess.event;
	}

471
	public get onDidCustomEvent(): Event<debug.DebugEvent> {
472 473 474
		return this._onDidCustomEvent.event;
	}

I
isidor 已提交
475 476 477 478 479 480 481
	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 已提交
482
		}
I
isidor 已提交
483 484

		const state = this.state;
I
isidor 已提交
485 486 487 488
		const stateLabel = debug.State[state];
		if (stateLabel) {
			this.debugState.set(stateLabel.toLowerCase());
		}
I
isidor 已提交
489
		this._onDidChangeState.fire(state);
E
Erich Gamma 已提交
490 491
	}

492
	public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
I
isidor 已提交
493
		if (!process) {
494 495 496 497 498 499 500
			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 已提交
501
		}
I
isidor 已提交
502

503
		this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
I
isidor 已提交
504
		this.updateStateAndEmit();
505

506
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
507 508
	}

I
isidor 已提交
509
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
510 511
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
512
			if (breakpoint instanceof Breakpoint) {
513
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
514
			} else if (breakpoint instanceof FunctionBreakpoint) {
515 516
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
517

518
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
519 520
		}

521 522
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
523 524
	}

525 526 527
	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)));
528

529
		return this.sendBreakpoints(uri);
530 531
	}

532 533
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
534 535
		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 已提交
536

537
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
538
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
539 540
	}

541 542
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
543 544 545
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
546 547
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
548 549
	}

I
isidor 已提交
550
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
551
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
552
		return this.sendFunctionBreakpoints();
I
isidor 已提交
553 554
	}

I
isidor 已提交
555
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
556
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
557
		return this.sendFunctionBreakpoints();
I
isidor 已提交
558 559
	}

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

567 568
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
569 570
	}

571 572 573 574 575 576 577
	public logToRepl(value: string | debug.IExpression, sev = severity.Info): void {
		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 {
			this.model.appendToRepl(value, sev);
		}
578 579
	}

I
isidor 已提交
580
	public addWatchExpression(name: string): TPromise<void> {
581
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
582 583
	}

I
isidor 已提交
584
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
585
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
586 587
	}

I
isidor 已提交
588 589 590 591
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

592 593
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
594 595
	}

596 597 598 599
	public evaluateWatchExpressions(): TPromise<void> {
		return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
	}

600
	public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
601

602
		// make sure to save all files and that the configuration is up to date
603
		return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() =>
604 605 606
			this.extensionService.onReady().then(() => {
				if (this.model.getProcesses().length === 0) {
					this.removeReplExpressions();
607
					this.allProcesses.clear();
608
				}
609
				this.launchJsonChanged = false;
610
				const manager = this.getConfigurationManager();
611
				const launch = root ? manager.getLaunches().filter(l => l.workspaceUri.toString() === root.toString()).pop() : undefined;
612

613
				let config: debug.IConfig, compound: debug.ICompound;
614
				if (!configOrName) {
615
					configOrName = this.configurationManager.selectedName;
616
				}
617 618 619
				if (typeof configOrName === 'string' && launch) {
					config = launch.getConfiguration(configOrName);
					compound = launch.getCompound(configOrName);
620
				} else if (typeof configOrName !== 'string') {
621 622
					config = configOrName;
				}
623
				if (launch) {
624 625
					// in the drop down the name of the top most compound takes precedence over the launch config name
					manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true);
626
				}
627

628 629 630 631 632
				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.")));
					}
633

634
					return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
635
				}
636 637
				if (configOrName && !config) {
					return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName)));
638 639
				}

R
Ron Buckton 已提交
640
				return manager.getStartSessionCommand(config ? config.type : undefined).then<any>(commandAndType => {
641

642 643 644 645 646 647
					if (!config) {
						// no-folder workspace
						config = <debug.IConfig>{};
						if (commandAndType && commandAndType.type) {
							config.type = commandAndType.type;
						}
648
					}
649

650 651
					if (noDebug) {
						config.noDebug = true;
652 653
					}

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
					return this.configurationManager.resolveDebugConfiguration(launch ? launch.workspaceUri : undefined, config).then(config => {

						// deprecated code: use DebugConfigurationProvider instead of startSessionCommand
						if (commandAndType && commandAndType.command) {
							const defaultConfig = noDebug ? { noDebug: true } : {};
							return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => {
								if (launch) {
									if (result && result.status === 'initialConfiguration') {
										return launch.openConfigFile(false, commandAndType.type);
									}

									if (result && result.status === 'saveConfiguration') {
										return this.fileService.updateContent(launch.uri, result.content).then(() => launch.openConfigFile(false));
									}
								}
								return <TPromise>undefined;
							});
						}
						// end of deprecation
673

674
						if (config.type) {
675 676
							// TODO@AW: handle the 'initialConfiguration' and 'saveConfiguration' cases from above!
							return this.createProcess(root, config);
677
						}
678

679 680 681 682 683 684
						if (launch && commandAndType) {
							return launch.openConfigFile(false, commandAndType.type);
						}

						return undefined;
					});
685 686
				});
			})
B
Benjamin Pasero 已提交
687
		)));
688
	}
689

690 691 692 693 694 695 696 697 698
	public findProcessByUUID(uuid: string): debug.IProcess | null {
		const processes = this.getModel().getProcesses();
		const result = processes.filter(process => process.getId() === uuid);
		if (result.length > 0) {
			return result[0];	// there can only be one
		}
		return null;
	}

699
	public createProcess(root: uri, config: debug.IConfig): TPromise<debug.IProcess> {
700
		return this.textFileService.saveAll().then(() =>
701
			(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
702 703 704 705
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
706

707 708 709 710 711
				if (!this.configurationManager.getAdapter(resolvedConfig.type)) {
					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] }));
				}
J
Johannes Rieken 已提交
712

713 714 715 716 717
				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)) {
718
						return this.doCreateProcess(root, resolvedConfig);
719
					}
I
isidor 已提交
720

721 722 723 724 725 726 727
					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();
728
								return this.doCreateProcess(root, resolvedConfig);
729 730 731 732 733 734 735 736 737
							}),
							this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
							CloseAction
						]
					});
					return undefined;
				}, (err: TaskError) => {
					this.messageService.show(err.severity, {
						message: err.message,
738 739 740 741 742
						actions: [
							this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
							this.taskService.configureAction(),
							CloseAction
						]
743
					});
744 745
				});
			}, err => {
B
Benjamin Pasero 已提交
746
				if (!this.contextService.hasWorkspace()) {
R
Ron Buckton 已提交
747 748
					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 undefined;
749
				}
E
Erich Gamma 已提交
750

751
				return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
752 753 754
					if (openend) {
						this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message));
					}
R
Ron Buckton 已提交
755
					return undefined;
756 757 758 759
				});
			})
		);
	}
760

761
	private doCreateProcess(root: uri, configuration: debug.IConfig, sessionId = generateUuid()): TPromise<debug.IProcess> {
762
		configuration.__sessionId = sessionId;
I
isidor 已提交
763
		this.updateStateAndEmit(sessionId, debug.State.Initializing);
764

J
Joao Moreno 已提交
765
		return this.telemetryService.getTelemetryInfo().then(info => {
766
			const telemetryInfo: { [key: string]: string } = Object.create(null);
767 768
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
769 770
			return telemetryInfo;
		}).then(data => {
771 772 773
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
774
			this.customTelemetryService = null;
775
			let client: TelemetryClient;
776 777

			if (aiKey) {
778
				client = new TelemetryClient(
779 780 781 782
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
783
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
784
						env: {
B
Benjamin Pasero 已提交
785
							ELECTRON_RUN_AS_NODE: 1,
786 787 788
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
789
					}
790
				);
791

792 793
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
794

795
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
796 797
			}

798
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root);
799
			const process = this.model.addProcess(configuration, session);
800
			this.allProcesses.set(process.getId(), process);
801

I
isidor 已提交
802
			this.toDisposeOnSessionEnd.set(session.getId(), []);
803
			if (client) {
I
isidor 已提交
804
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
805
			}
I
isidor 已提交
806
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
807

808
			return session.initialize({
809
				clientID: 'vscode',
J
Joao Moreno 已提交
810 811 812
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
813
				columnsStartAt1: true,
I
isidor 已提交
814
				supportsVariableType: true, // #8858
815 816
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
817
			}).then((result: DebugProtocol.InitializeResponse) => {
818
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
819
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
820
			}).then((result: DebugProtocol.Response) => {
821
				if (session.disconnected) {
822 823
					return TPromise.as(null);
				}
824
				this._onDidNewProcess.fire(process);
I
isidor 已提交
825
				this.focusStackFrameAndEvaluate(null, process);
826 827 828

				const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').internalConsoleOptions;
				if (internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && internalConsoleOptions === 'openOnFirstSessionStart')) {
J
Joao Moreno 已提交
829 830
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
831

B
Benjamin Pasero 已提交
832
				if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.hasWorkspace())) {
J
Joao Moreno 已提交
833 834 835 836
					// 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);
				}
837

J
Joao Moreno 已提交
838 839
				this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
				this.inDebugMode.set(true);
I
isidor 已提交
840
				this.debugType.set(configuration.type);
841 842 843
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
844
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
845

846
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
847 848 849 850
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
851
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
852
					isBuiltin: adapter.extensionDescription.isBuiltin,
I
isidor 已提交
853
					launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root })
I
isidor 已提交
854 855
				});
			}).then(() => process, (error: any) => {
856 857 858 859 860
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

861 862
				const errorMessage = error instanceof Error ? error.message : error;
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
863
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
864 865
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
866
				}
I
isidor 已提交
867 868 869
				if (process) {
					this.model.removeProcess(process.getId());
				}
J
Joao Moreno 已提交
870 871 872 873
				// 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 已提交
874

J
Joao Moreno 已提交
875 876
				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];
877
				this.messageService.show(severity.Error, { message: errorMessage, actions });
878
				return undefined;
J
Joao Moreno 已提交
879
			});
E
Erich Gamma 已提交
880 881 882
		});
	}

I
isidor 已提交
883
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
884
		if (!taskName) {
I
isidor 已提交
885
			return TPromise.as(null);
E
Erich Gamma 已提交
886 887
		}

888
		// run a task before starting a debug session
889 890
		return this.taskService.getTask(taskName).then(task => {
			if (!task) {
891
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
E
Erich Gamma 已提交
892 893
			}

I
isidor 已提交
894 895 896 897 898
			return this.taskService.getActiveTasks().then(tasks => {
				if (tasks.filter(t => t._id === task._id).length) {
					// task is already running - nothing to do.
					return TPromise.as(null);
				}
E
Erich Gamma 已提交
899

I
isidor 已提交
900 901 902 903
				const taskPromise = this.taskService.run(task);
				if (task.isBackground) {
					return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
				}
E
Erich Gamma 已提交
904

I
isidor 已提交
905
				return taskPromise;
E
Erich Gamma 已提交
906 907 908 909
			});
		});
	}

910 911
	public sourceIsNotAvailable(uri: uri): void {
		this.model.sourceIsNotAvailable(uri);
I
isidor 已提交
912 913
	}

914
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
915
		if (process.session.capabilities.supportsRestartRequest) {
916
			return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
917
		}
I
isidor 已提交
918 919
		const focusedProcess = this.viewModel.focusedProcess;
		const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
920

921 922
		return process.session.disconnect(true).then(() => {
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
923
				return this.broadcastService.broadcast({
924 925 926 927 928 929
					channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
					payload: [process.session.root.fsPath]
				});
			}

			return new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
930
				setTimeout(() => {
931 932
					// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
					let config = process.configuration;
933
					if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
934
						this.launchJsonChanged = false;
935
						config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
I
isidor 已提交
936 937 938
						// Take the type from the process since the debug extension might overwrite it #21316
						config.type = process.configuration.type;
						config.noDebug = process.configuration.noDebug;
939
					}
I
isidor 已提交
940
					config.__restart = restartData;
941
					this.createProcess(process.session.root, config).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
942
				}, 300);
943 944
			});
		}).then(() => {
I
isidor 已提交
945 946
			if (preserveFocus) {
				// Restart should preserve the focused process
947
				const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
I
isidor 已提交
948 949 950
				if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, restartedProcess);
				}
951 952
			}
		});
E
Erich Gamma 已提交
953 954
	}

955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
	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 已提交
970
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
971
		const bpsExist = this.model.getBreakpoints().length > 0;
972
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
I
isidor 已提交
973
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
974
			type: process && process.configuration.type,
I
isidor 已提交
975 976 977 978 979
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
980

I
isidor 已提交
981
		this.model.removeProcess(session.getId());
982
		if (process && process.state !== debug.ProcessState.INACTIVE) {
I
isidor 已提交
983 984
			this._onDidEndProcess.fire(process);
		}
I
isidor 已提交
985

I
isidor 已提交
986
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
987 988
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
989 990
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
991
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
992

993 994
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
995
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
996
			this.model.getBreakpoints().forEach(bp => {
997
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
998 999
			});
			this.model.updateBreakpoints(data);
1000

1001
			this.inDebugMode.reset();
I
isidor 已提交
1002
			this.debugType.reset();
1003
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
1004

1005
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
1006 1007
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
1008
		}
E
Erich Gamma 已提交
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
	}

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

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

1019 1020
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
1021 1022
	}

1023 1024 1025
	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 已提交
1026
			// send exception breakpoints at the end since some debug adapters rely on the order
1027
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
1028 1029
	}

1030 1031 1032 1033
	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 已提交
1034 1035 1036 1037 1038
			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 已提交
1039
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
1040 1041
				return TPromise.as(null);
			}
1042

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

1045
			const source = process.sources.get(modelUri.toString());
I
isidor 已提交
1046
			const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) };
I
isidor 已提交
1047 1048 1049

			return session.setBreakpoints({
				source: rawSource,
1050
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1051
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1052 1053 1054 1055 1056
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1057

1058
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1059 1060
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1061 1062 1063 1064
					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 已提交
1065
				}
1066

I
isidor 已提交
1067 1068 1069
				this.model.updateBreakpoints(data);
			});
		};
1070

1071
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1072 1073
	}

1074 1075 1076
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1077
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1078
				return TPromise.as(null);
1079 1080
			}

I
isidor 已提交
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
			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);
			});
		};

1096
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1097 1098
	}

1099 1100 1101 1102
	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 已提交
1103
				return TPromise.as(null);
I
isidor 已提交
1104 1105
			}

I
isidor 已提交
1106 1107 1108 1109
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1110
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1111 1112
	}

1113 1114 1115
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1116
		}
I
isidor 已提交
1117

1118
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1119 1120 1121
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1122
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1123
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1124 1125

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1126 1127
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1128 1129
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
1130 1131 1132
			if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) {
				this.launchJsonChanged = true;
			}
1133
		});
E
Erich Gamma 已提交
1134 1135 1136
	}

	private store(): void {
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
		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 已提交
1170 1171 1172
	}

	public dispose(): void {
I
isidor 已提交
1173
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1174
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1175 1176
	}
}