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

I
isidor 已提交
55 56 57 58 59 60
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 已提交
61

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

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

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

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

113
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
114
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
I
isidor 已提交
115
		this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
I
isidor 已提交
116
		this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
E
Erich Gamma 已提交
117

I
isidor 已提交
118
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
119
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
120
		this.toDispose.push(this.model);
I
isidor 已提交
121
		this.viewModel = new ViewModel(this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, null));
122 123 124 125 126 127 128 129 130 131 132
		this.callStackScheduler = new RunOnceScheduler(() => {
			const focusedThread = this.viewModel.focusedThread;
			if (focusedThread) {
				const callStack = focusedThread.getCallStack();
				// Some adapters might not respect the number levels in StackTraceRequest and might
				// return more stackFrames than requested. For those do not send an additional stackTrace request.
				if (callStack.length <= 1) {
					this.model.fetchCallStack(focusedThread).done(undefined, errors.onUnexpectedError);
				}
			}
		}, 420);
133

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

137 138
	private registerListeners(lifecycleService: ILifecycleService): void {
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
E
Erich Gamma 已提交
139 140

		if (this.taskService) {
A
Alex Dima 已提交
141
			this.toDispose.push(this.taskService.addListener(TaskServiceEvents.Active, (e: TaskEvent) => {
E
Erich Gamma 已提交
142 143
				this.lastTaskEvent = e;
			}));
A
Alex Dima 已提交
144
			this.toDispose.push(this.taskService.addListener(TaskServiceEvents.Inactive, (e: TaskEvent) => {
E
Erich Gamma 已提交
145 146 147 148
				if (e.type === TaskType.SingleRun) {
					this.lastTaskEvent = null;
				}
			}));
A
Alex Dima 已提交
149
			this.toDispose.push(this.taskService.addListener(TaskServiceEvents.Terminated, (e: TaskEvent) => {
E
Erich Gamma 已提交
150 151 152 153
				this.lastTaskEvent = null;
			}));
		}

154 155
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
156

I
isidor 已提交
157
		this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this));
158
		this.toDispose.push(this.configurationService.onDidUpdateConfiguration((event) => {
I
isidor 已提交
159
			if (event.sourceConfig) {
160 161 162 163 164 165 166
				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);
				}
			}
		}));
167 168 169
	}

	private onBroadcast(broadcast: IBroadcast): void {
170

I
isidor 已提交
171
		// attach: PH is ready to be attached to
172 173 174
		// 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
I
isidor 已提交
175
		const process = this.model.getProcesses().filter(p => strings.equalsIgnoreCase(p.configuration.type, 'extensionhost')).pop();
176
		const session = process ? <RawDebugSession>process.session : null;
177
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
178
			this.rawAttach(session, broadcast.payload.port);
179 180
			return;
		}
181

182
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
I
isidor 已提交
183 184 185
			if (session) {
				this.onSessionEnd(session);
			}
186 187 188
			return;
		}

I
isidor 已提交
189
		// from this point on we require an active session
190 191
		if (!session) {
			return;
192 193
		}

B
Benjamin Pasero 已提交
194
		// an extension logged output, show it inside the REPL
195
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
196 197 198 199 200 201 202 203 204 205 206
			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 已提交
207
			// add output for each argument logged
208 209 210 211
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

I
isidor 已提交
212
				// undefined gets printed as 'undefined'
213 214 215 216
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

I
isidor 已提交
217
				// null gets printed as 'null'
218 219 220 221
				else if (a === null) {
					simpleVals.push('null');
				}

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

I
isidor 已提交
225
					// flush any existing simple values logged
226
					if (simpleVals.length) {
I
isidor 已提交
227
						this.model.appendToRepl(simpleVals.join(' '), sev);
228 229 230
						simpleVals = [];
					}

I
isidor 已提交
231
					// show object
I
isidor 已提交
232
					this.model.appendToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
233 234
				}

I
isidor 已提交
235 236
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
237 238 239 240 241 242
				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 已提交
243
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

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

I
isidor 已提交
259
			// flush simple values
260
			// always append a new line for output coming from an extension such that seperate logs go to seperate lines #23695
261
			if (simpleVals.length) {
262
				this.model.appendToRepl(simpleVals.join(' ') + '\n', sev);
263 264
			}
		}
E
Erich Gamma 已提交
265 266
	}

