debugService.ts 45.4 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');
I
isidor 已提交
8
import {guessMimeTypes} from 'vs/base/common/mime';
I
isidor 已提交
9
import Event, {Emitter} from 'vs/base/common/event';
E
Erich Gamma 已提交
10
import uri from 'vs/base/common/uri';
I
isidor 已提交
11 12
import {RunOnceScheduler} from 'vs/base/common/async';
import {Action} from 'vs/base/common/actions';
E
Erich Gamma 已提交
13
import arrays = require('vs/base/common/arrays');
14
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
15 16
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
I
isidor 已提交
17
import {TPromise} from 'vs/base/common/winjs.base';
I
isidor 已提交
18
import aria = require('vs/base/browser/ui/aria/aria');
E
Erich Gamma 已提交
19
import editorbrowser = require('vs/editor/browser/editorBrowser');
I
isidor 已提交
20 21
import {ISuggestion} from 'vs/editor/common/modes';
import {Position} from 'vs/editor/common/core/position';
A
Alex Dima 已提交
22
import {IContextKeyService, IContextKey} from 'vs/platform/contextkey/common/contextkey';
23
import {IMarkerService} from 'vs/platform/markers/common/markers';
I
isidor 已提交
24 25 26 27 28 29 30 31 32 33 34
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';
I
isidor 已提交
35
import {asFileEditorInput} from 'vs/workbench/common/editor';
E
Erich Gamma 已提交
36
import debug = require('vs/workbench/parts/debug/common/debug');
37
import {RawDebugSession} from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
E
Erich Gamma 已提交
38
import model = require('vs/workbench/parts/debug/common/debugModel');
I
isidor 已提交
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');
I
isidor 已提交
42 43 44 45 46 47 48 49 50
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';
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/parts/files/common/files';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
51
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
I
isidor 已提交
52 53 54 55 56
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IWindowService, IBroadcast} from 'vs/workbench/services/window/electron-browser/windowService';
import {ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL} from 'vs/workbench/services/thread/electron-browser/threadService';
import {ipcRenderer as ipc} from 'electron';
import {Client} from 'vs/base/parts/ipc/node/ipc.cp';
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 _state: debug.State;
69
	private _onDidChangeState: Emitter<debug.State>;
70
	private session: RawDebugSession;
E
Erich Gamma 已提交
71 72
	private model: model.Model;
	private viewModel: 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: 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 = [];
E
Erich Gamma 已提交
105
		this.session = null;
106
		this.breakpointsToSendOnResourceSaved = {};
I
isidor 已提交
107
		this._state = debug.State.Inactive;
108
		this._onDidChangeState = new Emitter<debug.State>();
E
Erich Gamma 已提交
109

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

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

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

141 142
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
143

I
isidor 已提交
144
		this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this));
145 146 147
	}

	private onBroadcast(broadcast: IBroadcast): void {
148

I
isidor 已提交
149
		// attach: PH is ready to be attached to
150
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
151
			this.rawAttach(broadcast.payload.port);
152 153
			return;
		}
154

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

I
isidor 已提交
160
		// from this point on we require an active session
161
		let session = this.getActiveSession();
I
isidor 已提交
162
		if (!session || session.configuration.type !== 'extensionHost') {
163 164 165
			return; // we are only intersted if we have an active debug session for extensionHost
		}

I
isidor 已提交
166
		// a plugin logged output, show it inside the REPL
167
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
168 169 170 171 172 173 174 175 176 177 178
			let extensionOutput: ILogEntry = broadcast.payload;
			let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;

			let args: any[] = [];
			try {
				let parsed = JSON.parse(extensionOutput.arguments);
				args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
			} catch (error) {
				args.push(extensionOutput.arguments);
			}

I
isidor 已提交
179
			// add output for each argument logged
180 181 182 183
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

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

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

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

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

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

I
isidor 已提交
207 208
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
				else if (typeof a === 'string') {
					let buf = '';

					for (let j = 0, len = a.length; j < len; j++) {
						if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd')) {
							i++; // read over substitution
							buf += !types.isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

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

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

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

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

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

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

268 269
				const thread = this.model.getThreads()[threadId];
				thread.getCallStack(this).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, thread).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, thread).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
