debugService.ts 45.0 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 9
import { guessMimeTypes } from 'vs/base/common/mime';
import Event, { Emitter } from 'vs/base/common/event';
10
import * as paths from 'vs/base/common/paths';
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';
I
isidor 已提交
22
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
J
Johannes Rieken 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36
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';
I
isidor 已提交
37
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
38
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
39
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression } from 'vs/workbench/parts/debug/common/debugModel';
J
Johannes Rieken 已提交
40
import { DebugStringEditorInput, DebugErrorEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
I
isidor 已提交
41 42
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
J
Johannes Rieken 已提交
43 44 45 46
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 已提交
47
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
J
Johannes Rieken 已提交
48 49 50 51 52 53 54 55
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';
S
Sandeep Somavarapu 已提交
56
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/electron-browser/extensionHost';
J
Johannes Rieken 已提交
57
import { ipcRenderer as ipc } from 'electron';
E
Erich Gamma 已提交
58

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

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

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

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

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

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

E
Erich Gamma 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
		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;
			}));
		}

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

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

	private onBroadcast(broadcast: IBroadcast): void {
144

I
isidor 已提交
145
		// attach: PH is ready to be attached to
146 147 148 149
		// 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();
150
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
151
			this.rawAttach(session, broadcast.payload.port);
152 153
			return;
		}
154

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

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

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

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

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

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

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

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

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

					simpleVals.push(buf);
				}

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

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

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

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

260 261 262 263 264
			session.threads().then(response => {
				if (!response || !response.body || !response.body.threads) {
					return;
				}

I
isidor 已提交
265
				const rawThread = response.body.threads.filter(t => t.id === threadId).pop();
266
				this.model.rawUpdate({
267
					sessionId: session.getId(),
I
isidor 已提交
268
					thread: rawThread,
I
isidor 已提交
269
					threadId,
270 271 272
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
273

I
isidor 已提交
274
				const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
I
isidor 已提交
275 276 277 278 279
				const thread = process && process.getThread(threadId);
				if (thread) {
					thread.getCallStack().then(callStack => {
						if (callStack.length > 0) {
							// focus first stack frame from top that has source location
I
isidor 已提交
280
							const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, callStack[0]);
I
isidor 已提交
281 282 283 284 285 286 287 288 289 290
							this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
							this.windowService.getWindow().focus();
							aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.lineNumber));

							return this.openOrRevealSource(stackFrameToFocus.source, stackFrameToFocus.lineNumber, false, false);
						} else {
							this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
						}
					});
				}
E
Erich Gamma 已提交
291 292 293
			}, errors.onUnexpectedError);
		}));

294
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidThread(event => {
E
Erich Gamma 已提交
295
			if (event.body.reason === 'started') {
296 297 298 299 300 301 302 303 304 305
				session.threads().done(response => {
					if (response && response.body && response.body.threads) {
						response.body.threads.forEach(thread =>
							this.model.rawUpdate({
								sessionId: session.getId(),
								threadId: thread.id,
								thread
							}));
					}
				}, errors.onUnexpectedError);
E
Erich Gamma 已提交
306
			} else if (event.body.reason === 'exited') {
307
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
308 309 310
			}
		}));

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

323
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidContinued(event => {
324
			this.transitionToRunningState(session, event.body.allThreadsContinued ? undefined : event.body.threadId);
I
isidor 已提交
325 326
		}));

327
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidOutput(event => {
328
			if (event.body && event.body.category === 'telemetry') {
329
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
330
				// and the user opted in telemetry
331 332
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
333
				}
334
			} else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) {
E
Erich Gamma 已提交
335 336 337 338
				this.onOutput(event);
			}
		}));

