debugService.ts 38.2 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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');
import mime = require('vs/base/common/mime');
9
import Event, { Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
10
import uri from 'vs/base/common/uri';
11
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
12
import arrays = require('vs/base/common/arrays');
13
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
14 15
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
I
isidor 已提交
16
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
17
import aria = require('vs/base/browser/ui/aria/aria');
18
import { AIAdapter } from 'vs/base/node/aiAdapter';
E
Erich Gamma 已提交
19
import editorbrowser = require('vs/editor/browser/editorBrowser');
20 21 22
import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService';
import {IMarkerService} from 'vs/platform/markers/common/markers';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
A
Alex Dima 已提交
23
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
24 25 26 27 28 29
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 { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
E
Erich Gamma 已提交
30 31 32 33
import wbeditorcommon = require('vs/workbench/common/editor');
import debug = require('vs/workbench/parts/debug/common/debug');
import session = require('vs/workbench/parts/debug/node/rawDebugSession');
import model = require('vs/workbench/parts/debug/common/debugModel');
34
import { DebugStringEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
E
Erich Gamma 已提交
35
import viewmodel = require('vs/workbench/parts/debug/common/debugViewModel');
36
import debugactions = require('vs/workbench/parts/debug/electron-browser/debugActions');
I
isidor 已提交
37
import { BreakpointWidget } from 'vs/workbench/parts/debug/browser/breakpointWidget';
38
import { ConfigurationManager } from 'vs/workbench/parts/debug/node/debugConfigurationManager';
I
isidor 已提交
39
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
40 41
import { ITaskService, TaskEvent, TaskType, TaskServiceEvents, ITaskSummary} from 'vs/workbench/parts/tasks/common/taskService';
import { TaskError, TaskErrors } from 'vs/workbench/parts/tasks/common/taskSystem';
E
Erich Gamma 已提交
42
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
I
isidor 已提交
43
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
E
Erich Gamma 已提交
44
import { IPartService } from 'vs/workbench/services/part/common/partService';
45
import { ITextFileService } from 'vs/workbench/parts/files/common/files';
E
Erich Gamma 已提交
46 47
import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
48
import { IWindowService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService';
49
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/services/thread/electron-browser/threadService';
50
import { ipcRenderer as ipc } from 'electron';
E
Erich Gamma 已提交
51

I
isidor 已提交
52 53 54 55 56 57
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 已提交
58

59
export class DebugService implements debug.IDebugService {
E
Erich Gamma 已提交
60 61 62
	public serviceId = debug.IDebugService;

	private state: debug.State;
63
	private _onDidChangeState: Emitter<debug.State>;
I
isidor 已提交
64
	private session: session.RawDebugSession;
E
Erich Gamma 已提交
65 66
	private model: model.Model;
	private viewModel: viewmodel.ViewModel;
67
	private configurationManager: ConfigurationManager;
68
	private debugStringEditorInputs: DebugStringEditorInput[];
69
	private telemetryAdapter: AIAdapter;
E
Erich Gamma 已提交
70
	private lastTaskEvent: TaskEvent;
71
	private toDispose: lifecycle.IDisposable[];
72
	private toDisposeOnSessionEnd: lifecycle.IDisposable[];
E
Erich Gamma 已提交
73 74 75 76 77
	private inDebugMode: IKeybindingContextKey<boolean>;

	constructor(
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
78 79
		@ITextFileService private textFileService: ITextFileService,
		@IViewletService private viewletService: IViewletService,
I
isidor 已提交
80
		@IPanelService private panelService: IPanelService,
E
Erich Gamma 已提交
81 82 83 84 85 86
		@IFileService private fileService: IFileService,
		@IMessageService private messageService: IMessageService,
		@IPartService private partService: IPartService,
		@IWindowService private windowService: IWindowService,
		@ITelemetryService private telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
87 88
		@IKeybindingService keybindingService: IKeybindingService,
		@IEventService eventService: IEventService,
E
Erich Gamma 已提交
89 90
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IInstantiationService private instantiationService:IInstantiationService,
A
Alex Dima 已提交
91
		@IExtensionService private extensionService: IExtensionService,
92 93
		@IMarkerService private markerService: IMarkerService,
		@ITaskService private taskService: ITaskService
E
Erich Gamma 已提交
94 95
	) {
		this.toDispose = [];
96
		this.toDisposeOnSessionEnd = [];
E
Erich Gamma 已提交
97 98 99
		this.debugStringEditorInputs = [];
		this.session = null;
		this.state = debug.State.Inactive;
100
		this._onDidChangeState = new Emitter<debug.State>();
E
Erich Gamma 已提交
101

102
		if (!this.contextService.getWorkspace()) {
E
Erich Gamma 已提交
103 104
			this.state = debug.State.Disabled;
		}
105
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, 'null'));
E
Erich Gamma 已提交
106 107
		this.inDebugMode = keybindingService.createKey(debug.CONTEXT_IN_DEBUG_MODE, false);

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

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

133 134
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
135

I
isidor 已提交
136
		this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this));
137 138 139
	}

	private onBroadcast(broadcast: IBroadcast): void {
140

I
isidor 已提交
141
		// attach: PH is ready to be attached to
142
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
143
			this.rawAttach(broadcast.payload.port);
144 145
			return;
		}
146

147 148
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
			this.onSessionEnd();
149 150 151
			return;
		}

