debugService.ts 40.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  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');
7
import strings = require('vs/base/common/strings');
E
Erich Gamma 已提交
8 9
import lifecycle = require('vs/base/common/lifecycle');
import mime = require('vs/base/common/mime');
10
import Event, { Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
11
import uri from 'vs/base/common/uri';
12
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
13
import arrays = require('vs/base/common/arrays');
14
import types = require('vs/base/common/types');
E
Erich Gamma 已提交
15 16
import errors = require('vs/base/common/errors');
import severity from 'vs/base/common/severity';
I
isidor 已提交
17
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
18
import aria = require('vs/base/browser/ui/aria/aria');
19
import { IAIAdapter, createAIAdapter } from 'vs/base/parts/ai/node/ai';
E
Erich Gamma 已提交
20
import editorbrowser = require('vs/editor/browser/editorBrowser');
21 22 23
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 已提交
24
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
25 26 27 28 29 30
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 已提交
31 32 33 34
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');
35
import { DebugStringEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
E
Erich Gamma 已提交
36
import viewmodel = require('vs/workbench/parts/debug/common/debugViewModel');
37
import debugactions = require('vs/workbench/parts/debug/electron-browser/debugActions');
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';
46
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
E
Erich Gamma 已提交
47 48
import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
49
import { IWindowService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService';
50
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/services/thread/electron-browser/threadService';
51
import { ipcRenderer as ipc } from 'electron';
E
Erich Gamma 已提交
52

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

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

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

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

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

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

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

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

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

	private onBroadcast(broadcast: IBroadcast): void {
142

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

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

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

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

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

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

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

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

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

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

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

243
		this.toDisposeOnSessionEnd.push(this.session.onDidStop(event => {
244
			this.setStateAndEmit(debug.State.Stopped);
I
isidor 已提交
245
			const threadId = event.body.threadId;
E
Erich Gamma 已提交
246

I
isidor 已提交
247
			this.getThreadData().done(() => {
248
				this.model.rawUpdate({
I
isidor 已提交
249
					threadId,
250 251 252
					stoppedDetails: event.body,
					allThreadsStopped: event.body.allThreadsStopped
				});
253

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

262
						return this.openOrRevealSource(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.onDidThread(event => {
E
Erich Gamma 已提交
271
			if (event.body.reason === 'started') {
I
isidor 已提交
272
				this.getThreadData().done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
273 274 275 276 277
			} else if (event.body.reason === 'exited') {
				this.model.clearThreads(true, event.body.threadId);
			}
		}));

278
		this.toDisposeOnSessionEnd.push(this.session.onDidTerminateDebugee(event => {
279
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
280
			if (this.session && this.session.getId() === event.body.sessionId) {
281
				if (event.body && typeof event.body.restart === 'boolean' && event.body.restart) {
282
					this.restartSession().done(null, err => this.messageService.show(severity.Error, err.message));
283 284 285
				} else {
					this.session.disconnect().done(null, errors.onUnexpectedError);
				}
E
Erich Gamma 已提交
286 287 288
			}
		}));

289
		this.toDisposeOnSessionEnd.push(this.session.onDidOutput(event => {
290
			if (event.body && event.body.category === 'telemetry') {
291
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
292 293
				// and the user opted in telemetry
				if (this.telemetryAdapter && this.telemetryService.isOptedIn) {
294
					this.telemetryAdapter.log(event.body.output, event.body.data);
295
				}
296
			} else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) {
E
Erich Gamma 已提交
297 298 299 300
				this.onOutput(event);
			}
		}));

301
		this.toDisposeOnSessionEnd.push(this.session.onDidBreakpoint(event => {
I
isidor 已提交
302
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
303
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
304 305 306
			if (breakpoint) {
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
307
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
308 309 310 311 312 313
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

314
		this.toDisposeOnSessionEnd.push(this.session.onDidExitAdapter(event => {
315
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
316
			if (this.session.configuration.type === 'extensionHost' && this._state === debug.State.RunningNoDebug) {
317 318
				ipc.send('vscode:closeExtensionHostWindow', this.contextService.getWorkspace().resource.fsPath);
			}
319
			if (this.session && this.session.getId() === event.body.sessionId) {
320 321
				this.onSessionEnd();
			}
E
Erich Gamma 已提交
322 323 324 325
		}));
	}

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

I
isidor 已提交
330 331 332 333
	private getThreadData(): TPromise<void> {
		return this.session.threads().then(response => {
			response.body.threads.forEach(thread => this.model.rawUpdate({ threadId: thread.id, thread }));
		});
E
Erich Gamma 已提交
334 335 336 337 338
	}

	private loadBreakpoints(): debug.IBreakpoint[] {
		try {
			return JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
339
				return new model.Breakpoint(new Source(breakpoint.source.raw ? breakpoint.source.raw : { path: uri.parse(breakpoint.source.uri).fsPath, name: breakpoint.source.name }),
340
					breakpoint.desiredLineNumber || breakpoint.lineNumber, breakpoint.enabled, breakpoint.condition);
E
Erich Gamma 已提交
341 342 343 344 345 346
			});
		} catch (e) {
			return [];
		}
	}

I
isidor 已提交
347 348 349
	private loadFunctionBreakpoints(): debug.IFunctionBreakpoint[] {
		try {
			return JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
350
				return new model.FunctionBreakpoint(fb.name, fb.enabled);
I
isidor 已提交
351 352 353 354 355 356
			});
		} catch (e) {
			return [];
		}
	}

E
Erich Gamma 已提交
357
	private loadExceptionBreakpoints(): debug.IExceptionBreakpoint[] {
I
isidor 已提交
358
		let result: debug.IExceptionBreakpoint[] = null;
E
Erich Gamma 已提交
359 360
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
361
				return new model.ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
362 363 364 365 366
			});
		} catch (e) {
			result = [];
		}

367
		return result;
E
Erich Gamma 已提交
368 369 370 371 372 373 374 375 376 377 378 379
	}

	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 [];
		}
	}

I
isidor 已提交
380 381
	public get state(): debug.State {
		return this._state;
E
Erich Gamma 已提交
382 383
	}

384 385 386 387
	public get onDidChangeState(): Event<debug.State> {
		return this._onDidChangeState.event;
	}

388
	private setStateAndEmit(newState: debug.State): void {
I
isidor 已提交
389
		this._state = newState;
390
		this._onDidChangeState.fire(newState);
E
Erich Gamma 已提交
391 392 393 394 395 396
	}

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

I
isidor 已提交
397
	public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise<void> {
E
Erich Gamma 已提交
398 399
		this.viewModel.setFocusedStackFrame(focusedStackFrame);
		if (focusedStackFrame) {
I
isidor 已提交
400
			return this.model.evaluateWatchExpressions(this.session, focusedStackFrame);
E
Erich Gamma 已提交
401 402
		} else {
			this.model.clearWatchExpressionValues();
I
isidor 已提交
403
			return TPromise.as(null);
E
Erich Gamma 已提交
404 405 406
		}
	}

I
isidor 已提交
407
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
408 409 410
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
			if (breakpoint instanceof model.Breakpoint) {
I
isidor 已提交
411
				return this.sendBreakpoints((<model.Breakpoint>breakpoint).source.uri);
412 413 414
			} else if (breakpoint instanceof model.FunctionBreakpoint) {
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
415

416
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
417 418
		}

419 420
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
421 422
	}

423 424 425 426 427 428 429
	public addBreakpoints(rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void[]> {
		this.model.addBreakpoints(rawBreakpoints);
		const uris = arrays.distinct(rawBreakpoints, raw => raw.uri.toString()).map(raw => raw.uri);

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

430 431 432 433
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
		const urisToClear = arrays.distinct(toRemove, bp => bp.source.uri.toString()).map(bp => bp.source.uri);
		this.model.removeBreakpoints(toRemove);
E
Erich Gamma 已提交
434

I
isidor 已提交
435
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
436 437
	}

438 439
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
440 441 442
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
443 444
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
445 446
	}

I
isidor 已提交
447
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
448
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
449
		return this.sendFunctionBreakpoints();
I
isidor 已提交
450 451
	}