339
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidBreakpoint(event => {
I
isidor 已提交
340
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
341
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
342 343 344
			if (breakpoint) {
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
345
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
346 347 348 349 350 351
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

352
		this.toDisposeOnSessionEnd[session.getId()].push(session.onDidExitAdapter(event => {
353
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
354
			if (session && session.configuration.type === 'extensionHost' && this.sessionStates[session.getId()] === debug.State.RunningNoDebug) {
355 356
				ipc.send('vscode:closeExtensionHostWindow', this.contextService.getWorkspace().resource.fsPath);
			}
357
			if (session && session.getId() === event.body.sessionId) {
358
				this.onSessionEnd(session);
359
			}
E
Erich Gamma 已提交
360 361 362 363
		}));
	}

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

368 369
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
370
		try {
371
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
372
				return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.desiredLineNumber || breakpoint.lineNumber, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition);
E
Erich Gamma 已提交
373
			});
374 375 376
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
377 378
	}

379 380
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
381
		try {
382
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
383
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
384
			});
385 386 387
		} catch (e) { }

		return result || [];
I
isidor 已提交
388 389
	}

390 391
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
392 393
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
394
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
395
			});
396
		} catch (e) { }
E
Erich Gamma 已提交
397

398
		return result || [];
E
Erich Gamma 已提交
399 400
	}

I
isidor 已提交
401 402
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
403
		try {
J
Johannes Rieken 已提交
404
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
405
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
406
			});
407 408 409
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
410 411
	}

I
isidor 已提交
412
	public get state(): debug.State {
I
isidor 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425 426
		if (!this.contextService.getWorkspace()) {
			return debug.State.Disabled;
		}

		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess) {
			return this.sessionStates[focusedProcess.getId()];
		}
		const processes = this.model.getProcesses();
		if (processes.length > 0) {
			return this.sessionStates[processes[0].getId()];
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
427 428
	}

I
isidor 已提交
429
	public get onDidChangeState(): Event<void> {
430 431 432
		return this._onDidChangeState.event;
	}

I
isidor 已提交
433 434
	private setStateAndEmit(sessionId: string, newState: debug.State): void {
		this.sessionStates[sessionId] = newState;
I
isidor 已提交
435
		this._onDidChangeState.fire();
E
Erich Gamma 已提交
436 437 438 439 440 441
	}

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

I
isidor 已提交
442
	public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame, process?: debug.IProcess): TPromise<void> {
I
isidor 已提交
443
		const processes = this.model.getProcesses();
I
isidor 已提交
444 445 446
		if (!process) {
			process = focusedStackFrame ? focusedStackFrame.thread.process : processes.length ? processes[0] : null;
		}
I
isidor 已提交
447 448 449 450 451 452 453 454
		if (process && !focusedStackFrame) {
			const thread = process.getAllThreads().pop();
			const callStack = thread ? thread.getCachedCallStack() : null;
			focusedStackFrame = callStack && callStack.length ? callStack[0] : null;
		}

		this.viewModel.setFocusedStackFrame(focusedStackFrame, process);
		this._onDidChangeState.fire();
455 456

		return this.model.evaluateWatchExpressions(process, focusedStackFrame);
E
Erich Gamma 已提交
457 458
	}

I
isidor 已提交
459
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
460 461
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
462
			if (breakpoint instanceof Breakpoint) {
463
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
464
			} else if (breakpoint instanceof FunctionBreakpoint) {
465 466
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
467

468
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
469 470
		}

471 472
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
473 474
	}

475 476 477
	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)));
478

479
		return this.sendBreakpoints(uri);
480 481
	}

482 483
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
484 485
		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 已提交
486

487
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
488
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
489 490
	}

491 492
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
493 494 495
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
496 497
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
498 499
	}

I
isidor 已提交
500
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
501
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
502
		return this.sendFunctionBreakpoints();
I
isidor 已提交
503 504
	}

I
isidor 已提交
505
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
506
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
507
		return this.sendFunctionBreakpoints();
I
isidor 已提交
508 509
	}

I
isidor 已提交
510
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
511
		this.telemetryService.publicLog('debugService/addReplExpression');
512
		return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