I
isidor 已提交
152
		// from this point on we require an active session
153
		let session = this.getActiveSession();
I
isidor 已提交
154
		if (!session || session.configuration.type !== 'extensionHost') {
155 156 157
			return; // we are only intersted if we have an active debug session for extensionHost
		}

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

I
isidor 已提交
176
				// undefined gets printed as 'undefined'
177 178 179 180
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

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

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

I
isidor 已提交
189
					// flush any existing simple values logged
190 191 192 193 194
					if (simpleVals.length) {
						this.logToRepl(simpleVals.join(' '), sev);
						simpleVals = [];
					}

I
isidor 已提交
195
					// show object
196 197 198
					this.logToRepl(a, sev);
				}

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

	private registerSessionListeners(): void {
I
isidor 已提交
231
		this.toDisposeOnSessionEnd.push(this.session);
232
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.INITIALIZED, (event: DebugProtocol.InitializedEvent) => {
233
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
I
isidor 已提交
234
			this.sendAllBreakpoints().then(() => {
I
isidor 已提交
235
				if (this.session.configuration.capabilities.supportsConfigurationDoneRequest) {
I
isidor 已提交
236 237
					this.session.configurationDone().done(null, errors.onUnexpectedError);
				}
I
isidor 已提交
238 239
			});
		}));
E
Erich Gamma 已提交
240

241
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.STOPPED, (event: DebugProtocol.StoppedEvent) => {
242
			this.setStateAndEmit(debug.State.Stopped);
I
isidor 已提交
243
			const threadId = event.body.threadId;
E
Erich Gamma 已提交
244

245 246
			this.getThreadData(threadId).done(() => {
				let thread = this.model.getThreads()[threadId];
E
Erich Gamma 已提交
247

248 249 250 251 252
				this.model.rawUpdate({
					threadId: threadId,
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
253

254 255
				thread.getCallStack(this).then(callStack => {
					this.windowService.getWindow().focus();
E
Erich Gamma 已提交
256
					if (callStack.length > 0) {
257
						// focus first stack frame from top that has source location
I
isidor 已提交
258
						const stackFrameToFocus = arrays.first(callStack, sf => sf.source && sf.source.available, callStack[0]);
I
isidor 已提交
259
						this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
260 261 262
						aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.lineNumber));

						return this.openOrRevealEditor(stackFrameToFocus.source, stackFrameToFocus.lineNumber, false, false);
E
Erich Gamma 已提交
263
					} else {
I
isidor 已提交
264
						this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
265 266 267 268 269
					}
				});
			}, errors.onUnexpectedError);
		}));