I
isidor 已提交
267
	private registerSessionListeners(process: Process, session: RawDebugSession): void {
I
isidor 已提交
268 269
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => {
270
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
271
			const sendConfigurationDone = () => {
272
				if (session && session.capabilities.supportsConfigurationDoneRequest) {
273
					return session.configurationDone().done(null, e => {
274
						// Disconnect the debug session on configuration done error #10596
275 276
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
277 278 279
						}
						this.messageService.show(severity.Error, e.message);
					});
I
isidor 已提交
280
				}
281
			};
282 283 284

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

I
isidor 已提交
287
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
288
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
289
			const threadId = event.body.threadId;
E
Erich Gamma 已提交
290

291 292 293 294 295
			session.threads().then(response => {
				if (!response || !response.body || !response.body.threads) {
					return;
				}

I
isidor 已提交
296
				const rawThread = response.body.threads.filter(t => t.id === threadId).pop();
297
				this.model.rawUpdate({
298
					sessionId: session.getId(),
I
isidor 已提交
299
					thread: rawThread,
I
isidor 已提交
300
					threadId,
301 302 303
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
304

I
isidor 已提交
305 306
				const thread = process && process.getThread(threadId);
				if (thread) {
307 308 309 310
					// 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(() => {
						const callStack = thread.getCallStack();
311
						this.callStackScheduler.schedule();
312
						if (callStack.length > 0 && !this.viewModel.focusedStackFrame) {
313
							// focus first stack frame from top that has source location if no other stack frame is focussed
I
isidor 已提交
314
							const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, callStack[0]);
315
							this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
I
isidor 已提交
316
							this.windowService.getWindow().focus();
I
isidor 已提交
317
							aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
I
isidor 已提交
318

I
isidor 已提交
319
							return stackFrameToFocus.openInEditor(this.editorService);
I
isidor 已提交
320
						}
321
						return undefined;
I
isidor 已提交
322 323
					});
				}
E
Erich Gamma 已提交
324 325 326
			}, errors.onUnexpectedError);
		}));

I
isidor 已提交
327
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => {
E
Erich Gamma 已提交
328
			if (event.body.reason === 'started') {
329
				this.fetchThreads(session).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
330
			} else if (event.body.reason === 'exited') {
331
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
332 333 334
			}
		}));

I
isidor 已提交
335
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => {
336
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
337
			if (session && session.getId() === event.body.sessionId) {
338 339
				if (event.body && event.body.restart && process) {
					this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message));
340
				} else {
341
					session.disconnect().done(null, errors.onUnexpectedError);
342
				}
E
Erich Gamma 已提交
343 344 345
			}
		}));

I
isidor 已提交
346
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => {
I
isidor 已提交
347
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
348 349 350 351
			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 已提交
352
			this.updateStateAndEmit(session.getId(), debug.State.Running);
I
isidor 已提交
353 354
		}));

I
isidor 已提交
355
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
356 357 358 359
			if (!event.body) {
				return;
			}

360
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
361
			if (event.body.category === 'telemetry') {
362
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
363
				// and the user opted in telemetry
364 365
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
366
				}
367 368 369 370 371 372
			} 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;
373
						this.model.appendToRepl(child, outputSeverity);
I
isidor 已提交
374
					});
375 376 377
				});
			} else if (typeof event.body.output === 'string') {
				this.model.appendToRepl(event.body.output, outputSeverity);
E
Erich Gamma 已提交
378 379 380
			}
		}));

I
isidor 已提交
381
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
382
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
383
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
384
			if (breakpoint) {
I
isidor 已提交
385 386 387
				if (!breakpoint.column) {
					event.body.breakpoint.column = undefined;
				}
I
isidor 已提交
388 389
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
390
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
391 392 393 394 395 396
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
397
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
398
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
399
			const process = this.viewModel.focusedProcess;
I
isidor 已提交
400
			if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
I
isidor 已提交
401
				process && this.contextService.getWorkspace() && process.configuration.noDebug) {
402
				this.windowsService.closeExtensionHostWindow(this.contextService.getWorkspace().resource.fsPath);
403
			}
404
			if (session && session.getId() === event.body.sessionId) {
405
				this.onSessionEnd(session);
406
			}
E
Erich Gamma 已提交
407 408 409
		}));
	}

410 411 412 413 414 415 416 417 418 419 420
	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 已提交
421 422
	}

423 424
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
425
		try {
426
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
427
				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 已提交
428
			});
429 430 431
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
432 433
	}

434 435
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
436
		try {
437
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
438
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
439
			});
