debugService.ts 44.5 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import nls = require('vs/nls');
import lifecycle = require('vs/base/common/lifecycle');
J
Johannes Rieken 已提交
8 9
import { guessMimeTypes } from 'vs/base/common/mime';
import Event, { Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
10
import uri from 'vs/base/common/uri';
J
Johannes Rieken 已提交
11 12
import { RunOnceScheduler } from 'vs/base/common/async';
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
13
import arrays = require('vs/base/common/arrays');
14
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
15 16
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
17
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
18
import aria = require('vs/base/browser/ui/aria/aria');
19
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
E
Erich Gamma 已提交
20
import editorbrowser = require('vs/editor/browser/editorBrowser');
J
Johannes Rieken 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33 34
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';
import { IFileService, FileChangesEvent, FileChangeType, EventType } from 'vs/platform/files/common/files';
import { IEventService } from 'vs/platform/event/common/event';
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { asFileEditorInput } from 'vs/workbench/common/editor';
E
Erich Gamma 已提交
35
import debug = require('vs/workbench/parts/debug/common/debug');
J
Johannes Rieken 已提交
36
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
E
Erich Gamma 已提交
37
import model = require('vs/workbench/parts/debug/common/debugModel');
J
Johannes Rieken 已提交
38
import { DebugStringEditorInput, DebugErrorEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
E
Erich Gamma 已提交
39
import viewmodel = require('vs/workbench/parts/debug/common/debugViewModel');
40
import debugactions = require('vs/workbench/parts/debug/browser/debugActions');
J
Johannes Rieken 已提交
41 42 43 44
import { ConfigurationManager } from 'vs/workbench/parts/debug/node/debugConfigurationManager';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { ITaskService, TaskEvent, TaskType, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { TaskError, TaskErrors } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
45
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
J
Johannes Rieken 已提交
46 47 48 49 50 51 52 53
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
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';
import { IWindowService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService';
54
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
J
Johannes Rieken 已提交
55
import { ipcRenderer as ipc } from 'electron';
E
Erich Gamma 已提交
56

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

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

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

	constructor(
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
82 83
		@ITextFileService private textFileService: ITextFileService,
		@IViewletService private viewletService: IViewletService,
I
isidor 已提交
84
		@IPanelService private panelService: IPanelService,
E
Erich Gamma 已提交
85 86 87 88 89 90
		@IFileService private fileService: IFileService,
		@IMessageService private messageService: IMessageService,
		@IPartService private partService: IPartService,
		@IWindowService private windowService: IWindowService,
		@ITelemetryService private telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
91
		@IContextKeyService contextKeyService: IContextKeyService,
92
		@IEditorGroupService private editorGroupService: IEditorGroupService,
93
		@IEventService eventService: IEventService,
I
isidor 已提交
94
		@ILifecycleService lifecycleService: ILifecycleService,
I
isidor 已提交
95
		@IInstantiationService private instantiationService: IInstantiationService,
A
Alex Dima 已提交
96
		@IExtensionService private extensionService: IExtensionService,
97
		@IMarkerService private markerService: IMarkerService,
98 99
		@ITaskService private taskService: ITaskService,
		@IConfigurationService private configurationService: IConfigurationService
E
Erich Gamma 已提交
100 101
	) {
		this.toDispose = [];
102
		this.toDisposeOnSessionEnd = {};
103
		this.breakpointsToSendOnResourceSaved = {};
I
isidor 已提交
104
		this._state = debug.State.Inactive;
105
		this._onDidChangeState = new Emitter<debug.State>();
E
Erich Gamma 已提交
106

107
		if (!this.contextService.getWorkspace()) {
I
isidor 已提交
108
			this._state = debug.State.Disabled;
E
Erich Gamma 已提交
109
		}
110
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, 'null'));
111
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
E
Erich Gamma 已提交
112

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

E
Erich Gamma 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
		this.registerListeners(eventService, lifecycleService);
	}

	private registerListeners(eventService: IEventService, lifecycleService: ILifecycleService): void {
		this.toDispose.push(eventService.addListener2(EventType.FILE_CHANGES, (e: FileChangesEvent) => this.onFileChanges(e)));

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

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

I
isidor 已提交
141
		this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this));
142 143 144
	}

	private onBroadcast(broadcast: IBroadcast): void {
145

I
isidor 已提交
146
		// attach: PH is ready to be attached to
147 148 149 150
		// 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
		const session = <RawDebugSession>this.model.getProcesses().map(p => p.session).filter(s => s.configuration.type === 'extensionHost').pop();
151
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
152
			this.rawAttach(session, broadcast.payload.port);
153 154
			return;
		}