270
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.CONTINUED, () => {
271
			aria.status(nls.localize('debuggingContinued', "Debugging continued."));
272
			this.model.continueThreads();
I
isidor 已提交
273
			this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
274
			this.setStateAndEmit(this.configurationManager.configuration.noDebug ? debug.State.RunningNoDebug : debug.State.Running);
E
Erich Gamma 已提交
275 276
		}));

277
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.THREAD, (event: DebugProtocol.ThreadEvent) => {
E
Erich Gamma 已提交
278 279
			if (event.body.reason === 'started') {
				this.session.threads().done((result) => {
I
isidor 已提交
280 281
					const thread = result.body.threads.filter(thread => thread.id === event.body.threadId).pop();
					if (thread) {
E
Erich Gamma 已提交
282
						this.model.rawUpdate({
I
isidor 已提交
283 284
							threadId: thread.id,
							thread: thread
E
Erich Gamma 已提交
285 286 287 288 289 290 291 292
						});
					}
				}, errors.onUnexpectedError);
			} else if (event.body.reason === 'exited') {
				this.model.clearThreads(true, event.body.threadId);
			}
		}));

293
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.DEBUGEE_TERMINATED, (event: DebugProtocol.TerminatedEvent) => {
294
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
I
isidor 已提交
295
			if (this.session && this.session.getId() === (<any>event).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
			}
		}));

304
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.OUTPUT, (event: DebugProtocol.OutputEvent) => {
305
			if (event.body && event.body.category === 'telemetry') {
306
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
307 308
				if (this.telemetryAdapter) {
					this.telemetryAdapter.log(event.body.output, event.body.data);
309
				}
310
			} else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) {
E
Erich Gamma 已提交
311 312 313 314
				this.onOutput(event);
			}
		}));

315
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.BREAKPOINT, (event: DebugProtocol.BreakpointEvent) => {
I
isidor 已提交
316
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
317
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
318 319 320
			if (breakpoint) {
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
321
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
322 323 324 325 326 327
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

328
		this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.SERVER_EXIT, event => {
329
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
330
			if (this.session.configuration.type === 'extensionHost' && this.state === debug.State.RunningNoDebug) {
331 332
				ipc.send('vscode:closeExtensionHostWindow', this.contextService.getWorkspace().resource.fsPath);
			}
333 334 335
			if (this.session && this.session.getId() === event.sessionId) {
				this.onSessionEnd();
			}
E
Erich Gamma 已提交
336 337 338 339
		}));
	}

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

I
isidor 已提交
344 345
	private getThreadData(threadId: number): TPromise<void> {
		return this.model.getThreads()[threadId] ? TPromise.as(undefined) :
E
Erich Gamma 已提交
346
			this.session.threads().then((response: DebugProtocol.ThreadsResponse) => {
I
isidor 已提交
347 348
				const thread = response.body.threads.filter(t => t.id === threadId).pop();
				if (!thread) {
I
isidor 已提交
349
					throw new Error(nls.localize('debugNoThread', "Did not get a thread from debug adapter with id {0}.", threadId));
E
Erich Gamma 已提交
350 351 352
				}

				this.model.rawUpdate({
I
isidor 已提交
353 354
					threadId: thread.id,
					thread: thread
E
Erich Gamma 已提交
355 356 357 358 359 360 361
				});
			});
	}

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

I
isidor 已提交
370 371 372
	private loadFunctionBreakpoints(): debug.IFunctionBreakpoint[] {
		try {
			return 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 378 379
			});
		} catch (e) {
			return [];
		}
	}

E
Erich Gamma 已提交
380
	private loadExceptionBreakpoints(): debug.IExceptionBreakpoint[] {
I
isidor 已提交
381
		let result: debug.IExceptionBreakpoint[] = null;
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 387 388 389
			});
		} catch (e) {
			result = [];
		}

390
		return result;
