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

import nls = require('vs/nls');
import lifecycle = require('vs/base/common/lifecycle');
J
Johannes Rieken 已提交
8 9
import { guessMimeTypes } from 'vs/base/common/mime';
import Event, { Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
10
import uri from 'vs/base/common/uri';
J
Johannes Rieken 已提交
11 12
import { RunOnceScheduler } from 'vs/base/common/async';
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
13
import arrays = require('vs/base/common/arrays');
14
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
15 16
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
17
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
18
import aria = require('vs/base/browser/ui/aria/aria');
E
Erich Gamma 已提交
19
import editorbrowser = require('vs/editor/browser/editorBrowser');
J
Johannes Rieken 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import { ISuggestion } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
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 57
import { ipcRenderer as ipc } from 'electron';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
E
Erich Gamma 已提交
58

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

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

I
isidor 已提交
69
	private _state: debug.State;
70
	private _onDidChangeState: Emitter<debug.State>;
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 = [];
105
		this.breakpointsToSendOnResourceSaved = {};
I
isidor 已提交
106
		this._state = debug.State.Inactive;
107
		this._onDidChangeState = new Emitter<debug.State>();
E
Erich Gamma 已提交
108

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

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

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

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

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

146 147
	private get session(): debug.ISession {
		return this.viewModel.focusedProcess ? (<model.Process>this.viewModel.focusedProcess).session : null;
148 149
	}

150
	private onBroadcast(broadcast: IBroadcast): void {
151

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

538
		return this.textFileService.saveAll()							// make sure all dirty files are saved
539
			.then(() => this.configurationService.reloadConfiguration()	// make sure configuration is up to date
J
Johannes Rieken 已提交
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 581 582 583 584 585 586 587 588 589 590
				.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 已提交
591
	}
E
Erich Gamma 已提交
592

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

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

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

621 622
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
623

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

628 629
			const session = this.instantiationService.createInstance(RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.customTelemetryService);
			this.registerSessionListeners(session);
J
Joao Moreno 已提交
630

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

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

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

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

J
Joao Moreno 已提交
661 662 663 664 665 666
				// 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);
667
				this.lazyTransitionToRunningState(session);
J
Joao Moreno 已提交
668 669 670 671 672 673

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

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

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

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

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

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

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

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

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

			return taskPromise;
E
Erich Gamma 已提交
741 742 743
		});
	}

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

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

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

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

I
isidor 已提交
783 784 785 786 787 788
		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 已提交
789
		this.partService.removeClass('debugging');
790

791
		this.model.removeProcess(session.getId());
I
isidor 已提交
792
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
793 794
		this.setStateAndEmit(debug.State.Inactive);

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

E
Erich Gamma 已提交
804
		this.inDebugMode.reset();
I
isidor 已提交
805 806 807 808

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

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

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

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

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

A
Alex Dima 已提交
836
				return TPromise.as(null);
E
Erich Gamma 已提交
837 838 839 840
			}
		}

		if (source.inMemory) {
I
isidor 已提交
841
			// internal module
842
			if (source.reference !== 0 && this.session && source.available) {
843
				return this.session.source({ sourceReference: source.reference }).then(response => {
844 845 846
					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);
847 848
				}, (err: DebugProtocol.ErrorResponse) => {
					// Display the error from debug adapter using a temporary editor #8836
I
isidor 已提交
849
					return this.getDebugErrorEditorInput(source, err.message);
850
				}).then(editorInput => {
B
Benjamin Pasero 已提交
851
					return this.editorService.openEditor(editorInput, { preserveFocus, selection: { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 } }, sideBySide);
E
Erich Gamma 已提交
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
				});
			}

			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 已提交
874
	private sourceIsUnavailable(source: Source, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
875
		this.model.sourceIsUnavailable(source);
I
isidor 已提交
876
		const editorInput = this.getDebugErrorEditorInput(source, nls.localize('debugSourceNotAvailable', "Source {0} is not available.", source.name));
E
Erich Gamma 已提交
877

878
		return this.editorService.openEditor(editorInput, { preserveFocus: true }, sideBySide);
E
Erich Gamma 已提交
879 880
	}

881 882
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
883 884
	}