280 281 282 283 284
					}
				});
			}, errors.onUnexpectedError);
		}));

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

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

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

308
		this.toDisposeOnSessionEnd.push(this.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.push(this.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.push(this.session.onDidExitAdapter(event => {
334
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
335
			if (this.session && this.session.configuration.type === 'extensionHost' && this._state === debug.State.RunningNoDebug) {
336 337
				ipc.send('vscode:closeExtensionHostWindow', this.contextService.getWorkspace().resource.fsPath);
			}
338
			if (this.session && this.session.getId() === event.body.sessionId) {
339 340
				this.onSessionEnd();
			}
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 350
	private getThreadData(): TPromise<void> {
		return this.session.threads().then(response => {
351 352 353
			if (response && response.body && response.body.threads) {
				response.body.threads.forEach(thread => this.model.rawUpdate({ threadId: thread.id, thread }));
			}
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);
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);
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 {
394 395
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string } ) => {
				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 403
	public get state(): debug.State {
		return this._state;
E
Erich Gamma 已提交
404 405
	}

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

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

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

419 420 421 422 423 424
	public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame, thread?: debug.IThread): TPromise<void> {
		if (!thread && focusedStackFrame) {
			thread = this.model.getThreads()[focusedStackFrame.threadId];
		}

		this.viewModel.setFocusedStackFrame(focusedStackFrame, thread);
E
Erich Gamma 已提交
425
		if (focusedStackFrame) {
I
isidor 已提交
426
			return this.model.evaluateWatchExpressions(this.session, focusedStackFrame);
E
Erich Gamma 已提交
427 428
		} else {
			this.model.clearWatchExpressionValues();
I
isidor 已提交
429
			return TPromise.as(null);
E
Erich Gamma 已提交
430 431 432
		}
	}

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

442
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
443 444
		}

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

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

		return TPromise.join(uris.map(uri => this.sendBreakpoints(uri)));
	}

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

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

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

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

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

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

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

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

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

501 502
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
503 504
	}

I
isidor 已提交
505 506 507 508 509
	public setVariable(variable: debug.IExpression, value: string): TPromise<any> {
		if (!this.session || !(variable instanceof model.Variable)) {
			return TPromise.as(null);
		}

A
Andre Weinand 已提交
510
		return this.session.setVariable({
I
isidor 已提交
511 512 513
			name: variable.name,
			value,
			variablesReference: (<model.Variable>variable).parent.reference
514
		}).then(response => {
515 516 517
			if (response && response.body) {
				variable.value = response.body.value;
			}
518 519
			// Evaluate all watch expressions again since changing variable value might have changed some #8118.
			return this.setFocusedStackFrameAndEvaluate(this.viewModel.getFocusedStackFrame());
520 521 522
		}, err => {
			(<model.Variable>variable).errorMessage = err.message;
		});
I
isidor 已提交
523 524
	}

I
isidor 已提交
525
	public addWatchExpression(name: string): TPromise<void> {
E
Erich Gamma 已提交
526 527 528
		return this.model.addWatchExpression(this.session, this.viewModel.getFocusedStackFrame(), name);
	}

I
isidor 已提交
529
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
E
Erich Gamma 已提交
530 531 532
		return this.model.renameWatchExpression(this.session, this.viewModel.getFocusedStackFrame(), id, newName);
	}

533 534
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
535 536
	}

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

540
		return this.textFileService.saveAll()							// make sure all dirty files are saved
541
			.then(() => this.configurationService.reloadConfiguration()	// make sure configuration is up to date
542
			.then(() => this.extensionService.onReady()
543
			.then(() => this.configurationManager.setConfiguration(configuration || this.configurationManager.configurationName)
544 545 546
			.then(() => this.configurationManager.resolveInteractiveVariables())
			.then(resolvedConfiguration => {
				configuration = resolvedConfiguration;
547 548 549 550 551 552 553
				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."));
						}
					});
				}
554 555 556
				if (configuration.silentlyAbort) {
					return;
				}
E
Erich Gamma 已提交
557

558 559 560
				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)))
I
isidor 已提交
561
						: TPromise.wrapError(errors.create(nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."),
562
							{ actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
563
				}
I
isidor 已提交
564

565 566 567 568 569
				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 已提交
570
						return this.doCreateSession(configuration);
571 572 573 574 575 576
					}

					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),