E
Erich Gamma 已提交
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
	}

	private loadWatchExpressions(): model.Expression[] {
		try {
			return JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watch: any) => {
				return new model.Expression(watch.name, false, watch.id);
			});
		} catch (e) {
			return [];
		}
	}

	public getState(): debug.State {
		return this.state;
	}

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

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

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

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

430
	public setBreakpointsForModel(modelUri: uri, rawData: debug.IRawBreakpoint[]): void {
431 432 433
		this.model.removeBreakpoints(
			this.model.getBreakpoints().filter(bp => bp.source.uri.toString() === modelUri.toString()));
		this.model.addBreakpoints(rawData);
E
Erich Gamma 已提交
434 435
	}

I
isidor 已提交
436
	public toggleBreakpoint(rawBreakpoint: debug.IRawBreakpoint): TPromise<void> {
437
		const breakpoint = this.model.getBreakpoints().filter(bp => bp.lineNumber === rawBreakpoint.lineNumber && bp.source.uri.toString() === rawBreakpoint.uri.toString()).pop();
438
		if (breakpoint) {
439
			this.model.removeBreakpoints([breakpoint]);
440
		} else {
441
			this.model.addBreakpoints([rawBreakpoint]);
442 443
		}

444
		return this.sendBreakpoints(rawBreakpoint.uri);
E
Erich Gamma 已提交
445 446
	}

I
isidor 已提交
447
	public enableOrDisableAllBreakpoints(enabled: boolean): TPromise<void>{
E
Erich Gamma 已提交
448 449 450 451
		this.model.enableOrDisableAllBreakpoints(enabled);
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
452
	public toggleEnablement(element: debug.IEnablement): TPromise<void> {
E
Erich Gamma 已提交
453 454
		this.model.toggleEnablement(element);
		if (element instanceof model.Breakpoint) {
I
isidor 已提交
455
			const breakpoint = <model.Breakpoint> element;
E
Erich Gamma 已提交
456
			return this.sendBreakpoints(breakpoint.source.uri);
457
		} else if (element instanceof model.FunctionBreakpoint) {
I
isidor 已提交
458
			return this.sendFunctionBreakpoints();
E
Erich Gamma 已提交
459 460 461 462 463
		}

		return this.sendExceptionBreakpoints();
	}

I
isidor 已提交
464
	public removeAllBreakpoints(): TPromise<any> {
465 466
		const urisToClear = arrays.distinct(this.model.getBreakpoints(), bp => bp.source.uri.toString()).map(bp => bp.source.uri);
		this.model.removeBreakpoints(this.model.getBreakpoints());
E
Erich Gamma 已提交
467

I
isidor 已提交
468
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
469 470
	}

I
isidor 已提交
471
	public toggleBreakpointsActivated(): TPromise<void> {
E
Erich Gamma 已提交
472 473 474 475
		this.model.toggleBreakpointsActivated();
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
476
	public editBreakpoint(editor: editorbrowser.ICodeEditor, lineNumber: number): TPromise<void> {
477 478
		if (BreakpointWidget.INSTANCE) {
			BreakpointWidget.INSTANCE.dispose();
479
		}
480 481 482

		this.instantiationService.createInstance(BreakpointWidget, editor, lineNumber);
		BreakpointWidget.INSTANCE.show({ lineNumber, column: 1 }, 2);
I
isidor 已提交
483

I
isidor 已提交
484
		return TPromise.as(null);
485 486
	}

I
isidor 已提交
487 488
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
489 490
	}

I
isidor 已提交
491
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
492
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
493
		return this.sendFunctionBreakpoints();
I
isidor 已提交
494 495
	}

I
isidor 已提交
496
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
497
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
498
		return this.sendFunctionBreakpoints();
I
isidor 已提交
499 500
	}

I
isidor 已提交
501
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
502
		this.telemetryService.publicLog('debugService/addReplExpression');