440 441 442
		} catch (e) { }

		return result || [];
I
isidor 已提交
443 444
	}

445 446
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
447 448
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
449
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
450
			});
451
		} catch (e) { }
E
Erich Gamma 已提交
452

453
		return result || [];
E
Erich Gamma 已提交
454 455
	}

I
isidor 已提交
456 457
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
458
		try {
J
Johannes Rieken 已提交
459
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
460
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
461
			});
462 463 464
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
465 466
	}

I
isidor 已提交
467
	public get state(): debug.State {
468 469 470 471
		const focusedThread = this.viewModel.focusedThread;
		if (focusedThread && focusedThread.stopped) {
			return debug.State.Stopped;
		}
I
isidor 已提交
472
		const focusedProcess = this.viewModel.focusedProcess;
I
isidor 已提交
473
		if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) {
I
isidor 已提交
474
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
475
		}
I
isidor 已提交
476 477
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
478 479 480
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
481 482
	}

483
	public get onDidChangeState(): Event<debug.State> {
484 485 486
		return this._onDidChangeState.event;
	}

I
isidor 已提交
487 488 489 490 491 492 493
	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 已提交
494
		}
I
isidor 已提交
495 496 497 498

		const state = this.state;
		this.debugState.set(debug.State[state].toLowerCase());
		this._onDidChangeState.fire(state);
E
Erich Gamma 已提交
499 500 501
	}

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

505
	public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess): TPromise<void> {
I
isidor 已提交
506
		if (!process) {
507 508 509 510 511 512 513
			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 已提交
514
		}
I
isidor 已提交
515

516
		this.viewModel.setFocusedStackFrame(stackFrame, process);
I
isidor 已提交
517
		this.updateStateAndEmit();
518

519
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
520 521
	}

I
isidor 已提交
522
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
523 524
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
525
			if (breakpoint instanceof Breakpoint) {
526
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
527
			} else if (breakpoint instanceof FunctionBreakpoint) {
528 529
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
530

531
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
532 533
		}

534 535
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
536 537
	}

538 539 540
	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)));
541

542
		return this.sendBreakpoints(uri);
543 544
	}

545 546
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
547 548
		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 已提交
549

550
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
551
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
552 553
	}

554 555
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
556 557 558
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
559 560
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
561 562
	}

I
isidor 已提交
563
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
564
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
565
		return this.sendFunctionBreakpoints();
I
isidor 已提交
566 567
	}

I
isidor 已提交
568
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
569
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
570
		return this.sendFunctionBreakpoints();
I
isidor 已提交
571 572
	}

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

580 581
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
582 583
	}

584 585
	public logToRepl(value: string, sev = severity.Info): void {
		this.model.appendToRepl(value, sev);
586 587
	}

I
isidor 已提交
588
	public addWatchExpression(name: string): TPromise<void> {
589
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
590 591
	}

I
isidor 已提交
592
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
593
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
594 595
	}

I
isidor 已提交
596 597 598 599
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

600 601
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
602 603
	}

604
	public startDebugging(configName?: string, noDebug = false): TPromise<any> {
605 606 607 608 609
		// make sure to save all files and that the configuration is up to date
		return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() =>
			this.extensionService.onReady().then(() => {
				if (this.model.getProcesses().length === 0) {
					this.removeReplExpressions();
610
				}
611 612 613 614 615 616 617 618 619
				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.")));
					}
620

621 622 623 624
					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)));
625 626
				}

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
				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));
								}
642
							}
643 644 645
							return undefined;
						});
					}
646

647 648 649 650 651 652
					if (config) {
						return this.createProcess(config);
					}
					if (this.contextService.getWorkspace() && commandAndType) {
						return manager.openConfigFile(false, commandAndType.type);
					}
653

654 655 656 657
					return undefined;
				});
			})
		));
658
	}
659

660
	public createProcess(config: debug.IConfig): TPromise<any> {
661 662 663 664 665 666
		return this.textFileService.saveAll().then(() =>
			this.configurationManager.resloveConfiguration(config).then(resolvedConfig => {
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
667

668 669 670 671 672
				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 已提交
673

674 675 676 677 678 679 680
				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)) {
						return this.doCreateProcess(resolvedConfig);
					}
I
isidor 已提交
681

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
					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();
								return this.doCreateProcess(resolvedConfig);
							}),
							this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
							CloseAction
						]
					});
					return undefined;
				}, (err: TaskError) => {
					this.messageService.show(err.severity, {
						message: err.message,
699 700 701 702 703
						actions: [
							this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
							this.taskService.configureAction(),
							CloseAction
						]
704
					});
705 706 707 708 709
				});
			}, 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."));
				}