577
						actions: [new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
578
							this.messageService.hideAll();
I
isidor 已提交
579
							return this.doCreateSession(configuration);
580
						}), CloseAction]
581 582 583 584 585
					});
				}, (err: TaskError) => {
					if (err.code !== TaskErrors.NotConfigured) {
						throw err;
					}
586

587 588
					this.messageService.show(err.severity, {
						message: err.message,
589
						actions: [this.taskService.configureAction(), CloseAction]
590
					});
591
				});
592
			}))));
I
isidor 已提交
593
	}
E
Erich Gamma 已提交
594

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

J
Joao Moreno 已提交
598
		return this.telemetryService.getTelemetryInfo().then(info => {
599
			const telemetryInfo: { [key: string]: string } = Object.create(null);
600 601
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
602 603
			return telemetryInfo;
		}).then(data => {
604
			const { aiKey, type } = this.configurationManager.adapter;
605
			const publisher = this.configurationManager.adapter.extensionDescription.publisher;
606 607 608 609 610 611 612 613
			this.customTelemetryService = null;

			if (aiKey) {
				const client = new Client(
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
614
						args: [`${ publisher }.${ type }`, JSON.stringify(data), aiKey],
615
						env: {
B
Benjamin Pasero 已提交
616
							ELECTRON_RUN_AS_NODE: 1,
617 618 619
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
620
					}
621
				);
622

623 624
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
625

626 627
				this.toDisposeOnSessionEnd.push(client);
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
628 629
			}

630
			this.session = this.instantiationService.createInstance(RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.customTelemetryService);
J
Joao Moreno 已提交
631 632 633 634 635 636
			this.registerSessionListeners();

			return this.session.initialize({
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
637
				columnsStartAt1: true,
I
isidor 已提交
638
				supportsVariableType: true, // #8858
639 640
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
641 642 643 644
			}).then((result: DebugProtocol.InitializeResponse) => {
				if (!this.session) {
					return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
				}
645

J
Joao Moreno 已提交
646 647 648
				this.model.setExceptionBreakpoints(this.session.configuration.capabilities.exceptionBreakpointFilters);
				return configuration.request === 'attach' ? this.session.attach(configuration) : this.session.launch(configuration);
			}).then((result: DebugProtocol.Response) => {
649 650 651 652
				if (!this.session) {
					return TPromise.as(null);
				}

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

J
Joao Moreno 已提交
657 658 659 660 661
				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);
				}
662

J
Joao Moreno 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
				// 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);
				this.lazyTransitionToRunningState();

				this.telemetryService.publicLog('debugSessionStart', {
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
					extensionName: `${ this.configurationManager.adapter.extensionDescription.publisher }.${ this.configurationManager.adapter.extensionDescription.name }`,
					isBuiltin: this.configurationManager.adapter.extensionDescription.isBuiltin
				});
			}).then(undefined, (error: any) => {
680 681 682 683 684
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

J
Joao Moreno 已提交
685 686 687
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
				this.setStateAndEmit(debug.State.Inactive);
				if (this.session) {
688
					this.session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
689 690 691 692 693
				}
				// 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 已提交
694

J
Joao Moreno 已提交
695 696 697 698
				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 已提交
699 700 701
		});
	}

I
isidor 已提交
702
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
703
		if (!taskName) {
I
isidor 已提交
704
			return TPromise.as(null);
E
Erich Gamma 已提交
705 706
		}

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

I
isidor 已提交
720
			// task is already running - nothing to do.
721
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
722
				return TPromise.as(null);
E
Erich Gamma 已提交
723 724 725
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
726
				// there is a different task running currently.
I
isidor 已提交
727
				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 已提交
728 729
			}

I
isidor 已提交
730
			// no task running, execute the preLaunchTask.
731
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
732
				this.lastTaskEvent = null;
I
isidor 已提交
733
				return result;
E
Erich Gamma 已提交
734 735 736
			}, err => {
				this.lastTaskEvent = null;
			});
737

738
			if (filteredTasks[0].isWatching) {
A
Alex Dima 已提交
739
				return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
740 741 742
			}

			return taskPromise;
