debugService.ts 45.0 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';
I
isidor 已提交
10
import uuid = require('vs/base/common/uuid');
E
Erich Gamma 已提交
11
import uri from 'vs/base/common/uri';
J
Johannes Rieken 已提交
12 13
import { RunOnceScheduler } from 'vs/base/common/async';
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
14
import arrays = require('vs/base/common/arrays');
15
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
16 17
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
18
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
19
import aria = require('vs/base/browser/ui/aria/aria');
20
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
E
Erich Gamma 已提交
21
import editorbrowser = require('vs/editor/browser/editorBrowser');
J
Johannes Rieken 已提交
22 23 24 25 26 27 28 29 30 31 32 33 34 35
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 已提交
36
import debug = require('vs/workbench/parts/debug/common/debug');
J
Johannes Rieken 已提交
37
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
E
Erich Gamma 已提交
38
import model = require('vs/workbench/parts/debug/common/debugModel');
J
Johannes Rieken 已提交
39
import { DebugStringEditorInput, DebugErrorEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
E
Erich Gamma 已提交
40
import viewmodel = require('vs/workbench/parts/debug/common/debugViewModel');
41
import debugactions = require('vs/workbench/parts/debug/browser/debugActions');
J
Johannes Rieken 已提交
42 43 44 45
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 已提交
46
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
J
Johannes Rieken 已提交
47 48 49 50 51 52 53 54
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';
55
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 已提交
56
import { ipcRenderer as ipc } from 'electron';
E
Erich Gamma 已提交
57

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

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

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

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

108
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, 'null'));
109
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
E
Erich Gamma 已提交
110

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

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

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

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

	private onBroadcast(broadcast: IBroadcast): void {
143

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

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

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

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

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

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

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

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

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

I
isidor 已提交
205 206
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
				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 已提交
229
			// flush simple values
230 231 232 233
			if (simpleVals.length) {
				this.logToRepl(simpleVals.join(' '), sev);
			}
		}
E
Erich Gamma 已提交
234 235
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return result || [];
E
Erich Gamma 已提交
367 368
	}

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

		return result || [];
I
isidor 已提交
378 379
	}

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

388
		return result || [];
E
Erich Gamma 已提交
389 390 391
	}

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

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

I
isidor 已提交
402
	public get state(): debug.State {
I
isidor 已提交
403 404 405 406 407 408 409 410 411 412 413 414 415 416
		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 已提交
417 418
	}

419 420 421 422
	public get onDidChangeState(): Event<debug.State> {
		return this._onDidChangeState.event;
	}

I
isidor 已提交
423 424
	private setStateAndEmit(sessionId: string, newState: debug.State): void {
		this.sessionStates[sessionId] = newState;
425
		this._onDidChangeState.fire(newState);
E
Erich Gamma 已提交
426 427 428 429 430 431
	}

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

432
	public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise<void> {
433
		this.viewModel.setFocusedStackFrame(focusedStackFrame);
E
Erich Gamma 已提交
434
		if (focusedStackFrame) {
435
			return this.model.evaluateWatchExpressions(focusedStackFrame);
E
Erich Gamma 已提交
436 437
		} else {
			this.model.clearWatchExpressionValues();
I
isidor 已提交
438
			return TPromise.as(null);
E
Erich Gamma 已提交
439 440 441
		}
	}

I
isidor 已提交
442
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
443 444 445
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
			if (breakpoint instanceof model.Breakpoint) {
I
isidor 已提交
446
				return this.sendBreakpoints((<model.Breakpoint>breakpoint).source.uri);
447 448 449
			} else if (breakpoint instanceof model.FunctionBreakpoint) {
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
450

451
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
452 453
		}

454 455
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
456 457
	}

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

I
isidor 已提交
463
		return TPromise.join(uris.map(uri => this.sendBreakpoints(uri))).then(() => void 0);
464 465
	}

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

471
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
472
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
473 474
	}

475 476
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
477 478 479
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
480 481
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
482 483
	}

I
isidor 已提交
484
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
485
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
486
		return this.sendFunctionBreakpoints();