155

156
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
157
			this.onSessionEnd(session);
158 159 160
			return;
		}

I
isidor 已提交
161
		// from this point on we require an active session
162 163
		if (!session) {
			return;
164 165
		}

I
isidor 已提交
166
		// a plugin logged output, show it inside the REPL
167
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
168 169 170 171 172 173 174 175 176 177 178
			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 已提交
179
			// add output for each argument logged
180 181 182 183
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

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

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

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

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

I
isidor 已提交
203
					// show object
204 205 206
					this.logToRepl(a, sev);
				}

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

					simpleVals.push(buf);
				}

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

I
isidor 已提交
231
			// flush simple values
232 233 234 235
			if (simpleVals.length) {
				this.logToRepl(simpleVals.join(' '), sev);
			}
		}
E
Erich Gamma 已提交
236 237
	}

238
	private registerSessionListeners(session: RawDebugSession): void {
239 240
		this.toDisposeOnSessionEnd[session.getId()].push(session);
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidInitialize(event => {
241
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
242
			const sendConfigurationDone = () => {
243 244
				if (session && session.configuration.capabilities.supportsConfigurationDoneRequest) {
					session.configurationDone().done(null, e => {
245
						// Disconnect the debug session on configuration done error #10596
246 247
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
248 249 250
						}
						this.messageService.show(severity.Error, e.message);
					});
I
isidor 已提交
251
				}
252 253
			};

I
isidor 已提交
254
			this.sendAllBreakpoints(session).done(sendConfigurationDone, sendConfigurationDone);
I
isidor 已提交
255
		}));
E
Erich Gamma 已提交
256

257
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidStop(event => {
258
			this.setStateAndEmit(debug.State.Stopped);
I
isidor 已提交
259
			const threadId = event.body.threadId;
E
Erich Gamma 已提交
260

261
			this.getThreadData(session).done(() => {
262
				this.model.rawUpdate({
263
					sessionId: session.getId(),
I
isidor 已提交
264
					threadId,
265 266 267
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
268

I
isidor 已提交
269 270
				const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
				const thread = process.getThread(threadId);
271
				thread.getCallStack().then(callStack => {
E
Erich Gamma 已提交
272
					if (callStack.length > 0) {
273
						// focus first stack frame from top that has source location
I
isidor 已提交
274
						const stackFrameToFocus = arrays.first(callStack, sf => sf.source && sf.source.available, callStack[0]);
275
						this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
I
isidor 已提交
276
						this.windowService.getWindow().focus();
277 278
						aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.lineNumber));

279
						return this.openOrRevealSource(stackFrameToFocus.source, stackFrameToFocus.lineNumber, false, false);
E
Erich Gamma 已提交
280
					} else {
281
						this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
282 283 284 285 286
					}
				});
			}, errors.onUnexpectedError);
		}));

287
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidThread(event => {
E
Erich Gamma 已提交
288
			if (event.body.reason === 'started') {
289
				this.getThreadData(session).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
290
			} else if (event.body.reason === 'exited') {
291
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
292 293 294
			}
		}));

295
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidTerminateDebugee(event => {
296
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
297
			if (session && session.getId() === event.body.sessionId) {
298
				if (event.body && typeof event.body.restart === 'boolean' && event.body.restart) {
299
					this.restartSession(session).done(null, err => this.messageService.show(severity.Error, err.message));
300
				} else {
301
					session.disconnect().done(null, errors.onUnexpectedError);
302
				}
E
Erich Gamma 已提交
303 304 305
			}
		}));

306
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidContinued(event => {
307
			this.lazyTransitionToRunningState(session, event.body.allThreadsContinued ? undefined : event.body.threadId);
I
isidor 已提交
308 309
		}));

310
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidOutput(event => {
311
			if (event.body && event.body.category === 'telemetry') {
312
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
313
				// and the user opted in telemetry
314 315
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
316
				}
317
			} else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) {
E
Erich Gamma 已提交
318 319 320 321
				this.onOutput(event);
			}
		}));