E
Erich Gamma 已提交
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
		return this.model.addReplExpression(this.session, this.viewModel.getFocusedStackFrame(), name);
	}

	public logToRepl(value: string, severity?: severity): void;
	public logToRepl(value: { [key: string]: any }, severity?: severity): void;
	public logToRepl(value: any, severity?: severity): void {
		this.model.logToRepl(value, severity);
	}

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

	public clearReplExpressions(): void {
		this.model.clearReplExpressions();
	}

I
isidor 已提交
520
	public addWatchExpression(name: string): TPromise<void> {
E
Erich Gamma 已提交
521 522 523
		return this.model.addWatchExpression(this.session, this.viewModel.getFocusedStackFrame(), name);
	}

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

	public clearWatchExpressions(id?: string): void {
		this.model.clearWatchExpressions(id);
	}

532
	public createSession(noDebug: boolean, changeViewState = !this.partService.isSideBarHidden()): TPromise<any> {
533
		this.clearReplExpressions();
E
Erich Gamma 已提交
534

535 536
		return this.textFileService.saveAll()
		.then(() => this.extensionService.onReady()
537
		.then(() => this.configurationManager.setConfiguration((this.configurationManager.configurationName))
538
		.then(() => {
539
			const configuration = this.configurationManager.configuration;
540 541
			if (!configuration) {
				return this.configurationManager.openConfigFile(false).then(openend => {
542
					if (openend) {
I
isidor 已提交
543
						this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application."));
544 545
					}
				});
E
Erich Gamma 已提交
546
			}
547

548
			configuration.noDebug = noDebug;
549
			if (!this.configurationManager.adapter) {
550 551 552
				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 selected configuration in launch.json."),
						{ actions: [CloseAction, this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL)] }));
E
Erich Gamma 已提交
553 554
			}

I
isidor 已提交
555
			return this.runPreLaunchTask(configuration.preLaunchTask).then((taskSummary: ITaskSummary) => {
556
				const errorCount = configuration.preLaunchTask ? this.markerService.getStatistics().errors : 0;
I
isidor 已提交
557 558
				const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
				if (errorCount === 0 && !failureExitCode) {
I
isidor 已提交
559
					return this.doCreateSession(configuration, changeViewState);
560
				}
I
isidor 已提交
561 562

				this.messageService.show(severity.Error, {
I
isidor 已提交
563 564
					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) :
I
isidor 已提交
565
						nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", configuration.preLaunchTask, taskSummary.exitCode),
I
isidor 已提交
566
					actions: [CloseAction, new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
I
isidor 已提交
567
						this.messageService.hideAll();
I
isidor 已提交
568
						return this.doCreateSession(configuration, changeViewState);
I
isidor 已提交
569 570
					})]
				});
571 572
			}, (err: TaskError) => {
				if (err.code !== TaskErrors.NotConfigured) {
573
					throw err;
574 575 576 577 578 579
				}

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

I
isidor 已提交
584
	private doCreateSession(configuration: debug.IConfig, changeViewState: boolean): TPromise<any> {
585
		this.setStateAndEmit(debug.State.Initializing);
586
		const key = this.configurationManager.adapter.aiKey;
587 588 589 590 591
		const telemetryInfo = Object.create(null);
		this.telemetryService.getTelemetryInfo().then(info => {
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
		}, errors.onUnexpectedError);
592 593
		this.telemetryAdapter = new AIAdapter(key, this.configurationManager.adapter.type, null, telemetryInfo);
		this.session = new session.RawDebugSession(this.messageService, this.telemetryService, configuration.debugServer, this.configurationManager.adapter, this.telemetryAdapter);
594

I
isidor 已提交
595 596 597 598
		this.registerSessionListeners();

		return this.session.initialize({
			adapterID: configuration.type,
599
			pathFormat: 'path',
I
isidor 已提交
600
			linesStartAt1: true,
601
			columnsStartAt1: true
I
isidor 已提交
602
		}).then((result: DebugProtocol.InitializeResponse) => {
603
			if (!this.session) {
I
isidor 已提交
604
				return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
605 606
			}

I
isidor 已提交
607
			this.model.setExceptionBreakpoints(this.session.configuration.capabilities.exceptionBreakpointFilters);
I
isidor 已提交
608 609
			return configuration.request === 'attach' ? this.session.attach(configuration) : this.session.launch(configuration);
		}).then((result: DebugProtocol.Response) => {
I
isidor 已提交
610
			if (changeViewState) {
I
isidor 已提交
611
				this.viewletService.openViewlet(debug.VIEWLET_ID);
612
				this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
I
isidor 已提交
613
			}
614 615 616 617 618

			// Do not change status bar to orange if we are just running without debug.
			if (!configuration.noDebug) {
				this.partService.addClass('debugging');
			}
619
			this.extensionService.activateByEvent(`onDebug:${ configuration.type }`).done(null, errors.onUnexpectedError);
I
isidor 已提交
620 621
			this.contextService.updateOptions('editor', {
				glyphMargin: true
E
Erich Gamma 已提交
622
			});
I
isidor 已提交
623 624
			this.inDebugMode.set(true);

P
Pierson Lee 已提交
625
			this.telemetryService.publicLog('debugSessionStart', { type: configuration.type, breakpointCount: this.model.getBreakpoints().length, exceptionBreakpoints: this.model.getExceptionBreakpoints(), watchExpressionsCount: this.model.getWatchExpressions().length });
626
		}).then(undefined, (error: any) => {
I
isidor 已提交
627
			this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
I
isidor 已提交
628
			this.setStateAndEmit(debug.State.Inactive);
I
isidor 已提交
629 630 631 632
			if (this.session) {
				this.session.disconnect();
			}

I
isidor 已提交
633 634
			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];
635
			return TPromise.wrapError(errors.create(error.message, { actions }));
E
Erich Gamma 已提交
636 637 638
		});
	}