885 886 887 888 889 890
	public next(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

		return this.session.next({ threadId }).then(() => {
891
			this.lazyTransitionToRunningState(this.session, threadId);
892 893 894 895 896 897 898 899 900
		});
	}

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

		return this.session.stepIn({ threadId }).then(() => {
901
			this.lazyTransitionToRunningState(this.session, threadId);
902 903 904 905 906 907 908 909 910
		});
	}

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

		return this.session.stepOut({ threadId }).then(() => {
911
			this.lazyTransitionToRunningState(this.session, threadId);
912 913 914 915 916 917 918 919 920
		});
	}

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

		return this.session.stepBack({ threadId }).then(() => {
921
			this.lazyTransitionToRunningState(this.session, threadId);
922 923 924 925 926 927 928 929
		});
	}

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

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

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

I
isidor 已提交
941
		return this.session.pause({ threadId });
942 943
	}

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

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

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

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

972
	private lazyTransitionToRunningState(session: debug.ISession, threadId?: number): void {
973
		let setNewFocusedStackFrameScheduler: RunOnceScheduler;
974

975
		const toDispose = session.onDidStop(e => {
976
			if (e.body.threadId === threadId || e.body.allThreadsStopped || !threadId) {
977
				setNewFocusedStackFrameScheduler.cancel();
978 979 980
			}
		});

981
		this.model.clearThreads(session.getId(), false, threadId);
982

983
		// Get a top stack frame of a stopped thread if there is any.
984
		const threads = this.model.getThreads(session.getId());
985 986 987 988
		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;
989

990 991 992 993 994 995 996 997 998 999 1000
		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);
1001
		}, 500);
1002
		setNewFocusedStackFrameScheduler.schedule();
1003 1004
	}

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

		return result;
	}

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

1016
		return result;
E
Erich Gamma 已提交
1017 1018
	}

1019
	private sendAllBreakpoints(session?: debug.ISession): TPromise<any> {
I
isidor 已提交
1020 1021
		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 已提交
1022
			// send exception breakpoints at the end since some debug adapters rely on the order
I
isidor 已提交
1023
			.then(() => this.sendExceptionBreakpoints(session));
E
Erich Gamma 已提交
1024 1025
	}

1026 1027
	private sendBreakpoints(modelUri: uri, sourceModified = false, session?: debug.ISession): TPromise<void> {
		const sendBreakpointsToSession = (session: debug.ISession): TPromise<void> => {
I
isidor 已提交
1028 1029 1030 1031 1032 1033 1034 1035
			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);
			}
1036

I
isidor 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
			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;
				}
1052

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

I
isidor 已提交
1058 1059 1060
				this.model.updateBreakpoints(data);
			});
		};
1061

I
isidor 已提交
1062
		return this.sendToOneOrAllSessions(session, sendBreakpointsToSession);
E
Erich Gamma 已提交
1063 1064
	}

1065 1066
	private sendFunctionBreakpoints(session?: debug.ISession): TPromise<void> {
		const sendFunctionBreakpointsToSession = (session: debug.ISession): TPromise<void> => {
I
isidor 已提交
1067 1068
			if (!session.readyForBreakpoints || !session.configuration.capabilities.supportsFunctionBreakpoints) {
				return TPromise.as(null);
1069 1070
			}

I
isidor 已提交
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
			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);
			});
		};

		return this.sendToOneOrAllSessions(session, sendFunctionBreakpointsToSession);
	}

1089 1090
	private sendExceptionBreakpoints(session?: debug.ISession): TPromise<void> {
		const sendExceptionBreakpointsToSession = (session: debug.ISession): TPromise<any> => {
I
isidor 已提交
1091 1092
			if (!session || !session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
				return TPromise.as(null);
I
isidor 已提交
1093 1094
			}

I
isidor 已提交
1095 1096 1097 1098 1099
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

		return this.sendToOneOrAllSessions(session, sendExceptionBreakpointsToSession);
I
isidor 已提交
1100 1101
	}

1102
	private sendToOneOrAllSessions(session: debug.ISession, send: (session: debug.ISession) => TPromise<void>): TPromise<void> {
I
isidor 已提交
1103
		if (session) {
1104
			return send(session);
I
isidor 已提交
1105
		}
I
isidor 已提交
1106

1107
		return TPromise.join(this.model.getProcesses().map(p => send(p.session))).then(() => void 0);
E
Erich Gamma 已提交
1108 1109 1110
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1111 1112
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
1113 1114 1115 1116 1117 1118 1119 1120

		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 已提交
1121 1122 1123 1124 1125
	}

	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);
1126
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
1127
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
1128
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
1129
		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 已提交
1130 1131 1132
	}

	public dispose(): void {
J
Joao Moreno 已提交
1133
		this.toDisposeOnSessionEnd = lifecycle.dispose(this.toDisposeOnSessionEnd);
I
isidor 已提交
1134
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1135 1136
	}
}