322
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidBreakpoint(event => {
I
isidor 已提交
323
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
324
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
325 326 327
			if (breakpoint) {
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
328
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
329 330 331 332 333 334
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

335
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidExitAdapter(event => {
336
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
337
			if (session && session.configuration.type === 'extensionHost' && this._state === debug.State.RunningNoDebug) {
338 339
				ipc.send('vscode:closeExtensionHostWindow', this.contextService.getWorkspace().resource.fsPath);
			}
340
			if (session && session.getId() === event.body.sessionId) {
341
				this.onSessionEnd(session);
342
			}
E
Erich Gamma 已提交
343 344 345 346
		}));
	}

	private onOutput(event: DebugProtocol.OutputEvent): void {
347
		const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
E
Erich Gamma 已提交
348 349 350
		this.appendReplOutput(event.body.output, outputSeverity);
	}

I
isidor 已提交
351
	private getThreadData(session: RawDebugSession): TPromise<void> {
352
		return session.threads().then(response => {
353
			if (response && response.body && response.body.threads) {
354
				response.body.threads.forEach(thread => this.model.rawUpdate({ sessionId: session.getId(), threadId: thread.id, thread }));
355
			}
I
isidor 已提交
356
		});
E
Erich Gamma 已提交
357 358 359
	}

	private loadBreakpoints(): debug.IBreakpoint[] {
360
		let result: debug.IBreakpoint[];
E
Erich Gamma 已提交
361
		try {
362
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
363
				return new model.Breakpoint(new Source(breakpoint.source.raw ? breakpoint.source.raw : { path: uri.parse(breakpoint.source.uri).fsPath, name: breakpoint.source.name }),
364
					breakpoint.desiredLineNumber || breakpoint.lineNumber, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition);
E
Erich Gamma 已提交
365
			});
366 367 368
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
369 370
	}

I
isidor 已提交
371
	private loadFunctionBreakpoints(): debug.IFunctionBreakpoint[] {
372
		let result: debug.IFunctionBreakpoint[];
I
isidor 已提交
373
		try {
374
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
375
				return new model.FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
376
			});
377 378 379
		} catch (e) { }

		return result || [];
I
isidor 已提交
380 381
	}

E
Erich Gamma 已提交
382
	private loadExceptionBreakpoints(): debug.IExceptionBreakpoint[] {
383
		let result: debug.IExceptionBreakpoint[];
E
Erich Gamma 已提交
384 385
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
386
				return new model.ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
387
			});
388
		} catch (e) { }
E
Erich Gamma 已提交
389

390
		return result || [];
E
Erich Gamma 已提交
391 392 393
	}

	private loadWatchExpressions(): model.Expression[] {
394
		let result: model.Expression[];
E
Erich Gamma 已提交
395
		try {
J
Johannes Rieken 已提交
396
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
397
				return new model.Expression(watchStoredData.name, false, watchStoredData.id);
E
Erich Gamma 已提交
398
			});
399 400 401
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
402 403
	}

I
isidor 已提交
404 405
	public get state(): debug.State {
		return this._state;
E
Erich Gamma 已提交
406 407
	}

408 409 410 411
	public get onDidChangeState(): Event<debug.State> {
		return this._onDidChangeState.event;
	}

412
	private setStateAndEmit(newState: debug.State): void {
I
isidor 已提交
413
		this._state = newState;
414
		this._onDidChangeState.fire(newState);
E
Erich Gamma 已提交
415 416 417 418 419 420
	}

	public get enabled(): boolean {
		return !!this.contextService.getWorkspace();
	}

421
	public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise<void> {
422
		this.viewModel.setFocusedStackFrame(focusedStackFrame);
E
Erich Gamma 已提交
423
		if (focusedStackFrame) {
424
			return this.model.evaluateWatchExpressions(focusedStackFrame);
E
Erich Gamma 已提交
425 426
		} else {
			this.model.clearWatchExpressionValues();
I
isidor 已提交
427
			return TPromise.as(null);
E
Erich Gamma 已提交
428 429 430
		}
	}

I
isidor 已提交
431
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
432 433 434
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
			if (breakpoint instanceof model.Breakpoint) {
I
isidor 已提交
435
				return this.sendBreakpoints((<model.Breakpoint>breakpoint).source.uri);
436 437 438
			} else if (breakpoint instanceof model.FunctionBreakpoint) {
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
439

440
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
441 442
		}

443 444
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
445 446
	}