I
isidor 已提交
639
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
640
		if (!taskName) {
I
isidor 已提交
641
			return TPromise.as(null);
E
Erich Gamma 已提交
642 643
		}

644
		// run a task before starting a debug session
E
Erich Gamma 已提交
645
		return this.taskService.tasks().then(descriptions => {
646
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
647
			if (filteredTasks.length !== 1) {
648 649 650 651 652 653 654
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName), {
					actions: [
						CloseAction,
						this.taskService.configureAction(),
						this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL)
					]
				}));
E
Erich Gamma 已提交
655 656
			}

I
isidor 已提交
657
			// task is already running - nothing to do.
658
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
659
				return TPromise.as(null);
E
Erich Gamma 已提交
660 661 662
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
663
				// there is a different task running currently.
I
isidor 已提交
664
				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 已提交
665 666
			}

I
isidor 已提交
667
			// no task running, execute the preLaunchTask.
668
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
669
				this.lastTaskEvent = null;
I
isidor 已提交
670
				return result;
E
Erich Gamma 已提交
671 672 673
			}, err => {
				this.lastTaskEvent = null;
			});
674

675
			if (filteredTasks[0].isWatching) {
I
isidor 已提交
676
				return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
677 678 679
			}

			return taskPromise;
E
Erich Gamma 已提交
680 681 682
		});
	}

I
isidor 已提交
683
	private rawAttach(port: number): TPromise<any> {
I
isidor 已提交
684
		if (this.session) {
I
isidor 已提交
685
			if (!this.session.configuration.isAttach) {
686 687 688 689
				return this.session.attach({ port });
			}

			this.session.disconnect().done(null, errors.onUnexpectedError);
I
isidor 已提交
690 691
		}

I
isidor 已提交
692
		this.setStateAndEmit(debug.State.Initializing);
693
		const configuration = this.configurationManager.configuration;
I
isidor 已提交
694
		return this.doCreateSession({
695
			type: configuration.type,
I
isidor 已提交
696
			request: 'attach',
697 698
			port,
			sourceMaps: configuration.sourceMaps,
699 700
			outDir: configuration.outDir,
			debugServer: configuration.debugServer
701
		}, false);
I
isidor 已提交
702 703
	}