E
Erich Gamma 已提交
743 744 745
		});
	}

I
isidor 已提交
746
	private rawAttach(port: number): TPromise<any> {
I
isidor 已提交
747
		if (this.session) {
748
			return this.session.attach({ port });
I
isidor 已提交
749 750
		}

I
isidor 已提交
751
		this.setStateAndEmit(debug.State.Initializing);
I
isidor 已提交
752
		const configuration = <debug.IExtHostConfig>this.configurationManager.configuration;
I
isidor 已提交
753
		return this.doCreateSession({
754
			type: configuration.type,
I
isidor 已提交
755
			request: 'attach',
756 757
			port,
			sourceMaps: configuration.sourceMaps,
758 759
			outDir: configuration.outDir,
			debugServer: configuration.debugServer
I
isidor 已提交
760
		});
I
isidor 已提交
761 762
	}

I
isidor 已提交
763
	public restartSession(): TPromise<any> {
I
isidor 已提交
764
		return this.session ? this.session.disconnect(true).then(() =>
765
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
766
				setTimeout(() => {
I
isidor 已提交
767
					this.createSession(false, null).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
768
				}, 300);
I
isidor 已提交
769
			})
I
isidor 已提交
770
		) : this.createSession(false, null);
E
Erich Gamma 已提交
771 772 773 774 775 776 777 778
	}

	public getActiveSession(): debug.IRawDebugSession {
		return this.session;
	}

	private onSessionEnd(): void {
		if (this.session) {
I
isidor 已提交
779
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
780
			this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
781
				type: this.session.configuration.type,
I
isidor 已提交
782 783 784 785 786
				success: this.session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: this.session.getLengthInSeconds(),
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
787
		}
788

E
Erich Gamma 已提交
789
		this.session = null;
I
isidor 已提交
790 791 792 793 794 795
		try {
			this.toDisposeOnSessionEnd = lifecycle.dispose(this.toDisposeOnSessionEnd);
		} catch (e) {
			// an internal module might be open so the dispose can throw -> ignore and continue with stop session.
		}

E
Erich Gamma 已提交
796
		this.partService.removeClass('debugging');
797

798
		this.model.clearThreads(true);
I
isidor 已提交
799
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
800 801
		this.setStateAndEmit(debug.State.Inactive);

I
isidor 已提交
802
		// set breakpoints back to unverified since the session ended.
803
		// source reference changes across sessions, so we do not use it to persist the source.
I
isidor 已提交
804
		const data: { [id: string]: { line: number, verified: boolean } } = {};
805 806 807 808
		this.model.getBreakpoints().forEach(bp => {
			delete bp.source.raw.sourceReference;
			data[bp.getId()] = { line: bp.lineNumber, verified: false };
		});
809 810
		this.model.updateBreakpoints(data);

E
Erich Gamma 已提交
811 812 813 814 815 816 817 818 819 820 821
		this.inDebugMode.reset();
	}

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

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

822
	public openOrRevealSource(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
823
		const visibleEditors = this.editorService.getVisibleEditors();
I
isidor 已提交
824
		for (let i = 0; i < visibleEditors.length; i++) {
I
isidor 已提交
825
			const fileInput = asFileEditorInput(visibleEditors[i].input);
826 827 828
			if ((fileInput && fileInput.getResource().toString() === source.uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === source.uri.toString())) {

829 830 831 832
				const control = <editorbrowser.ICodeEditor>visibleEditors[i].getControl();
				if (control) {
					control.revealLineInCenterIfOutsideViewport(lineNumber);
					control.setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 });
833
					this.editorGroupService.activateGroup(i);
I
isidor 已提交
834
					if (!preserveFocus) {
835
						this.editorGroupService.focusGroup(i);
I
isidor 已提交
836
					}
E
Erich Gamma 已提交
837
				}
838

A
Alex Dima 已提交
839
				return TPromise.as(null);
E
Erich Gamma 已提交
840 841 842 843
			}
		}

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

			return this.sourceIsUnavailable(source, sideBySide);
		}

		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
				}
			}, sideBySide), err => this.sourceIsUnavailable(source, sideBySide)
		);
	}