I
isidor 已提交
447
	public addBreakpoints(rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
448 449
		this.model.addBreakpoints(rawBreakpoints);
		const uris = arrays.distinct(rawBreakpoints, raw => raw.uri.toString()).map(raw => raw.uri);
450
		rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, rbp.uri.fsPath)));
451

I
isidor 已提交
452
		return TPromise.join(uris.map(uri => this.sendBreakpoints(uri))).then(() => void 0);
453 454
	}

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

460
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
461
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
462 463
	}

464 465
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
466 467 468
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
469 470
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
471 472
	}

I
isidor 已提交
473
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
474
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
475
		return this.sendFunctionBreakpoints();
I
isidor 已提交
476 477
	}

I
isidor 已提交
478
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
479
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
480
		return this.sendFunctionBreakpoints();
I
isidor 已提交
481 482
	}

I
isidor 已提交
483
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
484
		this.telemetryService.publicLog('debugService/addReplExpression');
485
		return this.model.addReplExpression(this.viewModel.focusedStackFrame, name)
486
			// Evaluate all watch expressions again since repl evaluation might have changed some.
487
			.then(() => this.setFocusedStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
E
Erich Gamma 已提交
488 489
	}

490
	public logToRepl(value: string | { [key: string]: any }, severity?: severity): void {
E
Erich Gamma 已提交
491 492 493 494 495 496 497
		this.model.logToRepl(value, severity);
	}

	public appendReplOutput(value: string, severity?: severity): void {
		this.model.appendReplOutput(value, severity);
	}

498 499
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
500 501
	}

I
isidor 已提交
502
	public addWatchExpression(name: string): TPromise<void> {
503
		return this.model.addWatchExpression(this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
504 505
	}

I
isidor 已提交
506
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
507
		return this.model.renameWatchExpression(this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
508 509
	}

510 511
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
512 513
	}

I
isidor 已提交
514
	public createSession(noDebug: boolean, configuration?: debug.IConfig): TPromise<any> {
515
		this.removeReplExpressions();
E
Erich Gamma 已提交
516

517
		return this.textFileService.saveAll()							// make sure all dirty files are saved
518
			.then(() => this.configurationService.reloadConfiguration()	// make sure configuration is up to date
J
Johannes Rieken 已提交
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
				.then(() => this.extensionService.onReady()
					.then(() => this.configurationManager.setConfiguration(configuration || this.configurationManager.configurationName)
						.then(() => this.configurationManager.resolveInteractiveVariables())
						.then(resolvedConfiguration => {
							configuration = resolvedConfiguration;
							if (!configuration) {
								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."));
									}
								});
							}
							if (configuration.silentlyAbort) {
								return;
							}

							configuration.noDebug = noDebug;
							if (!this.configurationManager.adapter) {
								return configuration.type ? TPromise.wrapError(new Error(nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", configuration.type)))
									: TPromise.wrapError(errors.create(nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."),
										{ actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
							}

							return this.runPreLaunchTask(configuration.preLaunchTask).then((taskSummary: ITaskSummary) => {
								const errorCount = configuration.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.doCreateSession(configuration);
								}

								this.messageService.show(severity.Error, {
									message: errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", configuration.preLaunchTask) :
										errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", configuration.preLaunchTask) :
											nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", configuration.preLaunchTask, taskSummary.exitCode),
									actions: [new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
										this.messageService.hideAll();
										return this.doCreateSession(configuration);
									}), CloseAction]
								});
							}, (err: TaskError) => {
								if (err.code !== TaskErrors.NotConfigured) {
									throw err;
								}

								this.messageService.show(err.severity, {
									message: err.message,
									actions: [this.taskService.configureAction(), CloseAction]
								});
							});
						}))));
I
isidor 已提交
570
	}
E
Erich Gamma 已提交
571