I
isidor 已提交
704
	public restartSession(): TPromise<any> {
I
isidor 已提交
705
		return this.session ? this.session.disconnect(true).then(() =>
706
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
707
				setTimeout(() => {
I
isidor 已提交
708
					this.createSession(false, false).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
709
				}, 300);
I
isidor 已提交
710
			})
I
isidor 已提交
711
		) : this.createSession(false, false);
E
Erich Gamma 已提交
712 713 714 715 716 717 718 719
	}

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

	private onSessionEnd(): void {
		if (this.session) {
I
isidor 已提交
720
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
721
			this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
722
				type: this.session.configuration.type,
I
isidor 已提交
723 724 725 726 727
				success: this.session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: this.session.getLengthInSeconds(),
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
728
		}
729

E
Erich Gamma 已提交
730
		this.session = null;
I
isidor 已提交
731 732 733 734 735 736
		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 已提交
737 738 739
		this.partService.removeClass('debugging');
		this.editorService.focusEditor();

740
		this.model.clearThreads(true);
I
isidor 已提交
741
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
742 743
		this.setStateAndEmit(debug.State.Inactive);

I
isidor 已提交
744
		// set breakpoints back to unverified since the session ended.
745
		// source reference changes across sessions, so we do not use it to persist the source.
746
		const data: {[id: string]: { line: number, verified: boolean } } = { };
747 748 749 750
		this.model.getBreakpoints().forEach(bp => {
			delete bp.source.raw.sourceReference;
			data[bp.getId()] = { line: bp.lineNumber, verified: false };
		});
751 752
		this.model.updateBreakpoints(data);

753 754 755 756
		if (this.telemetryAdapter) {
			this.telemetryAdapter.dispose();
			this.telemetryAdapter = null;
		}
E
Erich Gamma 已提交
757 758 759 760 761 762 763 764 765 766 767
		this.inDebugMode.reset();
	}

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

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

I
isidor 已提交
768
	public openOrRevealEditor(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
769
		const visibleEditors = this.editorService.getVisibleEditors();
I
isidor 已提交
770
		for (let i = 0; i < visibleEditors.length; i++) {
771
			const fileInput = wbeditorcommon.asFileEditorInput(visibleEditors[i].input);
772 773 774
			if ((fileInput && fileInput.getResource().toString() === source.uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === source.uri.toString())) {

775 776 777 778 779
				const control = <editorbrowser.ICodeEditor>visibleEditors[i].getControl();
				if (control) {
					control.revealLineInCenterIfOutsideViewport(lineNumber);
					control.setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 });
					return this.editorService.openEditor(visibleEditors[i].input, wbeditorcommon.TextEditorOptions.create({ preserveFocus: preserveFocus, forceActive: true }), visibleEditors[i].position);
E
Erich Gamma 已提交
780
				}
781

A
Alex Dima 已提交
782
				return TPromise.as(null);
E
Erich Gamma 已提交
783 784 785 786
			}
		}

		if (source.inMemory) {
I
isidor 已提交
787
			// internal module
E
Erich Gamma 已提交
788
			if (source.reference !== 0 && this.session) {
789
				return this.session.source({ sourceReference: source.reference }).then(response => {
E
Erich Gamma 已提交
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
					const editorInput = this.getDebugStringEditorInput(source, response.body.content, mime.guessMimeTypes(source.name)[0]);
					return this.editorService.openEditor(editorInput, wbeditorcommon.TextEditorOptions.create({
						selection: {
							startLineNumber: lineNumber,
							startColumn: 1,
							endLineNumber: lineNumber,
							endColumn: 1
						},
						preserveFocus: preserveFocus
					}), sideBySide);
				});
			}

			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 已提交
822
	private sourceIsUnavailable(source: Source, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
823
		this.model.sourceIsUnavailable(source);
824
		const editorInput = this.getDebugStringEditorInput(source, nls.localize('debugSourceNotAvailable', "Source {0} is not available.", source.uri.fsPath), 'text/plain');
E
Erich Gamma 已提交
825 826 827 828

		return this.editorService.openEditor(editorInput, wbeditorcommon.TextEditorOptions.create({ preserveFocus: true }), sideBySide);
	}

829 830
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
831 832
	}