I
isidor 已提交
452
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
453
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
454
		return this.sendFunctionBreakpoints();
I
isidor 已提交
455 456
	}

I
isidor 已提交
457
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
458
		this.telemetryService.publicLog('debugService/addReplExpression');
459 460 461 462
		const focussedStackFrame = this.viewModel.getFocusedStackFrame();
		return this.model.addReplExpression(this.session, focussedStackFrame, name)
			// Evaluate all watch expressions again since repl evaluation might have changed some.
			.then(() => this.setFocusedStackFrameAndEvaluate(focussedStackFrame));
E
Erich Gamma 已提交
463 464
	}

465
	public logToRepl(value: string | { [key: string]: any }, severity?: severity): void {
E
Erich Gamma 已提交
466 467 468 469 470 471 472
		this.model.logToRepl(value, severity);
	}

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

473 474
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
475 476
	}

I
isidor 已提交
477
	public addWatchExpression(name: string): TPromise<void> {
E
Erich Gamma 已提交
478 479 480
		return this.model.addWatchExpression(this.session, this.viewModel.getFocusedStackFrame(), name);
	}

I
isidor 已提交
481
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
E
Erich Gamma 已提交
482 483 484
		return this.model.renameWatchExpression(this.session, this.viewModel.getFocusedStackFrame(), id, newName);
	}