I
isidor 已提交
487 488
	}

I
isidor 已提交
489
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
490
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
491
		return this.sendFunctionBreakpoints();
I
isidor 已提交
492 493
	}

I
isidor 已提交
494
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
495
		this.telemetryService.publicLog('debugService/addReplExpression');
496
		return this.model.addReplExpression(this.viewModel.focusedStackFrame, name)
497
			// Evaluate all watch expressions again since repl evaluation might have changed some.
498
			.then(() => this.setFocusedStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
E
Erich Gamma 已提交
499 500
	}

501
	public logToRepl(value: string | { [key: string]: any }, severity?: severity): void {
E
Erich Gamma 已提交
502 503 504 505 506 507 508
		this.model.logToRepl(value, severity);
	}

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

509 510
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
511 512
	}

I
isidor 已提交
513
	public addWatchExpression(name: string): TPromise<void> {
514
		return this.model.addWatchExpression(this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
515 516
	}

I
isidor 已提交
517
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
518
		return this.model.renameWatchExpression(this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
519 520
	}

521 522
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
523 524
	}

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

528
		return this.textFileService.saveAll()							// make sure all dirty files are saved
529
			.then(() => this.configurationService.reloadConfiguration()	// make sure configuration is up to date
J
Johannes Rieken 已提交
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 570 571 572 573 574 575 576 577 578 579 580
				.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 已提交
581
	}
E
Erich Gamma 已提交
582

I
isidor 已提交
583
	private doCreateSession(configuration: debug.IExtHostConfig): TPromise<any> {
I
isidor 已提交
584 585
		const sessionId = uuid.generateUuid();
		this.setStateAndEmit(sessionId, debug.State.Initializing);
586

J
Joao Moreno 已提交
587
		return this.telemetryService.getTelemetryInfo().then(info => {
588
			const telemetryInfo: { [key: string]: string } = Object.create(null);
589 590
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
591 592
			return telemetryInfo;
		}).then(data => {
593
			const { aiKey, type } = this.configurationManager.adapter;
594
			const publisher = this.configurationManager.adapter.extensionDescription.publisher;
595
			this.customTelemetryService = null;
596
			let client: TelemetryClient;
597 598

			if (aiKey) {
599
				client = new TelemetryClient(
600 601 602 603
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
604
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
605
						env: {
B
Benjamin Pasero 已提交
606
							ELECTRON_RUN_AS_NODE: 1,
607 608 609
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
610
					}
611
				);
612

613 614
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
615

616
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
617 618
			}

I
isidor 已提交
619
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, this.configurationManager.adapter, this.customTelemetryService);
I
isidor 已提交
620
			this.model.addProcess(name, session);
621 622 623 624
			this.toDisposeOnSessionEnd[session.getId()] = [];
			if (client) {
				this.toDisposeOnSessionEnd[session.getId()].push(client);
			}
625
			this.registerSessionListeners(session);
J
Joao Moreno 已提交
626

627
			return session.initialize({
J
Joao Moreno 已提交
628 629 630
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
631
				columnsStartAt1: true,
I
isidor 已提交
632
				supportsVariableType: true, // #8858
633 634
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
635
			}).then((result: DebugProtocol.InitializeResponse) => {
636
				if (session.disconnected) {
J
Joao Moreno 已提交
637 638
					return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
				}
639

640 641
				this.model.setExceptionBreakpoints(session.configuration.capabilities.exceptionBreakpointFilters);
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
642
			}).then((result: DebugProtocol.Response) => {
643
				if (session.disconnected) {
644 645 646
					return TPromise.as(null);
				}

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

J
Joao Moreno 已提交
651 652 653 654 655
				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);
				}
656

J
Joao Moreno 已提交
657 658 659 660 661 662
				// 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);
663
				this.lazyTransitionToRunningState(session);
J
Joao Moreno 已提交
664 665 666 667 668 669

				this.telemetryService.publicLog('debugSessionStart', {
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
J
Johannes Rieken 已提交
670
					extensionName: `${this.configurationManager.adapter.extensionDescription.publisher}.${this.configurationManager.adapter.extensionDescription.name}`,
J
Joao Moreno 已提交
671 672 673
					isBuiltin: this.configurationManager.adapter.extensionDescription.isBuiltin
				});
			}).then(undefined, (error: any) => {
674 675 676 677 678
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

J
Joao Moreno 已提交
679
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
I
isidor 已提交
680
				this.setStateAndEmit(session.getId(), debug.State.Inactive);
681 682
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
683 684 685 686 687
				}
				// 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 已提交
688

J
Joao Moreno 已提交
689 690 691 692
				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 已提交
693 694 695
		});
	}