E
Erich Gamma 已提交
710

711 712 713 714 715 716 717 718
				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));
					}
				});
			})
		);
	}
719 720 721

	private doCreateProcess(configuration: debug.IConfig): TPromise<any> {
		const sessionId = generateUuid();
I
isidor 已提交
722
		this.updateStateAndEmit(sessionId, debug.State.Initializing);
723

J
Joao Moreno 已提交
724
		return this.telemetryService.getTelemetryInfo().then(info => {
725
			const telemetryInfo: { [key: string]: string } = Object.create(null);
726 727
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
728 729
			return telemetryInfo;
		}).then(data => {
730 731 732
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
733
			this.customTelemetryService = null;
734
			let client: TelemetryClient;
735 736

			if (aiKey) {
737
				client = new TelemetryClient(
738 739 740 741
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
742
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
743
						env: {
B
Benjamin Pasero 已提交
744
							ELECTRON_RUN_AS_NODE: 1,
745 746 747
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
748
					}
749
				);
750

751 752
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
753

754
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
755 756
			}

757
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService);
758
			const process = this.model.addProcess(configuration, session);
759

I
isidor 已提交
760
			this.toDisposeOnSessionEnd.set(session.getId(), []);
761
			if (client) {
I
isidor 已提交
762
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
763
			}
I
isidor 已提交
764
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
765

766
			return session.initialize({
767
				clientID: 'vscode',
J
Joao Moreno 已提交
768 769 770
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
771
				columnsStartAt1: true,
I
isidor 已提交
772
				supportsVariableType: true, // #8858
773 774
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
775
			}).then((result: DebugProtocol.InitializeResponse) => {
776
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
777
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
778
			}).then((result: DebugProtocol.Response) => {
779
				if (session.disconnected) {
780 781
					return TPromise.as(null);
				}
782 783 784
				if (!this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, process);
				}
785 786 787

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

I
isidor 已提交
791
				if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.getWorkspace())) {
J
Joao Moreno 已提交
792 793 794 795
					// 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);
				}
796

J
Joao Moreno 已提交
797 798
				this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
				this.inDebugMode.set(true);
I
isidor 已提交
799
				this.debugType.set(configuration.type);
800 801 802
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
803
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
804

805
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
806 807 808 809
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
810
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
811 812
					isBuiltin: adapter.extensionDescription.isBuiltin,
					launchJsonExists: !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch')
J
Joao Moreno 已提交
813 814
				});
			}).then(undefined, (error: any) => {
815 816 817 818 819
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

820 821
				const errorMessage = error instanceof Error ? error.message : error;
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
822
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
823 824
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
825 826 827 828 829
				}
				// 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 已提交
830

J
Joao Moreno 已提交
831 832
				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];
833
				this.messageService.show(severity.Error, { message: errorMessage, actions });
834
				return undefined;
J
Joao Moreno 已提交
835
			});
E
Erich Gamma 已提交
836 837 838
		});
	}

I
isidor 已提交
839
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
840
		if (!taskName) {
I
isidor 已提交
841
			return TPromise.as(null);
E
Erich Gamma 已提交
842 843
		}

844
		// run a task before starting a debug session
E
Erich Gamma 已提交
845
		return this.taskService.tasks().then(descriptions => {
846
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
847
			if (filteredTasks.length !== 1) {
848
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
E
Erich Gamma 已提交
849 850
			}

I
isidor 已提交
851
			// task is already running - nothing to do.
852
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
853
				return TPromise.as(null);
E
Erich Gamma 已提交
854 855 856
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
857
				// there is a different task running currently.
I
isidor 已提交
858
				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 已提交
859 860
			}

I
isidor 已提交
861
			// no task running, execute the preLaunchTask.
862
			const taskPromise = this.taskService.run(filteredTasks[0]).then(result => {
E
Erich Gamma 已提交
863
				this.lastTaskEvent = null;
I
isidor 已提交
864
				return result;
E
Erich Gamma 已提交
865 866 867
			}, err => {
				this.lastTaskEvent = null;
			});
868

869
			if (filteredTasks[0].isBackground) {
A
Alex Dima 已提交
870
				return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
871 872 873
			}

			return taskPromise;
E
Erich Gamma 已提交
874 875 876
		});
	}