485 486
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
487 488
	}

489
	public createSession(noDebug: boolean, configuration?: debug.IConfig, changeViewState = !this.partService.isSideBarHidden()): TPromise<any> {
490
		this.removeReplExpressions();
E
Erich Gamma 已提交
491

492
		return this.textFileService.saveAll()							// make sure all dirty files are saved
I
isidor 已提交
493
			.then(() => this.configurationService.loadConfiguration()	// make sure configuration is up to date
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
			.then(() => this.extensionService.onReady()
			.then(() => this.configurationManager.setConfiguration((this.configurationManager.configurationName))
			.then(() => {
				this.configurationManager.resloveConfiguration(configuration);
				configuration = configuration || this.configurationManager.configuration;
				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."));
						}
					});
				}

				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 selected configuration in launch.json."),
							{ actions: [CloseAction, this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL)] }));
				}

				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, changeViewState);
					}

					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: [CloseAction, new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
							this.messageService.hideAll();
							return this.doCreateSession(configuration, changeViewState);
						})]
					});
				}, (err: TaskError) => {
					if (err.code !== TaskErrors.NotConfigured) {
						throw err;
					}

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

I
isidor 已提交
544
	private doCreateSession(configuration: debug.IConfig, changeViewState: boolean): TPromise<any> {
545
		this.setStateAndEmit(debug.State.Initializing);
546 547 548 549 550 551 552 553 554 555 556

		this.telemetryService.getTelemetryInfo().then(info => {
			const telemetryInfo: { [key: string]: string } = Object.create(null);
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
			return telemetryInfo;
		}).then(data => {
			let {aiKey, type} = this.configurationManager.adapter;
			this.telemetryAdapter = createAIAdapter(aiKey, type, data);
		});

557
		this.session = this.instantiationService.createInstance(session.RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.telemetryAdapter);
I
isidor 已提交
558 559 560 561
		this.registerSessionListeners();

		return this.session.initialize({
			adapterID: configuration.type,
562
			pathFormat: 'path',
I
isidor 已提交
563
			linesStartAt1: true,
564
			columnsStartAt1: true
I
isidor 已提交
565
		}).then((result: DebugProtocol.InitializeResponse) => {
566
			if (!this.session) {
I
isidor 已提交
567
				return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
568 569
			}

I
isidor 已提交
570
			this.model.setExceptionBreakpoints(this.session.configuration.capabilities.exceptionBreakpointFilters);
I
isidor 已提交
571 572
			return configuration.request === 'attach' ? this.session.attach(configuration) : this.session.launch(configuration);
		}).then((result: DebugProtocol.Response) => {
573
			if (changeViewState) {
574
				if (configuration.internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && configuration.internalConsoleOptions !== 'neverOpen')) {
575 576 577 578 579 580 581 582
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}

				if (!this.viewModel.changedWorkbenchViewState) {
					// We only want to change the workbench view state on the first debug session #5738
					this.viewModel.changedWorkbenchViewState = true;
					this.viewletService.openViewlet(debug.VIEWLET_ID);
				}
I
isidor 已提交
583
			}
584 585 586 587 588

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

P
Pierson Lee 已提交
596
			this.telemetryService.publicLog('debugSessionStart', { type: configuration.type, breakpointCount: this.model.getBreakpoints().length, exceptionBreakpoints: this.model.getExceptionBreakpoints(), watchExpressionsCount: this.model.getWatchExpressions().length });
597
		}).then(undefined, (error: any) => {
I
isidor 已提交
598
			this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
I
isidor 已提交
599
			this.setStateAndEmit(debug.State.Inactive);
I
isidor 已提交
600 601 602
			if (this.session) {
				this.session.disconnect();
			}
603 604 605 606
			// 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 已提交
607

I
isidor 已提交
608 609
			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];
610
			return TPromise.wrapError(errors.create(error.message, { actions }));
E
Erich Gamma 已提交
611 612 613
		});
	}