I
isidor 已提交
877
	private sourceIsUnavailable(source: Source, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
878
		this.model.sourceIsUnavailable(source);
I
isidor 已提交
879
		const editorInput = this.getDebugErrorEditorInput(source, nls.localize('debugSourceNotAvailable', "Source {0} is not available.", source.name));
E
Erich Gamma 已提交
880

881
		return this.editorService.openEditor(editorInput, { preserveFocus: true }, sideBySide);
E
Erich Gamma 已提交
882 883
	}

884 885
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
886 887
	}

888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
	public next(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.next({ threadId }).then(() => {
			this.lazyTransitionToRunningState(threadId);
		});
	}

	public stepIn(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.stepIn({ threadId }).then(() => {
			this.lazyTransitionToRunningState(threadId);
		});
	}

	public stepOut(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.stepOut({ threadId }).then(() => {
914 915 916 917 918 919 920 921 922 923
			this.lazyTransitionToRunningState(threadId);
		});
	}

	public stepBack(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.stepBack({ threadId }).then(() => {
924 925 926 927 928 929 930 931 932
			this.lazyTransitionToRunningState(threadId);
		});
	}

	public continue(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

I
isidor 已提交
933
		return this.session.continue({ threadId }).then(response => {
934
			const allThreadsContinued = response && response.body ? response.body.allThreadsContinued !== false : true;
I
isidor 已提交
935
			this.lazyTransitionToRunningState(allThreadsContinued ? undefined : threadId);
936 937 938 939 940 941 942 943
		});
	}

	public pause(threadId: number): TPromise<any> {
		if (!this.session) {
			return TPromise.as(null);
		}

I
isidor 已提交
944
		return this.session.pause({ threadId });
945 946
	}

A
Andre Weinand 已提交
947 948 949 950 951 952 953 954
	public restartFrame(frameId: number): TPromise<any> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.restartFrame({ frameId });
	}

I
isidor 已提交
955
	public completions(text: string, position: Position): TPromise<ISuggestion[]> {
956
		if (!this.session || !this.session.configuration.capabilities.supportsCompletionsRequest) {
I
isidor 已提交
957 958
			return TPromise.as([]);
		}
I
isidor 已提交
959
		const focussedStackFrame = this.viewModel.getFocusedStackFrame();
I
isidor 已提交
960 961

		return this.session.completions({
I
isidor 已提交
962
			frameId: focussedStackFrame ? focussedStackFrame.frameId : undefined,
I
isidor 已提交
963 964 965
			text,
			column: position.column,
			line: position.lineNumber
I
isidor 已提交
966
		}).then(response => {
967
			return response && response.body && response.body.targets ? response.body.targets.map(item => ({
I
isidor 已提交
968 969 970
				label: item.label,
				insertText: item.text || item.label,
				type: item.type
971
			})) : [];
I
isidor 已提交
972
		}, err => []);
I
isidor 已提交
973 974
	}

975
	private lazyTransitionToRunningState(threadId?: number): void {
976
		let setNewFocusedStackFrameScheduler: RunOnceScheduler;
977

978
		const toDispose = this.session.onDidStop(e => {
979
			if (e.body.threadId === threadId || e.body.allThreadsStopped || !threadId) {
980
				setNewFocusedStackFrameScheduler.cancel();
981 982 983
			}
		});

I
isidor 已提交
984
		this.model.clearThreads(false, threadId);
985

986 987 988 989 990 991
		// Get a top stack frame of a stopped thread if there is any.
		const threads = this.model.getThreads();
		const stoppedReference = Object.keys(threads).filter(ref => threads[ref].stopped).pop();
		const stoppedThread = stoppedReference ? threads[parseInt(stoppedReference)] : null;
		const callStack = stoppedThread ? stoppedThread.getCachedCallStack() : null;
		const stackFrameToFocus = callStack && callStack.length > 0 ? callStack[0] : null;
992

993 994 995 996 997 998 999 1000 1001 1002 1003
		if (!stoppedThread) {
			this.setStateAndEmit(this.configurationManager.configuration.noDebug ? debug.State.RunningNoDebug : debug.State.Running);
		}

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

			this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
1004
		}, 500);
1005
		setNewFocusedStackFrameScheduler.schedule();
1006 1007
	}