I
isidor 已提交
572
	private doCreateSession(configuration: debug.IExtHostConfig): TPromise<any> {
573
		this.setStateAndEmit(debug.State.Initializing);
574

J
Joao Moreno 已提交
575
		return this.telemetryService.getTelemetryInfo().then(info => {
576
			const telemetryInfo: { [key: string]: string } = Object.create(null);
577 578
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
579 580
			return telemetryInfo;
		}).then(data => {
581
			const { aiKey, type } = this.configurationManager.adapter;
582
			const publisher = this.configurationManager.adapter.extensionDescription.publisher;
583
			this.customTelemetryService = null;
584
			let client: TelemetryClient;
585 586

			if (aiKey) {
587
				client = new TelemetryClient(
588 589 590 591
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
592
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
593
						env: {
B
Benjamin Pasero 已提交
594
							ELECTRON_RUN_AS_NODE: 1,
595 596 597
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
598
					}
599
				);
600

601 602
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
603

604
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
605 606
			}

607
			const session = this.instantiationService.createInstance(RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.customTelemetryService);
608
			this.model.addProcess(session);
609 610 611 612
			this.toDisposeOnSessionEnd[session.getId()] = [];
			if (client) {
				this.toDisposeOnSessionEnd[session.getId()].push(client);
			}
613
			this.registerSessionListeners(session);
J
Joao Moreno 已提交
614

615
			return session.initialize({
J
Joao Moreno 已提交
616 617 618
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
619
				columnsStartAt1: true,
I
isidor 已提交
620
				supportsVariableType: true, // #8858
621 622
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
623
			}).then((result: DebugProtocol.InitializeResponse) => {
624
				if (session.disconnected) {
J
Joao Moreno 已提交
625 626
					return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
				}
627

628 629
				this.model.setExceptionBreakpoints(session.configuration.capabilities.exceptionBreakpointFilters);
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
630
			}).then((result: DebugProtocol.Response) => {
631
				if (session.disconnected) {
632 633 634
					return TPromise.as(null);
				}

J
Joao Moreno 已提交
635 636 637
				if (configuration.internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && configuration.internalConsoleOptions !== 'neverOpen')) {
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
638

J
Joao Moreno 已提交
639 640 641 642 643
				if (!this.viewModel.changedWorkbenchViewState && !this.partService.isSideBarHidden()) {
					// 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);
				}
644

J
Joao Moreno 已提交
645 646 647 648 649 650
				// Do not change status bar to orange if we are just running without debug.
				if (!configuration.noDebug) {
					this.partService.addClass('debugging');
				}
				this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
				this.inDebugMode.set(true);
651
				this.lazyTransitionToRunningState(session);
J
Joao Moreno 已提交
652 653 654 655 656 657

				this.telemetryService.publicLog('debugSessionStart', {
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
J
Johannes Rieken 已提交
658
					extensionName: `${this.configurationManager.adapter.extensionDescription.publisher}.${this.configurationManager.adapter.extensionDescription.name}`,
J
Joao Moreno 已提交
659 660 661
					isBuiltin: this.configurationManager.adapter.extensionDescription.isBuiltin
				});
			}).then(undefined, (error: any) => {
662 663 664 665 666
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

J
Joao Moreno 已提交
667 668
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
				this.setStateAndEmit(debug.State.Inactive);
669 670
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
671 672 673 674 675
				}
				// 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 已提交
676

J
Joao Moreno 已提交
677 678 679 680
				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];
				return TPromise.wrapError(errors.create(error.message, { actions }));
			});
E
Erich Gamma 已提交
681 682 683
		});
	}

I
isidor 已提交
684
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
685
		if (!taskName) {
I
isidor 已提交
686
			return TPromise.as(null);
E
Erich Gamma 已提交
687 688
		}

689
		// run a task before starting a debug session
E
Erich Gamma 已提交
690
		return this.taskService.tasks().then(descriptions => {
691
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
692
			if (filteredTasks.length !== 1) {
693 694
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName), {
					actions: [
695
						this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
696
						this.taskService.configureAction(),
697
						CloseAction
698 699
					]
				}));
E
Erich Gamma 已提交
700 701
			}

I
isidor 已提交
702
			// task is already running - nothing to do.
703
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
704
				return TPromise.as(null);
E
Erich Gamma 已提交
705 706 707
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
708
				// there is a different task running currently.
I
isidor 已提交
709
				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 已提交
710 711
			}

I
isidor 已提交
712
			// no task running, execute the preLaunchTask.
713
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
714
				this.lastTaskEvent = null;
I
isidor 已提交
715
				return result;
E
Erich Gamma 已提交
716 717 718
			}, err => {
				this.lastTaskEvent = null;
			});
719

720
			if (filteredTasks[0].isWatching) {
A
Alex Dima 已提交
721
				return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
722 723 724
			}

			return taskPromise;
E
Erich Gamma 已提交
725 726 727
		});
	}