513
			// Evaluate all watch expressions again since repl evaluation might have changed some.
514
			.then(() => this.setFocusedStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
E
Erich Gamma 已提交
515 516
	}

517
	public logToRepl(value: string | { [key: string]: any }, severity?: severity): void {
E
Erich Gamma 已提交
518 519 520 521 522 523 524
		this.model.logToRepl(value, severity);
	}

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

525 526
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
527 528
	}

I
isidor 已提交
529
	public addWatchExpression(name: string): TPromise<void> {
530
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
531 532
	}

I
isidor 已提交
533
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
534
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
535 536
	}

I
isidor 已提交
537 538 539 540
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

541 542
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
543 544
	}

I
isidor 已提交
545
	public createProcess(configurationOrName: debug.IConfig | string): TPromise<any> {
546
		this.removeReplExpressions();
I
isidor 已提交
547
		const sessionId = generateUuid();
I
isidor 已提交
548
		this.setStateAndEmit(sessionId, debug.State.Initializing);
E
Erich Gamma 已提交
549

550
		return this.textFileService.saveAll()							// make sure all dirty files are saved
551
			.then(() => this.configurationService.reloadConfiguration()	// make sure configuration is up to date
J
Johannes Rieken 已提交
552
				.then(() => this.extensionService.onReady()
I
isidor 已提交
553 554
					.then(() => this.configurationManager.getConfiguration(configurationOrName)
						.then(configuration => {
J
Johannes Rieken 已提交
555 556 557 558 559 560 561 562 563 564
							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;
							}
I
isidor 已提交
565
							if (strings.equalsIgnoreCase(configuration.type, 'composite') && configuration.configurationNames) {
566 567
								return TPromise.join(configuration.configurationNames.map(name => this.createProcess(name)));
							}
J
Johannes Rieken 已提交
568

569
							if (!this.configurationManager.getAdapter(configuration.type)) {
J
Johannes Rieken 已提交
570 571 572 573 574 575 576 577 578 579
								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)) {
I
isidor 已提交
580
									return this.doCreateProcess(sessionId, configuration);
J
Johannes Rieken 已提交
581 582 583 584 585 586 587 588
								}

								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();
I
isidor 已提交
589
										return this.doCreateProcess(sessionId, configuration);
J
Johannes Rieken 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602
									}), 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 已提交
603
	}
E
Erich Gamma 已提交
604

I
isidor 已提交
605
	private doCreateProcess(sessionId: string, configuration: debug.IConfig): TPromise<any> {
606

J
Joao Moreno 已提交
607
		return this.telemetryService.getTelemetryInfo().then(info => {
608
			const telemetryInfo: { [key: string]: string } = Object.create(null);
609 610
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
611 612
			return telemetryInfo;
		}).then(data => {
613 614 615
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
616
			this.customTelemetryService = null;
617
			let client: TelemetryClient;
618 619

			if (aiKey) {
620
				client = new TelemetryClient(
621 622 623 624
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
625
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
626
						env: {
B
Benjamin Pasero 已提交
627
							ELECTRON_RUN_AS_NODE: 1,
628 629 630
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
631
					}
632
				);
633

634 635
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
636

637
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
638 639
			}

640
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService);
641 642 643
			const process = this.model.addProcess(configuration.name, session);

			if (!this.viewModel.focusedProcess) {
644 645
				this.viewModel.setFocusedStackFrame(null, process);
				this._onDidChangeState.fire();
646
			}
647 648 649 650
			this.toDisposeOnSessionEnd[session.getId()] = [];
			if (client) {
				this.toDisposeOnSessionEnd[session.getId()].push(client);
			}
651
			this.registerSessionListeners(session);
J
Joao Moreno 已提交
652

653
			return session.initialize({
J
Joao Moreno 已提交
654 655 656
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
657
				columnsStartAt1: true,
I
isidor 已提交
658
				supportsVariableType: true, // #8858
659 660
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
661
			}).then((result: DebugProtocol.InitializeResponse) => {
662
				if (session.disconnected) {
J
Joao Moreno 已提交
663 664
					return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
				}
665

666 667
				this.model.setExceptionBreakpoints(session.configuration.capabilities.exceptionBreakpointFilters);
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
668
			}).then((result: DebugProtocol.Response) => {
669
				if (session.disconnected) {
670 671 672
					return TPromise.as(null);
				}

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

J
Joao Moreno 已提交
677 678 679 680 681
				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);
				}
682

J
Joao Moreno 已提交
683 684 685 686 687 688
				// 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);
689
				this.transitionToRunningState(session);
J
Joao Moreno 已提交
690 691 692 693 694 695

				this.telemetryService.publicLog('debugSessionStart', {
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
696 697
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
					isBuiltin: adapter.extensionDescription.isBuiltin
J
Joao Moreno 已提交
698 699
				});
			}).then(undefined, (error: any) => {
700 701 702 703 704
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

J
Joao Moreno 已提交
705
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
I
isidor 已提交
706
				this.setStateAndEmit(session.getId(), debug.State.Inactive);
707 708
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
709 710 711 712 713
				}
				// 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 已提交
714

J
Joao Moreno 已提交
715 716 717 718
				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 已提交
719 720 721
		});
	}