I
isidor 已提交
696
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
697
		if (!taskName) {
I
isidor 已提交
698
			return TPromise.as(null);
E
Erich Gamma 已提交
699 700
		}

701
		// run a task before starting a debug session
E
Erich Gamma 已提交
702
		return this.taskService.tasks().then(descriptions => {
703
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
704
			if (filteredTasks.length !== 1) {
705 706
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName), {
					actions: [
707
						this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
708
						this.taskService.configureAction(),
709
						CloseAction
710 711
					]
				}));
E
Erich Gamma 已提交
712 713
			}

I
isidor 已提交
714
			// task is already running - nothing to do.
715
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
716
				return TPromise.as(null);
E
Erich Gamma 已提交
717 718 719
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
720
				// there is a different task running currently.
I
isidor 已提交
721
				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 已提交
722 723
			}

I
isidor 已提交
724
			// no task running, execute the preLaunchTask.
725
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
726
				this.lastTaskEvent = null;
I
isidor 已提交
727
				return result;
E
Erich Gamma 已提交
728 729 730
			}, err => {
				this.lastTaskEvent = null;
			});
731

732
			if (filteredTasks[0].isWatching) {
A
Alex Dima 已提交
733
				return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
734 735 736
			}

			return taskPromise;
E
Erich Gamma 已提交
737 738 739
		});
	}

740 741 742
	private rawAttach(session: RawDebugSession, port: number): TPromise<any> {
		if (session) {
			return session.attach({ port });
I
isidor 已提交
743 744
		}

I
isidor 已提交
745
		this.setStateAndEmit(session.getId(), debug.State.Initializing);
I
isidor 已提交
746
		const configuration = <debug.IExtHostConfig>this.configurationManager.configuration;
I
isidor 已提交
747
		return this.doCreateSession({
748
			type: configuration.type,
I
isidor 已提交
749
			request: 'attach',
750 751
			port,
			sourceMaps: configuration.sourceMaps,
752 753
			outDir: configuration.outDir,
			debugServer: configuration.debugServer
I
isidor 已提交
754
		});
I
isidor 已提交
755 756
	}

757 758
	public restartSession(session: debug.ISession): TPromise<any> {
		return session ? session.disconnect(true).then(() =>
759
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
760
				setTimeout(() => {
I
isidor 已提交
761
					this.createSession(false, null).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
762
				}, 300);
I
isidor 已提交
763
			})
I
isidor 已提交
764
		) : this.createSession(false, null);
E
Erich Gamma 已提交
765 766
	}