728 729 730
	private rawAttach(session: RawDebugSession, port: number): TPromise<any> {
		if (session) {
			return session.attach({ port });
I
isidor 已提交
731 732
		}

I
isidor 已提交
733
		this.setStateAndEmit(debug.State.Initializing);
I
isidor 已提交
734
		const configuration = <debug.IExtHostConfig>this.configurationManager.configuration;
I
isidor 已提交
735
		return this.doCreateSession({
736
			type: configuration.type,
I
isidor 已提交
737
			request: 'attach',
738 739
			port,
			sourceMaps: configuration.sourceMaps,
740 741
			outDir: configuration.outDir,
			debugServer: configuration.debugServer
I
isidor 已提交
742
		});
I
isidor 已提交
743 744
	}

745 746
	public restartSession(session: debug.ISession): TPromise<any> {
		return session ? session.disconnect(true).then(() =>
747
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
748
				setTimeout(() => {
I
isidor 已提交
749
					this.createSession(false, null).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
750
				}, 300);
I
isidor 已提交
751
			})
I
isidor 已提交
752
		) : this.createSession(false, null);
E
Erich Gamma 已提交
753 754
	}

I
isidor 已提交
755
	private onSessionEnd(session: RawDebugSession): void {
756
		if (session) {
I
isidor 已提交
757
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
758
			this.telemetryService.publicLog('debugSessionStop', {
759 760 761
				type: session.configuration.type,
				success: session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: session.getLengthInSeconds(),
I
isidor 已提交
762 763 764
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
765
		}
766

I
isidor 已提交
767
		try {
768
			this.toDisposeOnSessionEnd[session.getId()] = lifecycle.dispose(this.toDisposeOnSessionEnd[session.getId()]);
I
isidor 已提交
769 770 771 772
		} catch (e) {
			// an internal module might be open so the dispose can throw -> ignore and continue with stop session.
		}

E
Erich Gamma 已提交
773
		this.partService.removeClass('debugging');
774

775
		this.model.removeProcess(session.getId());
I
isidor 已提交
776
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
777 778
		this.setStateAndEmit(debug.State.Inactive);

I
isidor 已提交
779
		// set breakpoints back to unverified since the session ended.
780
		// source reference changes across sessions, so we do not use it to persist the source.
I
isidor 已提交
781
		const data: { [id: string]: { line: number, verified: boolean } } = {};
782 783 784 785
		this.model.getBreakpoints().forEach(bp => {
			delete bp.source.raw.sourceReference;
			data[bp.getId()] = { line: bp.lineNumber, verified: false };
		});
786 787
		this.model.updateBreakpoints(data);

E
Erich Gamma 已提交
788
		this.inDebugMode.reset();
I
isidor 已提交
789 790 791 792

		if (!this.partService.isSideBarHidden() && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
			this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
		}
E
Erich Gamma 已提交
793 794 795 796 797 798 799 800 801 802
	}

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

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

803
	public openOrRevealSource(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
804
		const visibleEditors = this.editorService.getVisibleEditors();
I
isidor 已提交
805
		for (let i = 0; i < visibleEditors.length; i++) {
I
isidor 已提交
806
			const fileInput = asFileEditorInput(visibleEditors[i].input);
807 808 809
			if ((fileInput && fileInput.getResource().toString() === source.uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === source.uri.toString())) {

810 811 812 813
				const control = <editorbrowser.ICodeEditor>visibleEditors[i].getControl();
				if (control) {
					control.revealLineInCenterIfOutsideViewport(lineNumber);
					control.setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 });
814
					this.editorGroupService.activateGroup(i);
I
isidor 已提交
815
					if (!preserveFocus) {
816
						this.editorGroupService.focusGroup(i);
I
isidor 已提交
817
					}
E
Erich Gamma 已提交
818
				}
819

A
Alex Dima 已提交
820
				return TPromise.as(null);
E
Erich Gamma 已提交
821 822 823
			}
		}

824
		const process = this.viewModel.focusedProcess;
E
Erich Gamma 已提交
825
		if (source.inMemory) {
I
isidor 已提交
826
			// internal module
827 828
			if (source.reference !== 0 && process && source.available) {
				return process.session.source({ sourceReference: source.reference }).then(response => {
829 830
					const mime = response && response.body && response.body.mimeType ? response.body.mimeType : guessMimeTypes(source.name)[0];
					const inputValue = response && response.body ? response.body.content : '';
831
					return this.getDebugStringEditorInput(process, source, inputValue, mime);
832 833
				}, (err: DebugProtocol.ErrorResponse) => {
					// Display the error from debug adapter using a temporary editor #8836
834
					return this.getDebugErrorEditorInput(process, source, err.message);
835
				}).then(editorInput => {
B
Benjamin Pasero 已提交
836
					return this.editorService.openEditor(editorInput, { preserveFocus, selection: { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 } }, sideBySide);
E
Erich Gamma 已提交
837 838 839
				});
			}

840
			return this.sourceIsUnavailable(process, source, sideBySide);
E
Erich Gamma 已提交
841 842 843 844 845 846 847 848 849 850 851 852 853 854
		}

		return this.fileService.resolveFile(source.uri).then(() =>
			this.editorService.openEditor({
				resource: source.uri,
				options: {
					selection: {
						startLineNumber: lineNumber,
						startColumn: 1,
						endLineNumber: lineNumber,
						endColumn: 1
					},
					preserveFocus: preserveFocus
				}
855
			}, sideBySide), err => this.sourceIsUnavailable(process, source, sideBySide)
E
Erich Gamma 已提交
856 857 858
		);
	}