I
isidor 已提交
614
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
615
		if (!taskName) {
I
isidor 已提交
616
			return TPromise.as(null);
E
Erich Gamma 已提交
617 618
		}

619
		// run a task before starting a debug session
E
Erich Gamma 已提交
620
		return this.taskService.tasks().then(descriptions => {
621
			const filteredTasks = descriptions.filter(task => task.name === taskName);
E
Erich Gamma 已提交
622
			if (filteredTasks.length !== 1) {
623 624 625 626 627 628 629
				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 已提交
630 631
			}

I
isidor 已提交
632
			// task is already running - nothing to do.
633
			if (this.lastTaskEvent && this.lastTaskEvent.taskName === taskName) {
I
isidor 已提交
634
				return TPromise.as(null);
E
Erich Gamma 已提交
635 636 637
			}

			if (this.lastTaskEvent) {
I
isidor 已提交
638
				// there is a different task running currently.
I
isidor 已提交
639
				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 已提交
640 641
			}

I
isidor 已提交
642
			// no task running, execute the preLaunchTask.
643
			const taskPromise = this.taskService.run(filteredTasks[0].id).then(result => {
E
Erich Gamma 已提交
644
				this.lastTaskEvent = null;
I
isidor 已提交
645
				return result;
E
Erich Gamma 已提交
646 647 648
			}, err => {
				this.lastTaskEvent = null;
			});
649

650
			if (filteredTasks[0].isWatching) {
I
isidor 已提交
651
				return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
652 653 654
			}

			return taskPromise;
E
Erich Gamma 已提交
655 656 657
		});
	}

I
isidor 已提交
658
	private rawAttach(port: number): TPromise<any> {
I
isidor 已提交
659
		if (this.session) {
I
isidor 已提交
660
			if (!this.session.configuration.isAttach) {
661 662 663 664
				return this.session.attach({ port });
			}

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

I
isidor 已提交
667
		this.setStateAndEmit(debug.State.Initializing);
668
		const configuration = this.configurationManager.configuration;
I
isidor 已提交
669
		return this.doCreateSession({
670
			type: configuration.type,
I
isidor 已提交
671
			request: 'attach',
672 673
			port,
			sourceMaps: configuration.sourceMaps,
674 675
			outDir: configuration.outDir,
			debugServer: configuration.debugServer
676
		}, false);
I
isidor 已提交
677 678
	}

I
isidor 已提交
679
	public restartSession(): TPromise<any> {
I
isidor 已提交
680
		return this.session ? this.session.disconnect(true).then(() =>
681
			new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
682
				setTimeout(() => {
683
					this.createSession(false, null, false).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
684
				}, 300);
I
isidor 已提交
685
			})
686
		) : this.createSession(false, null, false);