I
isidor 已提交
722
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
723
		if (!taskName) {
I
isidor 已提交
724
			return TPromise.as(null);
E
Erich Gamma 已提交
725 726
		}

727
		// run a task before starting a debug session
E
Erich Gamma 已提交
728
		return this.taskService.tasks().then(descriptions => {
729
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
730
			if (filteredTasks.length !== 1) {
731 732
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName), {
					actions: [
733
						this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
734
						this.taskService.configureAction(),
735
						CloseAction
736 737
					]
				}));
E
Erich Gamma 已提交
738 739
			}

I
isidor 已提交
740
			// task is already running - nothing to do.
741
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
742
				return TPromise.as(null);
E
Erich Gamma 已提交
743 744 745
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
746
				// there is a different task running currently.
I
isidor 已提交
747
				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 已提交
748 749
			}

I
isidor 已提交
750
			// no task running, execute the preLaunchTask.
751
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
752
				this.lastTaskEvent = null;
I
isidor 已提交
753
				return result;
E
Erich Gamma 已提交
754 755 756
			}, err => {
				this.lastTaskEvent = null;
			});
757

758
			if (filteredTasks[0].isWatching) {
A
Alex Dima 已提交
759
				return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
760 761 762
			}

			return taskPromise;
E
Erich Gamma 已提交
763 764 765
		});
	}

766 767 768
	private rawAttach(session: RawDebugSession, port: number): TPromise<any> {
		if (session) {
			return session.attach({ port });
I
isidor 已提交
769 770
		}

I
isidor 已提交
771
		const sessionId = generateUuid();
I
isidor 已提交
772
		this.setStateAndEmit(sessionId, debug.State.Initializing);
I
isidor 已提交
773 774
		return this.configurationManager.getConfiguration(this.viewModel.selectedConfigurationName).then(config =>
			this.doCreateProcess(sessionId, config)
775
		);
I
isidor 已提交
776 777
	}

I
isidor 已提交
778 779
	public restartProcess(process: debug.IProcess): TPromise<any> {
		return process ? process.session.disconnect(true).then(() =>
780
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
781
				setTimeout(() => {
I
isidor 已提交
782
					this.createProcess(process.name).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
783
				}, 300);
I
isidor 已提交
784
			})
I
isidor 已提交
785
		) : this.createProcess(this.viewModel.selectedConfigurationName);
E
Erich Gamma 已提交
786 787
	}