1008
	private getDebugStringEditorInput(source: Source, value: string, mtype: string): DebugStringEditorInput {
1009 1010
		const result = this.instantiationService.createInstance(DebugStringEditorInput, source.name, source.uri, source.origin, value, mtype, void 0);
		this.toDisposeOnSessionEnd.push(result);
I
isidor 已提交
1011 1012 1013 1014 1015 1016 1017 1018

		return result;
	}

	private getDebugErrorEditorInput(source: Source, value: string): DebugErrorEditorInput {
		const result = this.instantiationService.createInstance(DebugErrorEditorInput, source.name, value);
		this.toDisposeOnSessionEnd.push(result);

1019
		return result;
E
Erich Gamma 已提交
1020 1021
	}

1022
	private sendAllBreakpoints(): TPromise<any> {
I
isidor 已提交
1023 1024 1025 1026
		return TPromise.join(arrays.distinct(this.model.getBreakpoints(), bp => bp.source.uri.toString()).map(bp => this.sendBreakpoints(bp.source.uri)))
			.then(() => this.sendFunctionBreakpoints())
			// send exception breakpoints at the end since some debug adapters rely on the order
			.then(() => this.sendExceptionBreakpoints());
E
Erich Gamma 已提交
1027 1028
	}

1029
	private sendBreakpoints(modelUri: uri, sourceModified = false): TPromise<void> {
1030
		if (!this.session || !this.session.readyForBreakpoints) {
I
isidor 已提交
1031
			return TPromise.as(null);
1032
		}
1033 1034 1035 1036 1037
		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);
		}
1038

1039 1040
		const breakpointsToSend = arrays.distinct(
			this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.source.uri.toString() === modelUri.toString()),
I
isidor 已提交
1041
			bp => `${bp.desiredLineNumber}`
1042
		);
I
isidor 已提交
1043
		const rawSource = breakpointsToSend.length > 0 ? breakpointsToSend[0].source.raw : Source.toRawSource(modelUri, this.model);
1044

I
isidor 已提交
1045 1046 1047
		return this.session.setBreakpoints({
			source: rawSource,
			lines: breakpointsToSend.map(bp => bp.desiredLineNumber),
1048 1049 1050
			breakpoints: breakpointsToSend.map(bp => ({ line: bp.desiredLineNumber, condition: bp.condition })),
			sourceModified
		}).then(response => {
1051 1052 1053 1054
			if (!response || !response.body) {
				return;
			}

I
isidor 已提交
1055
			const data: { [id: string]: { line?: number, verified: boolean } } = {};
1056 1057 1058 1059 1060
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateBreakpoints(data);
E
Erich Gamma 已提交
1061 1062 1063
		});
	}

I
isidor 已提交
1064
	private sendFunctionBreakpoints(): TPromise<void> {
I
isidor 已提交
1065
		if (!this.session || !this.session.readyForBreakpoints || !this.session.configuration.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1066 1067 1068
			return TPromise.as(null);
		}

I
isidor 已提交
1069
		const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
I
isidor 已提交
1070
		return this.session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => {
1071 1072 1073 1074
			if (!response || !response.body) {
				return;
			}

I
isidor 已提交
1075
			const data: { [id: string]: { name?: string, verified?: boolean } } = {};
I
isidor 已提交
1076 1077 1078 1079 1080
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateFunctionBreakpoints(data);
I
isidor 已提交
1081 1082 1083
		});
	}

I
isidor 已提交
1084
	private sendExceptionBreakpoints(): TPromise<any> {
1085
		if (!this.session || !this.session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
I
isidor 已提交
1086
			return TPromise.as(null);
I
isidor 已提交
1087
		}
I
isidor 已提交
1088

I
isidor 已提交
1089
		const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
1090
		return this.session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
E
Erich Gamma 已提交
1091 1092 1093
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1094 1095
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
1096 1097 1098 1099 1100 1101 1102 1103

		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 已提交
1104 1105 1106 1107 1108
	}

	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);
1109
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1110
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1111
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
1112
		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 已提交
1113 1114 1115
	}

	public dispose(): void {
J
Joao Moreno 已提交
1116
		this.toDisposeOnSessionEnd = lifecycle.dispose(this.toDisposeOnSessionEnd);
I
isidor 已提交
1117
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1118 1119
	}
}