E
Erich Gamma 已提交
687 688 689 690 691 692 693 694
	}

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

	private onSessionEnd(): void {
		if (this.session) {
I
isidor 已提交
695
			const bpsExist = this.model.getBreakpoints().length > 0;
I
isidor 已提交
696
			this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
697
				type: this.session.configuration.type,
I
isidor 已提交
698 699 700 701 702
				success: this.session.emittedStopped || !bpsExist,
				sessionLengthInSeconds: this.session.getLengthInSeconds(),
				breakpointCount: this.model.getBreakpoints().length,
				watchExpressionsCount: this.model.getWatchExpressions().length
			});
E
Erich Gamma 已提交
703
		}
704

E
Erich Gamma 已提交
705
		this.session = null;
I
isidor 已提交
706 707 708 709 710 711
		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 已提交
712 713
		this.partService.removeClass('debugging');

714
		this.model.clearThreads(true);
I
isidor 已提交
715
		this.setFocusedStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
716 717
		this.setStateAndEmit(debug.State.Inactive);

I
isidor 已提交
718
		// set breakpoints back to unverified since the session ended.
719
		// source reference changes across sessions, so we do not use it to persist the source.
I
isidor 已提交
720
		const data: { [id: string]: { line: number, verified: boolean } } = {};
721 722 723 724
		this.model.getBreakpoints().forEach(bp => {
			delete bp.source.raw.sourceReference;
			data[bp.getId()] = { line: bp.lineNumber, verified: false };
		});
725 726
		this.model.updateBreakpoints(data);

727 728 729 730
		if (this.telemetryAdapter) {
			this.telemetryAdapter.dispose();
			this.telemetryAdapter = null;
		}
E
Erich Gamma 已提交
731 732 733 734 735 736 737 738 739 740 741
		this.inDebugMode.reset();
	}

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

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