877 878 879
	private rawAttach(session: RawDebugSession, port: number): TPromise<any> {
		if (session) {
			return session.attach({ port });
I
isidor 已提交
880 881
		}

882 883 884 885
		const config = this.configurationManager.getConfiguration(this.viewModel.selectedConfigurationName);
		return this.configurationManager.resloveConfiguration(config).then(resolvedConfig => {
			resolvedConfig.request = 'attach';
			resolvedConfig.port = port;
I
isidor 已提交
886
			this.doCreateProcess(resolvedConfig);
I
isidor 已提交
887
		});
I
isidor 已提交
888 889
	}

I
isidor 已提交
890 891 892 893
	public deemphasizeSource(uri: uri): void {
		this.model.deemphasizeSource(uri);
	}

894
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
895
		if (process.session.capabilities.supportsRestartRequest) {
896
			return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
897
		}
I
isidor 已提交
898 899
		const focusedProcess = this.viewModel.focusedProcess;
		const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
900 901

		return process.session.disconnect(true).then(() =>
902
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
903
				setTimeout(() => {
904 905
					// 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);
906 907 908
					if (config) {
						// Take the type from the process since the debug extension might overwrite it #21316
						config.type = process.configuration.type;
I
isidor 已提交
909
						config.noDebug = process.configuration.noDebug;
910
						config.__restart = restartData;
911
					}
912
					this.createProcess(config || process.configuration).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
913
				}, 300);
I
isidor 已提交
914
			})
915
		).then(() => {
I
isidor 已提交
916 917
			if (preserveFocus) {
				// Restart should preserve the focused process
918
				const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
I
isidor 已提交
919 920 921
				if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, restartedProcess);
				}
922 923
			}
		});
E
Erich Gamma 已提交
924 925
	}

926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
	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 已提交
941
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
942
		const bpsExist = this.model.getBreakpoints().length > 0;
943
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
I
isidor 已提交
944
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
945
			type: process && process.configuration.type,
I
isidor 已提交
946 947 948 949 950
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
951

I
isidor 已提交
952
		this.model.removeProcess(session.getId());
I
isidor 已提交
953

I
isidor 已提交
954
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
955 956
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
957 958
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
959
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
960

961 962
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
963
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
964
			this.model.getBreakpoints().forEach(bp => {
965
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
966 967
			});
			this.model.updateBreakpoints(data);
968

969
			this.inDebugMode.reset();
I
isidor 已提交
970
			this.debugType.reset();
971
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
972

973
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
974 975
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
976
		}
E
Erich Gamma 已提交
977 978 979 980 981 982 983 984 985 986
	}

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

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

987 988
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
989 990
	}

991 992 993
	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 已提交
994
			// send exception breakpoints at the end since some debug adapters rely on the order
995
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
996 997
	}

998 999 1000 1001
	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 已提交
1002 1003 1004 1005 1006
			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 已提交
1007
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
1008 1009
				return TPromise.as(null);
			}
1010

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

1013
			const source = process.sources.get(modelUri.toString());
I
isidor 已提交
1014
			const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) };
I
isidor 已提交
1015 1016 1017

			return session.setBreakpoints({
				source: rawSource,
1018
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1019
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1020 1021 1022 1023 1024
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1025

1026
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1027 1028
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1029 1030 1031 1032
					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 已提交
1033
				}
1034

I
isidor 已提交
1035 1036 1037
				this.model.updateBreakpoints(data);
			});
		};
1038

1039
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1040 1041
	}

1042 1043 1044
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1045
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1046
				return TPromise.as(null);
1047 1048
			}

I
isidor 已提交
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
			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);
			});
		};

1064
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1065 1066
	}

1067 1068 1069 1070
	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 已提交
1071
				return TPromise.as(null);
I
isidor 已提交
1072 1073
			}

I
isidor 已提交
1074 1075 1076 1077
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1078
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1079 1080
	}

1081 1082 1083
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1084
		}
I
isidor 已提交
1085

1086
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1087 1088 1089
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1090
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1091
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1092 1093

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1094 1095
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1096 1097 1098
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
		});
E
Erich Gamma 已提交
1099 1100 1101 1102 1103
	}

	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);
1104
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1105
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1106
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.viewModel.selectedConfigurationName, StorageScope.WORKSPACE);
1107
		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 已提交
1108 1109 1110
	}

	public dispose(): void {
I
isidor 已提交
1111
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1112
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1113 1114
	}
}