859
	private sourceIsUnavailable(process: debug.IProcess, source: Source, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
860
		this.model.sourceIsUnavailable(source);
861
		const editorInput = this.getDebugErrorEditorInput(process, source, nls.localize('debugSourceNotAvailable', "Source {0} is not available.", source.name));
E
Erich Gamma 已提交
862

863
		return this.editorService.openEditor(editorInput, { preserveFocus: true }, sideBySide);
E
Erich Gamma 已提交
864 865
	}

866 867
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
868 869
	}

I
isidor 已提交
870
	private lazyTransitionToRunningState(session: RawDebugSession, threadId?: number): void {
871
		let setNewFocusedStackFrameScheduler: RunOnceScheduler;
872

873
		const toDispose = session.onDidStop(e => {
874
			if (e.body.threadId === threadId || e.body.allThreadsStopped || !threadId) {
875
				setNewFocusedStackFrameScheduler.cancel();
876 877 878
			}
		});

879
		this.model.clearThreads(session.getId(), false, threadId);
880

881
		// Get a top stack frame of a stopped thread if there is any.
I
isidor 已提交
882 883 884

		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
		const stoppedThread = process && process.getAllThreads().filter(t => t.stopped).pop();
885 886
		const callStack = stoppedThread ? stoppedThread.getCachedCallStack() : null;
		const stackFrameToFocus = callStack && callStack.length > 0 ? callStack[0] : null;
887

888 889 890 891 892 893 894 895 896 897 898
		if (!stoppedThread) {
			this.setStateAndEmit(this.configurationManager.configuration.noDebug ? debug.State.RunningNoDebug : debug.State.Running);
		}

		// Do not immediatly set a new focused stack frame since that might cause unnecessery flickering
		// of the tree in the debug viewlet. Only set focused stack frame if no stopped event has arrived in 500ms.
		setNewFocusedStackFrameScheduler = new RunOnceScheduler(() => {
			toDispose.dispose();
			aria.status(nls.localize('debuggingContinued', "Debugging continued."));

			this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
899
		}, 500);
900
		setNewFocusedStackFrameScheduler.schedule();
901 902
	}

903
	private getDebugStringEditorInput(process: debug.IProcess, source: Source, value: string, mtype: string): DebugStringEditorInput {
904
		const result = this.instantiationService.createInstance(DebugStringEditorInput, source.name, source.uri, source.origin, value, mtype, void 0);
905
		this.toDisposeOnSessionEnd[process.getId()].push(result);
I
isidor 已提交
906 907 908 909

		return result;
	}

910
	private getDebugErrorEditorInput(process: debug.IProcess, source: Source, value: string): DebugErrorEditorInput {
I
isidor 已提交
911
		const result = this.instantiationService.createInstance(DebugErrorEditorInput, source.name, value);
912
		this.toDisposeOnSessionEnd[process.getId()].push(result);
I
isidor 已提交
913

914
		return result;
E
Erich Gamma 已提交
915 916
	}

I
isidor 已提交
917
	private sendAllBreakpoints(session?: RawDebugSession): TPromise<any> {
I
isidor 已提交
918 919
		return TPromise.join(arrays.distinct(this.model.getBreakpoints(), bp => bp.source.uri.toString()).map(bp => this.sendBreakpoints(bp.source.uri, false, session)))
			.then(() => this.sendFunctionBreakpoints(session))
I
isidor 已提交
920
			// send exception breakpoints at the end since some debug adapters rely on the order
I
isidor 已提交
921
			.then(() => this.sendExceptionBreakpoints(session));
E
Erich Gamma 已提交
922 923
	}