742
	public openOrRevealSource(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
743
		const visibleEditors = this.editorService.getVisibleEditors();
I
isidor 已提交
744
		for (let i = 0; i < visibleEditors.length; i++) {
745
			const fileInput = wbeditorcommon.asFileEditorInput(visibleEditors[i].input);
746 747 748
			if ((fileInput && fileInput.getResource().toString() === source.uri.toString()) ||
				(visibleEditors[i].input instanceof DebugStringEditorInput && (<DebugStringEditorInput>visibleEditors[i].input).getResource().toString() === source.uri.toString())) {

749 750 751 752 753
				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 已提交
754
				}
755

A
Alex Dima 已提交
756
				return TPromise.as(null);
E
Erich Gamma 已提交
757 758 759 760
			}
		}

		if (source.inMemory) {
I
isidor 已提交
761
			// internal module
E
Erich Gamma 已提交
762
			if (source.reference !== 0 && this.session) {
763
				return this.session.source({ sourceReference: source.reference }).then(response => {
E
Erich Gamma 已提交
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
					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 已提交
796
	private sourceIsUnavailable(source: Source, sideBySide: boolean): TPromise<any> {
E
Erich Gamma 已提交
797
		this.model.sourceIsUnavailable(source);
798
		const editorInput = this.getDebugStringEditorInput(source, nls.localize('debugSourceNotAvailable', "Source {0} is not available.", source.uri.fsPath), 'text/plain');
E
Erich Gamma 已提交
799 800 801 802

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

803 804
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
805 806
	}

807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
	public next(threadId: number): TPromise<void> {
		if (!this.session) {
			return TPromise.as(null);
		}

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

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

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

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

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

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

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

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

I
isidor 已提交
852
		return this.session.pause({ threadId });
853 854 855
	}


856
	private lazyTransitionToRunningState(threadId?: number): void {
857 858
		let cancelTransitionToRunningState = false;
		const toDispose = this.session.onDidStop(e => {
859
			if (e.body.threadId === threadId || e.body.allThreadsStopped || !threadId) {
860 861 862 863 864 865 866 867 868 869
				cancelTransitionToRunningState = true;
			}
		});

		// Do not immediatly transition to running state since that might cause unnecessery flickering
		// of the tree in the debug viewlet. Only transition if no stopped event has arrived in 500ms.
		setTimeout(() => {
			toDispose.dispose();
			if (!cancelTransitionToRunningState) {
				aria.status(nls.localize('debuggingContinued', "Debugging continued."));
870 871
				// TODO@Isidor temporary workaround for #1703
				if (this.session && strings.equalsIgnoreCase(this.session.configuration.type, 'php')) {
872
					this.model.clearThreads(false, threadId);
873 874
				} else {
					this.model.clearThreads(false);
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
				}

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

				this.setFocusedStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
				if (!stoppedThread) {
					this.setStateAndEmit(this.configurationManager.configuration.noDebug ? debug.State.RunningNoDebug : debug.State.Running);
				}
			}
		}, 500);
	}

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

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

E
Erich Gamma 已提交
900 901 902 903 904 905
			return result;
		} else {
			return filtered[0];
		}
	}

906
	private sendAllBreakpoints(): TPromise<any> {
I
isidor 已提交
907 908 909 910
		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 已提交
911 912
	}

I
isidor 已提交
913
	private sendBreakpoints(modelUri: uri): TPromise<void> {
914
		if (!this.session || !this.session.readyForBreakpoints) {
I
isidor 已提交
915
			return TPromise.as(null);
916 917
		}

918 919
		const breakpointsToSend = arrays.distinct(
			this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.source.uri.toString() === modelUri.toString()),
I
isidor 已提交
920
			bp => `${bp.desiredLineNumber}`
921
		);
I
isidor 已提交
922
		const rawSource = breakpointsToSend.length > 0 ? breakpointsToSend[0].source.raw : Source.toRawSource(modelUri, this.model);
923

I
isidor 已提交
924 925 926 927 928 929 930
		return this.session.setBreakpoints({
			source: rawSource,
			lines: breakpointsToSend.map(bp => bp.desiredLineNumber),
			breakpoints: breakpointsToSend.map(bp => ({ line: bp.desiredLineNumber,
			condition: bp.condition
		}))}).then(response => {
			const data: { [id: string]: { line?: number, verified: boolean } } = {};
931 932 933 934 935
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateBreakpoints(data);
E
Erich Gamma 已提交
936 937 938
		});
	}

I
isidor 已提交
939
	private sendFunctionBreakpoints(): TPromise<void> {
I
isidor 已提交
940
		if (!this.session || !this.session.readyForBreakpoints || !this.session.configuration.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
941 942 943
			return TPromise.as(null);
		}

I
isidor 已提交
944
		const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
I
isidor 已提交
945
		return this.session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => {
I
isidor 已提交
946
			const data: { [id: string]: { name?: string, verified?: boolean } } = {};
I
isidor 已提交
947 948 949 950 951
			for (let i = 0; i < breakpointsToSend.length; i++) {
				data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
			}

			this.model.updateFunctionBreakpoints(data);
I
isidor 已提交
952 953 954
		});
	}

I
isidor 已提交
955
	private sendExceptionBreakpoints(): TPromise<any> {
956
		if (!this.session || !this.session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
I
isidor 已提交
957
			return TPromise.as(null);
I
isidor 已提交
958
		}
I
isidor 已提交
959

I
isidor 已提交
960
		const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
961
		return this.session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
E
Erich Gamma 已提交
962 963 964
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
965 966
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
			fileChangesEvent.contains(bp.source.uri, FileChangeType.DELETED)));
E
Erich Gamma 已提交
967 968 969 970 971
	}

	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);
972
		this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getFunctionBreakpoints()), StorageScope.WORKSPACE);
E
Erich Gamma 已提交
973
		this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(this.model.getExceptionBreakpoints()), StorageScope.WORKSPACE);
974
		this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.configurationManager.configurationName, StorageScope.WORKSPACE);
E
Erich Gamma 已提交
975 976 977 978
		this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(this.model.getWatchExpressions()), StorageScope.WORKSPACE);
	}

	public dispose(): void {
J
Joao Moreno 已提交
979
		this.toDisposeOnSessionEnd = lifecycle.dispose(this.toDisposeOnSessionEnd);
I
isidor 已提交
980
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
981 982
	}
}