833
	private getDebugStringEditorInput(source: Source, value: string, mtype: string): DebugStringEditorInput {
I
isidor 已提交
834
		const filtered = this.debugStringEditorInputs.filter(input => input.getResource().toString() === source.uri.toString());
E
Erich Gamma 已提交
835 836

		if (filtered.length === 0) {
837
			const result = this.instantiationService.createInstance(DebugStringEditorInput, source.name, source.uri, source.origin, value, mtype, void 0);
E
Erich Gamma 已提交
838
			this.debugStringEditorInputs.push(result);
I
isidor 已提交
839 840
			this.toDisposeOnSessionEnd.push(result);

E
Erich Gamma 已提交
841 842 843 844 845 846
			return result;
		} else {
			return filtered[0];
		}
	}

I
isidor 已提交
847
	public sendAllBreakpoints(): TPromise<any> {
I
isidor 已提交
848 849 850 851
		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 已提交
852 853
	}

I
isidor 已提交
854
	private sendBreakpoints(modelUri: uri): TPromise<void> {
855
		if (!this.session || !this.session.readyForBreakpoints) {
I
isidor 已提交
856
			return TPromise.as(null);
857 858
		}

859 860
		const breakpointsToSend = arrays.distinct(
			this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.source.uri.toString() === modelUri.toString()),
861
			bp => `${ bp.desiredLineNumber }`
862
		);
I
isidor 已提交
863
		const rawSource = breakpointsToSend.length > 0 ? breakpointsToSend[0].source.raw : Source.toRawSource(modelUri, this.model);
864

865
		return this.session.setBreakpoints({ source: rawSource, lines: breakpointsToSend.map(bp => bp.desiredLineNumber),
866
			breakpoints: breakpointsToSend.map(bp => ({ line: bp.desiredLineNumber, condition: bp.condition })) }).then(response => {
867

868
			const data: {[id: string]: { line?: number, verified: boolean } } = { };
869 870 871 872 873
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateBreakpoints(data);
E
Erich Gamma 已提交
874 875 876
		});
	}

I
isidor 已提交
877
	private sendFunctionBreakpoints(): TPromise<void> {
I
isidor 已提交
878
		if (!this.session || !this.session.readyForBreakpoints || !this.session.configuration.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
879 880 881
			return TPromise.as(null);
		}

I
isidor 已提交
882
		const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
I
isidor 已提交
883 884 885 886 887 888 889
		return this.session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => {
			const data: {[id: string]: { name?: string, verified?: boolean } } = { };
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateFunctionBreakpoints(data);
I
isidor 已提交
890 891 892
		});
	}

I
isidor 已提交
893
	private sendExceptionBreakpoints(): TPromise<any> {
894
		if (!this.session || !this.session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
I
isidor 已提交
895
			return TPromise.as(null);
I
isidor 已提交
896
		}
I
isidor 已提交
897

I
isidor 已提交
898
		const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
899
		return this.session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
E
Erich Gamma 已提交
900 901 902
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
903 904
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
E
Erich Gamma 已提交
905 906 907 908 909
	}

	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);
910
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
911
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
912
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
E
Erich Gamma 已提交
913 914 915 916
		this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(this.model.getWatchExpressions()), StorageScope.WORKSPACE);
	}

	public dispose(): void {
J
Joao Moreno 已提交
917
		this.toDisposeOnSessionEnd = lifecycle.dispose(this.toDisposeOnSessionEnd);
I
isidor 已提交
918
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
919 920
	}
}