I
isidor 已提交
924
	private sendBreakpoints(modelUri: uri, sourceModified = false, targetSession?: RawDebugSession): TPromise<void> {
I
isidor 已提交
925
		const sendBreakpointsToSession = (session: RawDebugSession): TPromise<void> => {
I
isidor 已提交
926 927 928 929 930 931 932 933
			if (!session.readyForBreakpoints) {
				return TPromise.as(null);
			}
			if (this.textFileService.isDirty(modelUri)) {
				// Only send breakpoints for a file once it is not dirty #8077
				this.breakpointsToSendOnResourceSaved[modelUri.toString()] = true;
				return TPromise.as(null);
			}
934

I
isidor 已提交
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
			const breakpointsToSend = arrays.distinct(
				this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.source.uri.toString() === modelUri.toString()),
				bp => `${bp.desiredLineNumber}`
			);
			const rawSource = breakpointsToSend.length > 0 ? breakpointsToSend[0].source.raw : Source.toRawSource(modelUri, this.model);

			return session.setBreakpoints({
				source: rawSource,
				lines: breakpointsToSend.map(bp => bp.desiredLineNumber),
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.desiredLineNumber, condition: bp.condition, hitCondition: bp.hitCondition })),
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
950

I
isidor 已提交
951 952 953 954
				const data: { [id: string]: { line?: number, verified: boolean } } = {};
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
				}
955

I
isidor 已提交
956 957 958
				this.model.updateBreakpoints(data);
			});
		};
959

I
isidor 已提交
960
		return this.sendToOneOrAllSessions(targetSession, sendBreakpointsToSession);
E
Erich Gamma 已提交
961 962
	}

I
isidor 已提交
963 964
	private sendFunctionBreakpoints(targetSession?: RawDebugSession): TPromise<void> {
		const sendFunctionBreakpointsToSession = (session: RawDebugSession): TPromise<void> => {
I
isidor 已提交
965 966
			if (!session.readyForBreakpoints || !session.configuration.capabilities.supportsFunctionBreakpoints) {
				return TPromise.as(null);
967 968
			}

I
isidor 已提交
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
			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);
			});
		};

I
isidor 已提交
984
		return this.sendToOneOrAllSessions(targetSession, sendFunctionBreakpointsToSession);
I
isidor 已提交
985 986
	}

I
isidor 已提交
987
	private sendExceptionBreakpoints(targetSession?: RawDebugSession): TPromise<void> {
I
isidor 已提交
988
		const sendExceptionBreakpointsToSession = (session: RawDebugSession): TPromise<any> => {
I
isidor 已提交
989 990
			if (!session || !session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
				return TPromise.as(null);
I
isidor 已提交
991 992
			}

I
isidor 已提交
993 994 995 996
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

I
isidor 已提交
997
		return this.sendToOneOrAllSessions(targetSession, sendExceptionBreakpointsToSession);
I
isidor 已提交
998 999
	}

I
isidor 已提交
1000
	private sendToOneOrAllSessions(session: RawDebugSession, send: (session: RawDebugSession) => TPromise<void>): TPromise<void> {
I
isidor 已提交
1001
		if (session) {
1002
			return send(session);
I
isidor 已提交
1003
		}
I
isidor 已提交
1004

I
isidor 已提交
1005
		return TPromise.join(this.model.getProcesses().map(p => send(<RawDebugSession>p.session))).then(() => void 0);
E
Erich Gamma 已提交
1006 1007 1008
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1009 1010
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
1011 1012 1013 1014 1015 1016 1017 1018

		fileChangesEvent.getUpdated().forEach(event => {
			if (this.breakpointsToSendOnResourceSaved[event.resource.toString()]) {
				this.breakpointsToSendOnResourceSaved[event.resource.toString()] = false;
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
		});

E
Erich Gamma 已提交
1019 1020 1021 1022 1023
	}

	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);
1024
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1025
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1026
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
1027
		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 已提交
1028 1029 1030
	}

	public dispose(): void {
1031
		Object.keys(this.toDisposeOnSessionEnd).forEach(key => lifecycle.dispose(this.toDisposeOnSessionEnd[key]));
I
isidor 已提交
1032
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1033 1034
	}
}