I
isidor 已提交
788
	private onSessionEnd(session: RawDebugSession): void {
789
		if (session) {
I
isidor 已提交
790
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
791
			this.telemetryService.publicLog('debugSessionStop', {
792 793 794
				type: session.configuration.type,
				success: session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: session.getLengthInSeconds(),
I
isidor 已提交
795 796 797
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
798
		}
799

I
isidor 已提交
800
		try {
801
			this.toDisposeOnSessionEnd[session.getId()] = lifecycle.dispose(this.toDisposeOnSessionEnd[session.getId()]);
I
isidor 已提交
802 803 804 805
		} catch (e) {
			// an internal module might be open so the dispose can throw -> ignore and continue with stop session.
		}

806
		this.model.removeProcess(session.getId());
I
isidor 已提交
807
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
I
isidor 已提交
808
		this.setStateAndEmit(session.getId(), debug.State.Inactive);
809

810 811 812
		if (this.model.getProcesses().length === 0) {
			this.partService.removeClass('debugging');
			// set breakpoints back to unverified since the session ended.
813
			const data: { [id: string]: { line: number, verified: boolean } } = {};
814
			this.model.getBreakpoints().forEach(bp => {
815
				data[bp.getId()] = { line: bp.lineNumber, verified: false };
816 817
			});
			this.model.updateBreakpoints(data);
818

819
			this.inDebugMode.reset();
I
isidor 已提交
820

821 822 823
			if (!this.partService.isSideBarHidden() && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
824
		}
E
Erich Gamma 已提交
825 826 827 828 829 830 831 832 833 834
	}

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

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

835
	public openOrRevealSource(sourceOrUri: Source | uri, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
836
		const visibleEditors = this.editorService.getVisibleEditors();
837 838
		const uri = sourceOrUri instanceof Source ? sourceOrUri.uri : sourceOrUri;
		const source = sourceOrUri instanceof Source ? sourceOrUri : null;
I
isidor 已提交
839
		for (let i = 0; i < visibleEditors.length; i++) {
I
isidor 已提交
840
			const fileInput = asFileEditorInput(visibleEditors[i].input);
841 842
			if ((fileInput && fileInput.getResource().toString() === uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === uri.toString())) {
843

I
isidor 已提交
844
				const control = <ICodeEditor>visibleEditors[i].getControl();
845 846 847
				if (control) {
					control.revealLineInCenterIfOutsideViewport(lineNumber);
					control.setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 });
848
					this.editorGroupService.activateGroup(i);
I
isidor 已提交
849
					if (!preserveFocus) {
850
						this.editorGroupService.focusGroup(i);
I
isidor 已提交
851
					}
E
Erich Gamma 已提交
852
				}
853

A
Alex Dima 已提交
854
				return TPromise.as(null);
E
Erich Gamma 已提交
855 856 857
			}
		}

858
		const process = this.viewModel.focusedProcess;
859
		if (process && source && source.inMemory) {
I
isidor 已提交
860
			// internal module
861
			if (source.reference !== 0 && source.available) {
862
				return process.session.source({ sourceReference: source.reference }).then(response => {
863 864
					const mime = response && response.body && response.body.mimeType ? response.body.mimeType : guessMimeTypes(source.name)[0];
					const inputValue = response && response.body ? response.body.content : '';
865
					return this.getDebugStringEditorInput(process, source, inputValue, mime);
866 867
				}, (err: DebugProtocol.ErrorResponse) => {
					// Display the error from debug adapter using a temporary editor #8836
868
					return this.getDebugErrorEditorInput(process, source, err.message);
869
				}).then(editorInput => {
B
Benjamin Pasero 已提交
870
					return this.editorService.openEditor(editorInput, { preserveFocus, selection: { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 } }, sideBySide);
E
Erich Gamma 已提交
871 872 873
				});
			}

874
			return this.sourceIsUnavailable(process, source, sideBySide);
E
Erich Gamma 已提交
875
		}
876 877 878
		if (Source.isInMemory(uri)) {
			return TPromise.as(null);
		}
E
Erich Gamma 已提交
879

880
		return this.fileService.resolveFile(uri).then(() =>
E
Erich Gamma 已提交
881
			this.editorService.openEditor({
882
				resource: uri,
E
Erich Gamma 已提交
883 884 885 886 887 888 889 890 891
				options: {
					selection: {
						startLineNumber: lineNumber,
						startColumn: 1,
						endLineNumber: lineNumber,
						endColumn: 1
					},
					preserveFocus: preserveFocus
				}
892
			}, sideBySide), err => this.sourceIsUnavailable(process, source, sideBySide)
E
Erich Gamma 已提交
893 894 895
		);
	}

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

900
		return this.editorService.openEditor(editorInput, { preserveFocus: true }, sideBySide);
E
Erich Gamma 已提交
901 902
	}