I
isidor 已提交
767
	private onSessionEnd(session: RawDebugSession): void {
768
		if (session) {
I
isidor 已提交
769
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
770
			this.telemetryService.publicLog('debugSessionStop', {
771 772 773
				type: session.configuration.type,
				success: session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: session.getLengthInSeconds(),
I
isidor 已提交
774 775 776
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
777
		}
778

I
isidor 已提交
779
		try {
780
			this.toDisposeOnSessionEnd[session.getId()] = lifecycle.dispose(this.toDisposeOnSessionEnd[session.getId()]);
I
isidor 已提交
781 782 783 784
		} catch (e) {
			// an internal module might be open so the dispose can throw -> ignore and continue with stop session.
		}

E
Erich Gamma 已提交
785
		this.partService.removeClass('debugging');
786

787
		this.model.removeProcess(session.getId());
I
isidor 已提交
788
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
I
isidor 已提交
789
		this.setStateAndEmit(session.getId(), debug.State.Inactive);
790

I
isidor 已提交
791
		// set breakpoints back to unverified since the session ended.
792
		// source reference changes across sessions, so we do not use it to persist the source.
I
isidor 已提交
793
		const data: { [id: string]: { line: number, verified: boolean } } = {};
794 795 796 797
		this.model.getBreakpoints().forEach(bp => {
			delete bp.source.raw.sourceReference;
			data[bp.getId()] = { line: bp.lineNumber, verified: false };
		});
798 799
		this.model.updateBreakpoints(data);

E
Erich Gamma 已提交
800
		this.inDebugMode.reset();
I
isidor 已提交
801 802 803 804

		if (!this.partService.isSideBarHidden() && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
			this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
		}
E
Erich Gamma 已提交
805 806 807 808 809 810 811 812 813 814
	}

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

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

815
	public openOrRevealSource(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
816
		const visibleEditors = this.editorService.getVisibleEditors();
I
isidor 已提交
817
		for (let i = 0; i < visibleEditors.length; i++) {
I
isidor 已提交
818
			const fileInput = asFileEditorInput(visibleEditors[i].input);
819 820 821
			if ((fileInput && fileInput.getResource().toString() === source.uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === source.uri.toString())) {

822 823 824 825
				const control = <editorbrowser.ICodeEditor>visibleEditors[i].getControl();
				if (control) {
					control.revealLineInCenterIfOutsideViewport(lineNumber);
					control.setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 });
826
					this.editorGroupService.activateGroup(i);
I
isidor 已提交
827
					if (!preserveFocus) {
828
						this.editorGroupService.focusGroup(i);
I
isidor 已提交
829
					}
E
Erich Gamma 已提交
830
				}
831

A
Alex Dima 已提交
832
				return TPromise.as(null);
E
Erich Gamma 已提交
833 834 835
			}
		}

836
		const process = this.viewModel.focusedProcess;
E
Erich Gamma 已提交
837
		if (source.inMemory) {
I
isidor 已提交
838
			// internal module
839 840
			if (source.reference !== 0 && process && source.available) {
				return process.session.source({ sourceReference: source.reference }).then(response => {
841 842
					const mime = response && response.body && response.body.mimeType ? response.body.mimeType : guessMimeTypes(source.name)[0];
					const inputValue = response && response.body ? response.body.content : '';
843
					return this.getDebugStringEditorInput(process, source, inputValue, mime);
844 845
				}, (err: DebugProtocol.ErrorResponse) => {
					// Display the error from debug adapter using a temporary editor #8836
846
					return this.getDebugErrorEditorInput(process, source, err.message);
847
				}).then(editorInput => {
B
Benjamin Pasero 已提交
848
					return this.editorService.openEditor(editorInput, { preserveFocus, selection: { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 } }, sideBySide);
E
Erich Gamma 已提交
849 850 851
				});
			}

852
			return this.sourceIsUnavailable(process, source, sideBySide);
E
Erich Gamma 已提交
853 854 855 856 857 858 859 860 861 862 863 864 865 866
		}

		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
				}
867
			}, sideBySide), err => this.sourceIsUnavailable(process, source, sideBySide)
E
Erich Gamma 已提交
868 869 870
		);
	}

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

875
		return this.editorService.openEditor(editorInput, { preserveFocus: true }, sideBySide);
E
Erich Gamma 已提交
876 877
	}

878 879
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
880 881
	}

I
isidor 已提交
882
	private lazyTransitionToRunningState(session: RawDebugSession, threadId?: number): void {
883
		let setNewFocusedStackFrameScheduler: RunOnceScheduler;
884

885
		const toDispose = session.onDidStop(e => {
886
			if (e.body.threadId === threadId || e.body.allThreadsStopped || !threadId) {
887
				setNewFocusedStackFrameScheduler.cancel();
888 889 890
			}
		});

891
		this.model.clearThreads(session.getId(), false, threadId);
892

893
		// Get a top stack frame of a stopped thread if there is any.
I
isidor 已提交
894 895 896

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

900
		if (!stoppedThread) {
I
isidor 已提交
901
			this.setStateAndEmit(session.getId(), this.configurationManager.configuration.noDebug ? debug.State.RunningNoDebug : debug.State.Running);
902 903 904 905 906 907 908 909 910
		}

		// 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);
911
		}, 500);