903 904
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
905 906
	}

907
	private transitionToRunningState(session: RawDebugSession, threadId?: number): void {
908
		this.model.clearThreads(session.getId(), false, threadId);
909 910
		this.setStateAndEmit(session.getId(), session.requestType === debug.SessionRequestType.LAUNCH_NO_DEBUG ? debug.State.RunningNoDebug : debug.State.Running);
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
911 912
	}

913
	private getDebugStringEditorInput(process: debug.IProcess, source: Source, value: string, mtype: string): DebugStringEditorInput {
914
		const result = this.instantiationService.createInstance(DebugStringEditorInput, source.name, source.uri, source.origin, value, mtype, void 0);
915
		this.toDisposeOnSessionEnd[process.getId()].push(result);
I
isidor 已提交
916 917 918 919

		return result;
	}

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

924
		return result;
E
Erich Gamma 已提交
925 926
	}

927 928 929
	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 已提交
930
			// send exception breakpoints at the end since some debug adapters rely on the order
931
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
932 933
	}

934 935 936 937
	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 已提交
938 939 940 941 942 943 944 945
			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);
			}
946

I
isidor 已提交
947
			const breakpointsToSend = distinct(
948
				this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString()),
I
isidor 已提交
949 950
				bp => `${bp.desiredLineNumber}`
			);
951 952 953 954 955 956 957 958 959 960 961

			let rawSource: DebugProtocol.Source;
			for (let t of process.getAllThreads()) {
				for (let sf of t.getCachedCallStack()) {
					if (sf.source.uri.toString() === modelUri.toString()) {
						rawSource = sf.source.raw;
						break;
					}
				}
			}
			rawSource = rawSource || { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) };
I
isidor 已提交
962 963 964 965 966 967 968 969 970 971

			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;
				}
972

I
isidor 已提交
973 974 975 976
				const data: { [id: string]: { line?: number, verified: boolean } } = {};
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
				}
977

I
isidor 已提交
978 979 980
				this.model.updateBreakpoints(data);
			});
		};
981

982
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
983 984
	}

985 986 987
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
I
isidor 已提交
988 989
			if (!session.readyForBreakpoints || !session.configuration.capabilities.supportsFunctionBreakpoints) {
				return TPromise.as(null);
990 991
			}

I
isidor 已提交
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
			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);
			});
		};

1007
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1008 1009
	}

1010 1011 1012 1013
	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 已提交
1014
				return TPromise.as(null);
I
isidor 已提交
1015 1016
			}

I
isidor 已提交
1017 1018 1019 1020
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1021
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1022 1023
	}

1024 1025 1026
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1027
		}
I
isidor 已提交
1028

1029
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1030 1031 1032
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1033
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1034
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1035 1036 1037 1038 1039 1040 1041 1042

		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 已提交
1043 1044 1045 1046 1047
	}

	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);
1048
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1049
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1050
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.viewModel.selectedConfigurationName, StorageScope.WORKSPACE);
1051
		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 已提交
1052 1053 1054
	}

	public dispose(): void {
1055
		Object.keys(this.toDisposeOnSessionEnd).forEach(key => lifecycle.dispose(this.toDisposeOnSessionEnd[key]));
I
isidor 已提交
1056
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1057 1058
	}
}