912
		setNewFocusedStackFrameScheduler.schedule();
913 914
	}

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

		return result;
	}

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

926
		return result;
E
Erich Gamma 已提交
927 928
	}

I
isidor 已提交
929
	private sendAllBreakpoints(session?: RawDebugSession): TPromise<any> {
I
isidor 已提交
930 931
		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 已提交
932
			// send exception breakpoints at the end since some debug adapters rely on the order
I
isidor 已提交
933
			.then(() => this.sendExceptionBreakpoints(session));
E
Erich Gamma 已提交
934 935
	}

I
isidor 已提交
936
	private sendBreakpoints(modelUri: uri, sourceModified = false, targetSession?: RawDebugSession): TPromise<void> {
I
isidor 已提交
937
		const sendBreakpointsToSession = (session: RawDebugSession): TPromise<void> => {
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 948 949 950 951 952 953 954 955 956 957 958 959 960 961
			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;
				}
962

I
isidor 已提交
963 964 965 966
				const data: { [id: string]: { line?: number, verified: boolean } } = {};
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
				}
967

I
isidor 已提交
968 969 970
				this.model.updateBreakpoints(data);
			});
		};
971

I
isidor 已提交
972
		return this.sendToOneOrAllSessions(targetSession, sendBreakpointsToSession);
E
Erich Gamma 已提交
973 974
	}

I
isidor 已提交
975 976
	private sendFunctionBreakpoints(targetSession?: RawDebugSession): TPromise<void> {
		const sendFunctionBreakpointsToSession = (session: RawDebugSession): TPromise<void> => {
I
isidor 已提交
977 978
			if (!session.readyForBreakpoints || !session.configuration.capabilities.supportsFunctionBreakpoints) {
				return TPromise.as(null);
979 980
			}

I
isidor 已提交
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
			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 已提交
996
		return this.sendToOneOrAllSessions(targetSession, sendFunctionBreakpointsToSession);
I
isidor 已提交
997 998
	}

I
isidor 已提交
999
	private sendExceptionBreakpoints(targetSession?: RawDebugSession): TPromise<void> {
I
isidor 已提交
1000
		const sendExceptionBreakpointsToSession = (session: RawDebugSession): TPromise<any> => {
I
isidor 已提交
1001 1002
			if (!session || !session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
				return TPromise.as(null);
I
isidor 已提交
1003 1004
			}

I
isidor 已提交
1005 1006 1007 1008
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

I
isidor 已提交
1009
		return this.sendToOneOrAllSessions(targetSession, sendExceptionBreakpointsToSession);
I
isidor 已提交
1010 1011
	}

I
isidor 已提交
1012
	private sendToOneOrAllSessions(session: RawDebugSession, send: (session: RawDebugSession) => TPromise<void>): TPromise<void> {
I
isidor 已提交
1013
		if (session) {
1014
			return send(session);
I
isidor 已提交
1015
		}
I
isidor 已提交
1016

I
isidor 已提交
1017
		return TPromise.join(this.model.getProcesses().map(p => send(<RawDebugSession>p.session))).then(() => void 0);
E
Erich Gamma 已提交
1018 1019 1020
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1021 1022
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
1023 1024 1025 1026 1027 1028 1029 1030

		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 已提交
1031 1032 1033 1034 1035
	}

	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);
1036
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1037
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1038
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
1039
		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 已提交
1040 1041 1042
	}

	public dispose(): void {
1043
		Object.keys(this.toDisposeOnSessionEnd).forEach(key => lifecycle.dispose(this.toDisposeOnSessionEnd[key]));
I
isidor 已提交
1044
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1045 1046
	}
}