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

I
isidor 已提交
6
import * as nls from 'vs/nls';
J
Johannes Rieken 已提交
7
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
8
import * as lifecycle from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
9
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
I
isidor 已提交
10 11 12 13
import * as paths from 'vs/base/common/paths';
import * as async from 'vs/base/common/async';
import * as errors from 'vs/base/common/errors';
import { equalsIgnoreCase } from 'vs/base/common/strings';
J
Johannes Rieken 已提交
14
import { isMacintosh } from 'vs/base/common/platform';
I
isidor 已提交
15
import * as dom from 'vs/base/browser/dom';
I
isidor 已提交
16
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
I
isidor 已提交
17 18 19
import { getPathLabel } from 'vs/base/common/labels';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { IActionItem, Separator, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
I
isidor 已提交
20
import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, DRAG_OVER_ACCEPT, IDragAndDropData, IDragOverReaction } from 'vs/base/parts/tree/browser/tree';
J
Johannes Rieken 已提交
21
import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox';
I
isidor 已提交
22
import { DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
I
isidor 已提交
23 24 25 26 27
import { IActionProvider } from 'vs/base/parts/tree/browser/actionsRenderer';
import * as debug from 'vs/workbench/parts/debug/common/debug';
import { Expression, Variable, FunctionBreakpoint, StackFrame, Thread, Process, Breakpoint, ExceptionBreakpoint, Model, Scope } from 'vs/workbench/parts/debug/common/debugModel';
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import { ContinueAction, StepOverAction, PauseAction, AddFunctionBreakpointAction, ReapplyBreakpointsAction, DisableAllBreakpointsAction, RemoveBreakpointAction, ToggleEnablementAction, RenameFunctionBreakpointAction, RemoveWatchExpressionAction, AddWatchExpressionAction, EditWatchExpressionAction, RemoveAllBreakpointsAction, EnableAllBreakpointsAction, StepOutAction, StepIntoAction, SetValueAction, RemoveAllWatchExpressionsAction, ToggleBreakpointsActivatedAction, RestartFrameAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions';
J
Johannes Rieken 已提交
28 29 30 31 32 33
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
E
Erich Gamma 已提交
34

J
Joao Moreno 已提交
35
const $ = dom.$;
I
isidor 已提交
36 37
const booleanRegex = /^true|false$/i;
const stringRegex = /^(['"]).*\1$/;
38
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
E
Erich Gamma 已提交
39

40 41 42 43 44 45 46
export interface IRenderValueOptions {
	preserveWhitespace: boolean;
	showChanged: boolean;
	maxValueLength?: number;
}

export function renderExpressionValue(expressionOrValue: debug.IExpression | string, container: HTMLElement, options: IRenderValueOptions): void {
47
	let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value;
E
Erich Gamma 已提交
48

I
isidor 已提交
49
	// remove stale classes
E
Erich Gamma 已提交
50
	container.className = 'value';
I
isidor 已提交
51
	// when resolving expressions we represent errors from the server as a variable with name === null.
I
isidor 已提交
52
	if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) {
E
Erich Gamma 已提交
53
		dom.addClass(container, 'unavailable');
I
isidor 已提交
54
		if (value !== Expression.DEFAULT_VALUE) {
55 56
			dom.addClass(container, 'error');
		}
E
Erich Gamma 已提交
57 58 59 60 61 62 63 64
	} else if (!isNaN(+value)) {
		dom.addClass(container, 'number');
	} else if (booleanRegex.test(value)) {
		dom.addClass(container, 'boolean');
	} else if (stringRegex.test(value)) {
		dom.addClass(container, 'string');
	}

65
	if (options.showChanged && (<any>expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) {
66 67 68
		// value changed color has priority over other colors.
		container.className = 'value changed';
	}
I
isidor 已提交
69

70 71 72 73 74 75 76
	if (options.maxValueLength && value.length > options.maxValueLength) {
		value = value.substr(0, options.maxValueLength) + '...';
	}
	if (value && !options.preserveWhitespace) {
		container.textContent = value.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
	} else {
		container.textContent = value;
I
isidor 已提交
77
	}
E
Erich Gamma 已提交
78 79 80
	container.title = value;
}

I
isidor 已提交
81
export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void {
82
	if (variable.available) {
83
		data.name.textContent = variable.name;
84
		data.name.title = variable.type ? variable.type : '';
85 86
	}

E
Erich Gamma 已提交
87
	if (variable.value) {
88
		data.name.textContent += ':';
89 90 91 92 93
		renderExpressionValue(variable, data.value, {
			showChanged,
			maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
			preserveWhitespace: false
		});
94
		data.value.title = variable.value;
E
Erich Gamma 已提交
95 96 97 98 99 100
	} else {
		data.value.textContent = '';
		data.value.title = '';
	}
}

I
isidor 已提交
101 102 103 104
interface IRenameBoxOptions {
	initialValue: string;
	ariaLabel: string;
	placeholder?: string;
105
	validationOptions?: IInputValidationOptions;
I
isidor 已提交
106 107
}

I
isidor 已提交
108
function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
109
	let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
110
	let inputBox = new InputBox(inputBoxContainer, contextViewService, {
I
isidor 已提交
111 112 113
		validationOptions: options.validationOptions,
		placeholder: options.placeholder,
		ariaLabel: options.ariaLabel
114 115
	});

I
isidor 已提交
116
	inputBox.value = options.initialValue ? options.initialValue : '';
117
	inputBox.focus();
118
	inputBox.select();
119

I
isidor 已提交
120 121
	let disposed = false;
	const toDispose: [lifecycle.IDisposable] = [inputBox];
122

J
Joao Moreno 已提交
123
	const wrapUp = async.once((renamed: boolean) => {
124 125
		if (!disposed) {
			disposed = true;
I
isidor 已提交
126
			if (element instanceof Expression && renamed && inputBox.value) {
127
				debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
I
isidor 已提交
128
			} else if (element instanceof Expression && !element.name) {
129
				debugService.removeWatchExpressions(element.getId());
I
isidor 已提交
130
			} else if (element instanceof FunctionBreakpoint && renamed && inputBox.value) {
131
				debugService.renameFunctionBreakpoint(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
I
isidor 已提交
132
			} else if (element instanceof FunctionBreakpoint && !element.name) {
133
				debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
I
isidor 已提交
134
			} else if (element instanceof Variable) {
135
				element.errorMessage = null;
136
				if (renamed && element.value !== inputBox.value) {
137
					element.setVariable(inputBox.value)
138
						// if everything went fine we need to refresh ui elements since the variable update can change watch and variables view
139
						.done(() => tree.refresh(element, false), errors.onUnexpectedError);
140
				}
141
			}
142

143 144
			tree.clearHighlight();
			tree.DOMFocus();
145
			tree.setFocus(element);
146

I
isidor 已提交
147
			// need to remove the input box since this template will be reused.
148
			container.removeChild(inputBoxContainer);
J
Joao Moreno 已提交
149
			lifecycle.dispose(toDispose);
150 151 152
		}
	});

A
Cleanup  
Alex Dima 已提交
153
	toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
A
Alexandru Dima 已提交
154 155
		const isEscape = e.equals(KeyCode.Escape);
		const isEnter = e.equals(KeyCode.Enter);
156 157 158 159 160 161 162 163 164
		if (isEscape || isEnter) {
			wrapUp(isEnter);
		}
	}));
	toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
		wrapUp(true);
	}));
}

165 166 167 168 169
function getSourceName(source: Source, contextService: IWorkspaceContextService): string {
	if (source.inMemory) {
		return source.name;
	}

I
isidor 已提交
170
	return getPathLabel(paths.basename(source.uri.fsPath), contextService);
171 172
}

I
isidor 已提交
173
export class BaseDebugController extends DefaultController {
E
Erich Gamma 已提交
174

I
isidor 已提交
175 176 177
	constructor(
		protected debugService: debug.IDebugService,
		private contextMenuService: IContextMenuService,
I
isidor 已提交
178
		private actionProvider: IActionProvider,
I
isidor 已提交
179 180
		private focusOnContextMenu = true
	) {
E
Erich Gamma 已提交
181 182 183
		super();

		if (isMacintosh) {
A
Alexandru Dima 已提交
184
			this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Backspace, this.onDelete.bind(this));
E
Erich Gamma 已提交
185
		} else {
A
Alexandru Dima 已提交
186 187
			this.downKeyBindingDispatcher.set(KeyCode.Delete, this.onDelete.bind(this));
			this.downKeyBindingDispatcher.set(KeyMod.Shift | KeyCode.Delete, this.onDelete.bind(this));
E
Erich Gamma 已提交
188 189 190
		}
	}

I
isidor 已提交
191
	public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean {
E
Erich Gamma 已提交
192 193 194 195 196 197 198 199 200 201 202 203
		if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
			return false;
		}

		event.preventDefault();
		event.stopPropagation();

		if (this.focusOnContextMenu) {
			tree.setFocus(element);
		}

		if (this.actionProvider.hasSecondaryActions(tree, element)) {
I
isidor 已提交
204
			const anchor = { x: event.posx + 1, y: event.posy };
E
Erich Gamma 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
			this.contextMenuService.showContextMenu({
				getAnchor: () => anchor,
				getActions: () => this.actionProvider.getSecondaryActions(tree, element),
				onHide: (wasCancelled?: boolean) => {
					if (wasCancelled) {
						tree.DOMFocus();
					}
				},
				getActionsContext: () => element
			});

			return true;
		}

		return false;
	}

I
isidor 已提交
222
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
223 224 225 226
		return false;
	}
}

I
isidor 已提交
227
// call stack
E
Erich Gamma 已提交
228

I
isidor 已提交
229
class ThreadAndProcessIds implements debug.ITreeElement {
I
isidor 已提交
230
	constructor(public processId: string, public threadId: number) { }
I
isidor 已提交
231 232 233 234

	public getId(): string {
		return `${this.processId}:${this.threadId}`;
	}
235 236
}

237 238
export class CallStackController extends BaseDebugController {

I
isidor 已提交
239
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
240
		if (element instanceof ThreadAndProcessIds) {
241 242
			return this.showMoreStackFrames(tree, element);
		}
243
		this.focusStackFrame(element, event, true);
244 245 246 247

		return super.onLeftClick(tree, element, event);
	}

I
isidor 已提交
248
	protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
249
		const element = tree.getFocus();
I
isidor 已提交
250
		if (element instanceof ThreadAndProcessIds) {
251 252
			return this.showMoreStackFrames(tree, element);
		}
253
		this.focusStackFrame(element, event, false);
254 255 256 257

		return super.onEnter(tree, event);
	}

I
isidor 已提交
258
	protected onUp(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
259 260 261 262 263 264
		super.onUp(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
265
	protected onPageUp(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
266 267 268 269 270 271
		super.onPageUp(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
272
	protected onDown(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
273 274 275 276 277 278
		super.onDown(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
279
	protected onPageDown(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
280 281 282 283 284 285
		super.onPageDown(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

286
	// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
I
isidor 已提交
287
	private showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
I
isidor 已提交
288 289
		const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
		const thread = process && process.getThread(threadAndProcessIds.threadId);
290
		if (thread) {
291
			(<Thread>thread).fetchCallStack(true)
292 293 294 295 296
				.done(() => tree.refresh(), errors.onUnexpectedError);
		}

		return true;
	}
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311
	private focusStackFrame(element: any, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
		let stackFrame: debug.IStackFrame;
		let process: debug.IProcess;
		if (element instanceof StackFrame) {
			stackFrame = element;
			process = element.thread.process;
		}
		if (element instanceof Thread) {
			process = element.process;
		}
		if (element instanceof Process) {
			process = element;
		}

312
		this.debugService.focusStackFrameAndEvaluate(stackFrame, process).done(null, errors.onUnexpectedError);
313

I
isidor 已提交
314 315
		if (stackFrame) {
			const sideBySide = (event && (event.ctrlKey || event.metaKey));
I
isidor 已提交
316
			this.debugService.openOrRevealSource(stackFrame.source, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
I
isidor 已提交
317
		}
318
	}
319 320 321
}


I
isidor 已提交
322
export class CallStackActionProvider implements IActionProvider {
I
isidor 已提交
323

J
Johannes Rieken 已提交
324
	constructor( @IInstantiationService private instantiationService: IInstantiationService, @debug.IDebugService private debugService: debug.IDebugService) {
I
isidor 已提交
325 326 327
		// noop
	}

I
isidor 已提交
328
	public hasActions(tree: ITree, element: any): boolean {
I
isidor 已提交
329 330 331
		return false;
	}

I
isidor 已提交
332
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
I
isidor 已提交
333 334 335
		return TPromise.as([]);
	}

I
isidor 已提交
336 337
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Thread || element instanceof StackFrame;
I
isidor 已提交
338 339
	}

I
isidor 已提交
340 341 342 343
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		const actions: IAction[] = [];
		if (element instanceof Thread) {
			const thread = <Thread>element;
A
Andre Weinand 已提交
344
			if (thread.stopped) {
I
isidor 已提交
345 346 347 348
				actions.push(this.instantiationService.createInstance(ContinueAction, ContinueAction.ID, ContinueAction.LABEL));
				actions.push(this.instantiationService.createInstance(StepOverAction, StepOverAction.ID, StepOverAction.LABEL));
				actions.push(this.instantiationService.createInstance(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL));
				actions.push(this.instantiationService.createInstance(StepOutAction, StepOutAction.ID, StepOutAction.LABEL));
A
Andre Weinand 已提交
349
			} else {
I
isidor 已提交
350
				actions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL));
A
Andre Weinand 已提交
351
			}
I
isidor 已提交
352
		} else if (element instanceof StackFrame) {
I
isidor 已提交
353
			const capabilities = this.debugService.getViewModel().focusedProcess.session.configuration.capabilities;
I
isidor 已提交
354
			if (typeof capabilities.supportsRestartFrame === 'boolean' && capabilities.supportsRestartFrame) {
I
isidor 已提交
355
				actions.push(this.instantiationService.createInstance(RestartFrameAction, RestartFrameAction.ID, RestartFrameAction.LABEL));
356
			}
I
isidor 已提交
357 358 359 360 361
		}

		return TPromise.as(actions);
	}

I
isidor 已提交
362
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
363 364 365 366
		return null;
	}
}

I
isidor 已提交
367
export class CallStackDataSource implements IDataSource {
E
Erich Gamma 已提交
368

I
isidor 已提交
369
	public getId(tree: ITree, element: any): string {
370 371 372
		if (typeof element === 'string') {
			return element;
		}
I
isidor 已提交
373

E
Erich Gamma 已提交
374 375 376
		return element.getId();
	}

I
isidor 已提交
377 378
	public hasChildren(tree: ITree, element: any): boolean {
		return element instanceof Model || element instanceof Process || (element instanceof Thread && (<Thread>element).stopped);
E
Erich Gamma 已提交
379 380
	}

I
isidor 已提交
381 382
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Thread) {
I
isidor 已提交
383
			return this.getThreadChildren(element);
E
Erich Gamma 已提交
384
		}
I
isidor 已提交
385
		if (element instanceof Model) {
386
			return TPromise.as(element.getProcesses());
387 388
		}

389
		const process = <debug.IProcess>element;
I
isidor 已提交
390
		return TPromise.as(process.getAllThreads());
E
Erich Gamma 已提交
391 392
	}

393 394
	private getThreadChildren(thread: Thread): TPromise<any> {
		return thread.fetchCallStack().then((callStack: any[]) => {
I
isidor 已提交
395
			if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) {
396 397
				return callStack.concat([thread.stoppedDetails.framesErrorMessage]);
			}
I
isidor 已提交
398
			if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length) {
I
isidor 已提交
399
				return callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]);
I
isidor 已提交
400 401 402 403 404 405
			}

			return callStack;
		});
	}

I
isidor 已提交
406
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
407
		return TPromise.as(null);
E
Erich Gamma 已提交
408 409 410 411
	}
}

interface IThreadTemplateData {
I
isidor 已提交
412
	thread: HTMLElement;
E
Erich Gamma 已提交
413
	name: HTMLElement;
I
isidor 已提交
414 415
	state: HTMLElement;
	stateLabel: HTMLSpanElement;
E
Erich Gamma 已提交
416 417
}

I
isidor 已提交
418 419 420
interface IProcessTemplateData {
	process: HTMLElement;
	name: HTMLElement;
I
isidor 已提交
421 422
	state: HTMLElement;
	stateLabel: HTMLSpanElement;
I
isidor 已提交
423 424
}

425 426 427 428
interface IErrorTemplateData {
	label: HTMLElement;
}

I
isidor 已提交
429 430 431 432
interface ILoadMoreTemplateData {
	label: HTMLElement;
}

E
Erich Gamma 已提交
433 434
interface IStackFrameTemplateData {
	stackFrame: HTMLElement;
435 436 437 438
	label: HTMLElement;
	file: HTMLElement;
	fileName: HTMLElement;
	lineNumber: HTMLElement;
E
Erich Gamma 已提交
439 440
}

I
isidor 已提交
441
export class CallStackRenderer implements IRenderer {
E
Erich Gamma 已提交
442 443 444

	private static THREAD_TEMPLATE_ID = 'thread';
	private static STACK_FRAME_TEMPLATE_ID = 'stackFrame';
445
	private static ERROR_TEMPLATE_ID = 'error';
I
isidor 已提交
446
	private static LOAD_MORE_TEMPLATE_ID = 'loadMore';
I
isidor 已提交
447
	private static PROCESS_TEMPLATE_ID = 'process';
E
Erich Gamma 已提交
448

J
Johannes Rieken 已提交
449
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
E
Erich Gamma 已提交
450 451 452
		// noop
	}

I
isidor 已提交
453
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
454
		return 22;
E
Erich Gamma 已提交
455 456
	}

I
isidor 已提交
457 458
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Process) {
I
isidor 已提交
459 460
			return CallStackRenderer.PROCESS_TEMPLATE_ID;
		}
I
isidor 已提交
461
		if (element instanceof Thread) {
E
Erich Gamma 已提交
462 463
			return CallStackRenderer.THREAD_TEMPLATE_ID;
		}
I
isidor 已提交
464
		if (element instanceof StackFrame) {
E
Erich Gamma 已提交
465 466
			return CallStackRenderer.STACK_FRAME_TEMPLATE_ID;
		}
467 468 469
		if (typeof element === 'string') {
			return CallStackRenderer.ERROR_TEMPLATE_ID;
		}
E
Erich Gamma 已提交
470

I
isidor 已提交
471
		return CallStackRenderer.LOAD_MORE_TEMPLATE_ID;
E
Erich Gamma 已提交
472 473
	}

I
isidor 已提交
474
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
475 476 477 478
		if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) {
			let data: IProcessTemplateData = Object.create(null);
			data.process = dom.append(container, $('.process'));
			data.name = dom.append(data.process, $('.name'));
I
isidor 已提交
479 480
			data.state = dom.append(data.process, $('.state'));
			data.stateLabel = dom.append(data.state, $('span.label'));
I
isidor 已提交
481 482 483 484

			return data;
		}

I
isidor 已提交
485 486 487 488 489 490
		if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.load-more'));

			return data;
		}
491 492 493 494 495 496
		if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.error'));

			return data;
		}
E
Erich Gamma 已提交
497 498
		if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
			let data: IThreadTemplateData = Object.create(null);
I
isidor 已提交
499 500 501 502
			data.thread = dom.append(container, $('.thread'));
			data.name = dom.append(data.thread, $('.name'));
			data.state = dom.append(data.thread, $('.state'));
			data.stateLabel = dom.append(data.state, $('span.label'));
E
Erich Gamma 已提交
503 504 505 506 507 508

			return data;
		}

		let data: IStackFrameTemplateData = Object.create(null);
		data.stackFrame = dom.append(container, $('.stack-frame'));
509
		data.label = dom.append(data.stackFrame, $('span.label.expression'));
E
Erich Gamma 已提交
510 511 512 513 514 515 516
		data.file = dom.append(data.stackFrame, $('.file'));
		data.fileName = dom.append(data.file, $('span.file-name'));
		data.lineNumber = dom.append(data.file, $('span.line-number'));

		return data;
	}

I
isidor 已提交
517
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
518
		if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) {
I
isidor 已提交
519 520
			this.renderProcess(element, templateData);
		} else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
E
Erich Gamma 已提交
521
			this.renderThread(element, templateData);
I
isidor 已提交
522
		} else if (templateId === CallStackRenderer.STACK_FRAME_TEMPLATE_ID) {
E
Erich Gamma 已提交
523
			this.renderStackFrame(element, templateData);
524 525
		} else if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
			this.renderError(element, templateData);
I
isidor 已提交
526
		} else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
I
isidor 已提交
527
			this.renderLoadMore(element, templateData);
E
Erich Gamma 已提交
528 529 530
		}
	}

I
isidor 已提交
531
	private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void {
I
isidor 已提交
532
		data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process");
I
isidor 已提交
533
		data.name.textContent = process.name;
I
isidor 已提交
534 535 536 537
		const stoppedThread = process.getAllThreads().filter(t => t.stopped).pop();

		data.stateLabel.textContent = stoppedThread ? nls.localize('paused', "Paused")
			: nls.localize({ key: 'running', comment: ['indicates state'] }, "Running");
I
isidor 已提交
538 539
	}

E
Erich Gamma 已提交
540
	private renderThread(thread: debug.IThread, data: IThreadTemplateData): void {
I
isidor 已提交
541
		data.thread.title = nls.localize('thread', "Thread");
E
Erich Gamma 已提交
542
		data.name.textContent = thread.name;
I
isidor 已提交
543 544 545

		data.stateLabel.textContent = thread.stopped ? nls.localize('pausedOn', "Paused on {0}", thread.stoppedDetails.reason)
			: nls.localize({ key: 'running', comment: ['indicates state'] }, "Running");
E
Erich Gamma 已提交
546 547
	}

548 549
	private renderError(element: string, data: IErrorTemplateData) {
		data.label.textContent = element;
I
isidor 已提交
550
		data.label.title = element;
551 552
	}

I
isidor 已提交
553
	private renderLoadMore(element: any, data: ILoadMoreTemplateData): void {
I
isidor 已提交
554
		data.label.textContent = nls.localize('loadMoreStackFrames', "Load More Stack Frames");
I
isidor 已提交
555 556
	}

E
Erich Gamma 已提交
557 558 559 560
	private renderStackFrame(stackFrame: debug.IStackFrame, data: IStackFrameTemplateData): void {
		stackFrame.source.available ? dom.removeClass(data.stackFrame, 'disabled') : dom.addClass(data.stackFrame, 'disabled');
		data.file.title = stackFrame.source.uri.fsPath;
		data.label.textContent = stackFrame.name;
561
		data.label.title = stackFrame.name;
562
		data.fileName.textContent = getSourceName(stackFrame.source, this.contextService);
I
isidor 已提交
563
		if (stackFrame.lineNumber !== undefined) {
564
			data.lineNumber.textContent = `${stackFrame.lineNumber}`;
I
isidor 已提交
565 566 567 568
			dom.removeClass(data.lineNumber, 'unavailable');
		} else {
			dom.addClass(data.lineNumber, 'unavailable');
		}
E
Erich Gamma 已提交
569 570
	}

I
isidor 已提交
571
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
572 573 574 575
		// noop
	}
}

I
isidor 已提交
576
export class CallstackAccessibilityProvider implements IAccessibilityProvider {
577

J
Johannes Rieken 已提交
578
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
579 580 581
		// noop
	}

I
isidor 已提交
582 583 584
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Thread) {
			return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (<Thread>element).name);
585
		}
I
isidor 已提交
586 587
		if (element instanceof StackFrame) {
			return nls.localize('stackFrameAriaLabel', "Stack Frame {0} line {1} {2}, callstack, debug", (<StackFrame>element).name, (<StackFrame>element).lineNumber, getSourceName((<StackFrame>element).source, this.contextService));
588 589 590 591 592 593
		}

		return null;
	}
}

I
isidor 已提交
594
// variables
E
Erich Gamma 已提交
595

I
isidor 已提交
596
export class VariablesActionProvider implements IActionProvider {
E
Erich Gamma 已提交
597

I
isidor 已提交
598 599
	constructor(private instantiationService: IInstantiationService) {
		// noop
E
Erich Gamma 已提交
600 601
	}

I
isidor 已提交
602
	public hasActions(tree: ITree, element: any): boolean {
E
Erich Gamma 已提交
603 604 605
		return false;
	}

I
isidor 已提交
606
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
A
Alex Dima 已提交
607
		return TPromise.as([]);
I
isidor 已提交
608 609
	}

I
isidor 已提交
610 611
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Variable;
E
Erich Gamma 已提交
612 613
	}

I
isidor 已提交
614 615 616
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		let actions: IAction[] = [];
		const variable = <Variable>element;
617
		if (!variable.hasChildren) {
I
isidor 已提交
618
			actions.push(this.instantiationService.createInstance(SetValueAction, SetValueAction.ID, SetValueAction.LABEL, variable));
619
			actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable));
I
isidor 已提交
620
			actions.push(new Separator());
E
Erich Gamma 已提交
621 622
		}

I
isidor 已提交
623
		actions.push(this.instantiationService.createInstance(AddToWatchExpressionsAction, AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable));
A
Alex Dima 已提交
624
		return TPromise.as(actions);
E
Erich Gamma 已提交
625
	}
I
isidor 已提交
626

I
isidor 已提交
627
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
628 629
		return null;
	}
E
Erich Gamma 已提交
630 631
}

I
isidor 已提交
632
export class VariablesDataSource implements IDataSource {
E
Erich Gamma 已提交
633

I
isidor 已提交
634
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
635 636 637
		return element.getId();
	}

I
isidor 已提交
638 639
	public hasChildren(tree: ITree, element: any): boolean {
		if (element instanceof ViewModel || element instanceof Scope) {
E
Erich Gamma 已提交
640 641 642
			return true;
		}

I
isidor 已提交
643
		let variable = <Variable>element;
644
		return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null');
E
Erich Gamma 已提交
645 646
	}

I
isidor 已提交
647 648 649
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof ViewModel) {
			const focusedStackFrame = (<ViewModel>element).focusedStackFrame;
650
			return focusedStackFrame ? focusedStackFrame.getScopes() : TPromise.as([]);
E
Erich Gamma 已提交
651 652
		}

I
isidor 已提交
653
		let scope = <Scope>element;
654
		return scope.getChildren();
E
Erich Gamma 已提交
655 656
	}

I
isidor 已提交
657
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
658
		return TPromise.as(null);
E
Erich Gamma 已提交
659 660 661 662 663 664 665 666 667 668 669 670 671
	}
}

interface IScopeTemplateData {
	name: HTMLElement;
}

export interface IVariableTemplateData {
	expression: HTMLElement;
	name: HTMLElement;
	value: HTMLElement;
}

I
isidor 已提交
672
export class VariablesRenderer implements IRenderer {
E
Erich Gamma 已提交
673 674 675 676

	private static SCOPE_TEMPLATE_ID = 'scope';
	private static VARIABLE_TEMPLATE_ID = 'variable';

I
isidor 已提交
677 678 679 680 681 682 683
	constructor(
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
	) {
		// noop
	}

I
isidor 已提交
684
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
685
		return 22;
E
Erich Gamma 已提交
686 687
	}

I
isidor 已提交
688 689
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Scope) {
E
Erich Gamma 已提交
690 691
			return VariablesRenderer.SCOPE_TEMPLATE_ID;
		}
I
isidor 已提交
692
		if (element instanceof Variable) {
E
Erich Gamma 已提交
693 694 695 696 697 698
			return VariablesRenderer.VARIABLE_TEMPLATE_ID;
		}

		return null;
	}

I
isidor 已提交
699
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
E
Erich Gamma 已提交
700 701 702 703 704 705 706 707
		if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
			let data: IScopeTemplateData = Object.create(null);
			data.name = dom.append(container, $('.scope'));

			return data;
		}

		let data: IVariableTemplateData = Object.create(null);
708
		data.expression = dom.append(container, $('.expression'));
E
Erich Gamma 已提交
709 710 711 712 713 714
		data.name = dom.append(data.expression, $('span.name'));
		data.value = dom.append(data.expression, $('span.value'));

		return data;
	}

I
isidor 已提交
715
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
716 717 718
		if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
			this.renderScope(element, templateData);
		} else {
I
isidor 已提交
719
			const variable = <Variable>element;
720 721 722 723 724 725 726
			if (variable === this.debugService.getViewModel().getSelectedExpression() || variable.errorMessage) {
				renderRenameBox(this.debugService, this.contextViewService, tree, variable, (<IVariableTemplateData>templateData).expression, {
					initialValue: variable.value,
					ariaLabel: nls.localize('variableValueAriaLabel', "Type new variable value"),
					validationOptions: {
						validation: (value: string) => variable.errorMessage ? ({ content: variable.errorMessage }) : null
					}
I
isidor 已提交
727
				});
I
isidor 已提交
728
			} else {
729
				renderVariable(tree, variable, templateData, true);
I
isidor 已提交
730
			}
E
Erich Gamma 已提交
731 732 733
		}
	}

I
isidor 已提交
734
	private renderScope(scope: Scope, data: IScopeTemplateData): void {
E
Erich Gamma 已提交
735 736 737
		data.name.textContent = scope.name;
	}

I
isidor 已提交
738
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
739 740 741 742
		// noop
	}
}

I
isidor 已提交
743
export class VariablesAccessibilityProvider implements IAccessibilityProvider {
744

I
isidor 已提交
745 746 747
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Scope) {
			return nls.localize('variableScopeAriaLabel', "Scope {0}, variables, debug", (<Scope>element).name);
748
		}
I
isidor 已提交
749 750
		if (element instanceof Variable) {
			return nls.localize('variableAriaLabel', "{0} value {1}, variables, debug", (<Variable>element).name, (<Variable>element).value);
751 752 753 754 755 756
		}

		return null;
	}
}

757 758
export class VariablesController extends BaseDebugController {

I
isidor 已提交
759
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
760
		super(debugService, contextMenuService, actionProvider);
A
Alexandru Dima 已提交
761
		this.downKeyBindingDispatcher.set(KeyCode.Enter, this.setSelectedExpression.bind(this));
762 763
	}

I
isidor 已提交
764
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
765
		// double click on primitive value: open input box to be able to set the value
I
isidor 已提交
766
		if (element instanceof Variable && event.detail === 2) {
767
			const expression = <debug.IExpression>element;
768
			if (!expression.hasChildren) {
769 770 771 772 773 774 775
				this.debugService.getViewModel().setSelectedExpression(expression);
			}
			return true;
		}

		return super.onLeftClick(tree, element, event);
	}
I
isidor 已提交
776

I
isidor 已提交
777
	protected setSelectedExpression(tree: ITree, event: KeyboardEvent): boolean {
I
isidor 已提交
778
		const element = tree.getFocus();
779
		if (element instanceof Variable && !element.hasChildren) {
I
isidor 已提交
780
			this.debugService.getViewModel().setSelectedExpression(element);
781
			return true;
I
isidor 已提交
782 783
		}

784
		return false;
I
isidor 已提交
785
	}
786 787
}

I
isidor 已提交
788
// watch expressions
E
Erich Gamma 已提交
789

I
isidor 已提交
790
export class WatchExpressionsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
791 792 793 794 795 796 797

	private instantiationService: IInstantiationService;

	constructor(instantiationService: IInstantiationService) {
		this.instantiationService = instantiationService;
	}

I
isidor 已提交
798 799
	public hasActions(tree: ITree, element: any): boolean {
		return element instanceof Expression && !!element.name;
E
Erich Gamma 已提交
800 801
	}

I
isidor 已提交
802
	public hasSecondaryActions(tree: ITree, element: any): boolean {
E
Erich Gamma 已提交
803 804 805
		return true;
	}

I
isidor 已提交
806
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
A
Alex Dima 已提交
807
		return TPromise.as(this.getExpressionActions());
E
Erich Gamma 已提交
808 809
	}

I
isidor 已提交
810 811
	public getExpressionActions(): IAction[] {
		return [this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL)];
E
Erich Gamma 已提交
812 813
	}

I
isidor 已提交
814 815 816 817 818 819
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		const actions: IAction[] = [];
		if (element instanceof Expression) {
			const expression = <Expression>element;
			actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL));
			actions.push(this.instantiationService.createInstance(EditWatchExpressionAction, EditWatchExpressionAction.ID, EditWatchExpressionAction.LABEL, expression));
820
			if (!expression.hasChildren) {
821
				actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value));
E
Erich Gamma 已提交
822
			}
I
isidor 已提交
823
			actions.push(new Separator());
E
Erich Gamma 已提交
824

I
isidor 已提交
825 826
			actions.push(this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL));
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
827
		} else {
I
isidor 已提交
828 829 830
			actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL));
			if (element instanceof Variable) {
				const variable = <Variable>element;
831
				if (!variable.hasChildren) {
832
					actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable.value));
E
Erich Gamma 已提交
833
				}
I
isidor 已提交
834
				actions.push(new Separator());
E
Erich Gamma 已提交
835
			}
I
isidor 已提交
836
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
837 838
		}

A
Alex Dima 已提交
839
		return TPromise.as(actions);
E
Erich Gamma 已提交
840
	}
I
isidor 已提交
841

I
isidor 已提交
842
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
843 844
		return null;
	}
E
Erich Gamma 已提交
845 846
}

I
isidor 已提交
847
export class WatchExpressionsDataSource implements IDataSource {
E
Erich Gamma 已提交
848

I
isidor 已提交
849
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
850 851 852
		return element.getId();
	}

I
isidor 已提交
853 854
	public hasChildren(tree: ITree, element: any): boolean {
		if (element instanceof Model) {
E
Erich Gamma 已提交
855 856 857
			return true;
		}

I
isidor 已提交
858
		const watchExpression = <Expression>element;
859
		return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null');
E
Erich Gamma 已提交
860 861
	}

I
isidor 已提交
862 863 864
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Model) {
			return TPromise.as((<Model>element).getWatchExpressions());
E
Erich Gamma 已提交
865 866
		}

I
isidor 已提交
867
		let expression = <Expression>element;
868
		return expression.getChildren();
E
Erich Gamma 已提交
869 870
	}

I
isidor 已提交
871
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
872
		return TPromise.as(null);
E
Erich Gamma 已提交
873 874 875
	}
}

I
isidor 已提交
876 877
interface IWatchExpressionTemplateData {
	watchExpression: HTMLElement;
I
isidor 已提交
878
	actionBar: ActionBar;
I
isidor 已提交
879 880 881
	expression: HTMLElement;
	name: HTMLSpanElement;
	value: HTMLSpanElement;
E
Erich Gamma 已提交
882 883
}

I
isidor 已提交
884
export class WatchExpressionsRenderer implements IRenderer {
E
Erich Gamma 已提交
885 886 887 888 889 890

	private static WATCH_EXPRESSION_TEMPLATE_ID = 'watchExpression';
	private static VARIABLE_TEMPLATE_ID = 'variables';
	private toDispose: lifecycle.IDisposable[];
	private actionProvider: WatchExpressionsActionProvider;

I
isidor 已提交
891
	constructor(
I
isidor 已提交
892 893
		actionProvider: IActionProvider,
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
894 895 896 897
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
	) {
		this.toDispose = [];
898
		this.actionProvider = <WatchExpressionsActionProvider>actionProvider;
E
Erich Gamma 已提交
899 900
	}

I
isidor 已提交
901
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
902
		return 22;
E
Erich Gamma 已提交
903 904
	}

I
isidor 已提交
905 906
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Expression) {
E
Erich Gamma 已提交
907 908 909 910 911 912
			return WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID;
		}

		return WatchExpressionsRenderer.VARIABLE_TEMPLATE_ID;
	}

I
isidor 已提交
913
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
914 915 916 917 918 919
		const createVariableTemplate = ((data: IVariableTemplateData, container: HTMLElement) => {
			data.expression = dom.append(container, $('.expression'));
			data.name = dom.append(data.expression, $('span.name'));
			data.value = dom.append(data.expression, $('span.value'));
		});

E
Erich Gamma 已提交
920
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
I
isidor 已提交
921 922 923 924
			const data: IWatchExpressionTemplateData = Object.create(null);
			data.watchExpression = dom.append(container, $('.watch-expression'));
			createVariableTemplate(data, data.watchExpression);
			data.actionBar = new ActionBar(data.watchExpression, { actionRunner: this.actionRunner });
925
			data.actionBar.push(this.actionProvider.getExpressionActions(), { icon: true, label: false });
I
isidor 已提交
926 927

			return data;
E
Erich Gamma 已提交
928 929
		}

I
isidor 已提交
930 931
		const data: IVariableTemplateData = Object.create(null);
		createVariableTemplate(data, container);
E
Erich Gamma 已提交
932 933 934 935

		return data;
	}

I
isidor 已提交
936
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
937 938 939
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
			this.renderWatchExpression(tree, element, templateData);
		} else {
940
			renderVariable(tree, element, templateData, true);
E
Erich Gamma 已提交
941 942 943
		}
	}

I
isidor 已提交
944
	private renderWatchExpression(tree: ITree, watchExpression: debug.IExpression, data: IWatchExpressionTemplateData): void {
E
Erich Gamma 已提交
945
		let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
I
isidor 已提交
946
		if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
I
isidor 已提交
947 948 949 950 951
			renderRenameBox(this.debugService, this.contextViewService, tree, watchExpression, data.expression, {
				initialValue: watchExpression.name,
				placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),
				ariaLabel: nls.localize('watchExpressionInputAriaLabel', "Type watch expression")
			});
E
Erich Gamma 已提交
952 953 954
		}
		data.actionBar.context = watchExpression;

955
		data.name.textContent = watchExpression.name;
956
		if (watchExpression.value) {
957
			data.name.textContent += ':';
958 959 960 961 962
			renderExpressionValue(watchExpression, data.value, {
				showChanged: true,
				maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
				preserveWhitespace: false
			});
963
			data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value;
E
Erich Gamma 已提交
964 965 966
		}
	}

I
isidor 已提交
967
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
968 969 970
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
			(<IWatchExpressionTemplateData>templateData).actionBar.dispose();
		}
E
Erich Gamma 已提交
971 972 973
	}

	public dispose(): void {
J
Joao Moreno 已提交
974
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
975 976 977
	}
}

I
isidor 已提交
978
export class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider {
979

I
isidor 已提交
980 981 982
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Expression) {
			return nls.localize('watchExpressionAriaLabel', "{0} value {1}, watch, debug", (<Expression>element).name, (<Expression>element).value);
983
		}
I
isidor 已提交
984 985
		if (element instanceof Variable) {
			return nls.localize('watchVariableAriaLabel', "{0} value {1}, watch, debug", (<Variable>element).name, (<Variable>element).value);
986 987 988 989 990 991
		}

		return null;
	}
}

E
Erich Gamma 已提交
992 993
export class WatchExpressionsController extends BaseDebugController {

I
isidor 已提交
994
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
E
Erich Gamma 已提交
995 996 997
		super(debugService, contextMenuService, actionProvider);

		if (isMacintosh) {
A
Alexandru Dima 已提交
998
			this.downKeyBindingDispatcher.set(KeyCode.Enter, this.onRename.bind(this));
E
Erich Gamma 已提交
999
		} else {
A
Alexandru Dima 已提交
1000
			this.downKeyBindingDispatcher.set(KeyCode.F2, this.onRename.bind(this));
E
Erich Gamma 已提交
1001 1002 1003
		}
	}

I
isidor 已提交
1004
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
1005
		// double click on primitive value: open input box to be able to select and copy value.
I
isidor 已提交
1006
		if (element instanceof Expression && event.detail === 2) {
1007
			const expression = <debug.IExpression>element;
1008
			if (!expression.hasChildren) {
E
Erich Gamma 已提交
1009 1010 1011 1012 1013 1014 1015 1016
				this.debugService.getViewModel().setSelectedExpression(expression);
			}
			return true;
		}

		return super.onLeftClick(tree, element, event);
	}

I
isidor 已提交
1017
	protected onRename(tree: ITree, event: KeyboardEvent): boolean {
I
isidor 已提交
1018
		const element = tree.getFocus();
I
isidor 已提交
1019 1020
		if (element instanceof Expression) {
			const watchExpression = <Expression>element;
1021
			if (!watchExpression.hasChildren) {
E
Erich Gamma 已提交
1022 1023 1024 1025 1026 1027 1028 1029
				this.debugService.getViewModel().setSelectedExpression(watchExpression);
			}
			return true;
		}

		return false;
	}

I
isidor 已提交
1030
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
1031
		const element = tree.getFocus();
I
isidor 已提交
1032 1033
		if (element instanceof Expression) {
			const we = <Expression>element;
1034
			this.debugService.removeWatchExpressions(we.getId());
E
Erich Gamma 已提交
1035 1036 1037 1038 1039 1040 1041 1042

			return true;
		}

		return false;
	}
}

I
isidor 已提交
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
export class WatchExpressionsDragAndDrop extends DefaultDragAndDrop {

	constructor( @debug.IDebugService private debugService: debug.IDebugService) {
		super();
	}

	public getDragURI(tree: ITree, element: Expression): string {
		if (!(element instanceof Expression)) {
			return null;
		}

		return element.getId();
	}

	public getDragLabel(tree: ITree, elements: Expression[]): string {
		if (elements.length > 1) {
			return String(elements.length);
		}

		return elements[0].name;
	}

	public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): IDragOverReaction {
		return DRAG_OVER_ACCEPT;
	}

	public drop(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): void {
		const draggedData = data.getData();
		if (Array.isArray(draggedData)) {
			const draggedElement = <Expression>draggedData[0];
			const watches = this.debugService.getModel().getWatchExpressions();
			const position = target instanceof Model ? watches.length - 1 : watches.indexOf(target);
			this.debugService.moveWatchExpression(draggedElement.getId(), position);
		}
	}
}

I
isidor 已提交
1080
// breakpoints
E
Erich Gamma 已提交
1081

I
isidor 已提交
1082
export class BreakpointsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
1083 1084

	constructor(private instantiationService: IInstantiationService) {
I
isidor 已提交
1085
		// noop
E
Erich Gamma 已提交
1086 1087
	}

I
isidor 已提交
1088 1089
	public hasActions(tree: ITree, element: any): boolean {
		return element instanceof Breakpoint;
E
Erich Gamma 已提交
1090 1091
	}

I
isidor 已提交
1092 1093
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Breakpoint || element instanceof ExceptionBreakpoint || element instanceof FunctionBreakpoint;
E
Erich Gamma 已提交
1094 1095
	}

I
isidor 已提交
1096 1097
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
		if (element instanceof Breakpoint) {
A
Alex Dima 已提交
1098
			return TPromise.as(this.getBreakpointActions());
E
Erich Gamma 已提交
1099 1100
		}

A
Alex Dima 已提交
1101
		return TPromise.as([]);
E
Erich Gamma 已提交
1102 1103
	}

I
isidor 已提交
1104 1105
	public getBreakpointActions(): IAction[] {
		return [this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)];
E
Erich Gamma 已提交
1106 1107
	}

I
isidor 已提交
1108 1109 1110
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		const actions: IAction[] = [this.instantiationService.createInstance(ToggleEnablementAction, ToggleEnablementAction.ID, ToggleEnablementAction.LABEL)];
		actions.push(new Separator());
E
Erich Gamma 已提交
1111

I
isidor 已提交
1112 1113
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
			actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
1114
		}
I
isidor 已提交
1115 1116
		actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL));
		actions.push(new Separator());
E
Erich Gamma 已提交
1117

I
isidor 已提交
1118 1119
		actions.push(this.instantiationService.createInstance(ToggleBreakpointsActivatedAction, ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL));
		actions.push(new Separator());
E
Erich Gamma 已提交
1120

I
isidor 已提交
1121 1122 1123
		actions.push(this.instantiationService.createInstance(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL));
		actions.push(this.instantiationService.createInstance(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL));
		actions.push(new Separator());
E
Erich Gamma 已提交
1124

I
isidor 已提交
1125 1126 1127
		actions.push(this.instantiationService.createInstance(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL));
		if (element instanceof FunctionBreakpoint) {
			actions.push(this.instantiationService.createInstance(RenameFunctionBreakpointAction, RenameFunctionBreakpointAction.ID, RenameFunctionBreakpointAction.LABEL));
1128
		}
I
isidor 已提交
1129
		actions.push(new Separator());
I
isidor 已提交
1130

I
isidor 已提交
1131
		actions.push(this.instantiationService.createInstance(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL));
E
Erich Gamma 已提交
1132

A
Alex Dima 已提交
1133
		return TPromise.as(actions);
E
Erich Gamma 已提交
1134
	}
I
isidor 已提交
1135

I
isidor 已提交
1136
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
1137 1138
		return null;
	}
E
Erich Gamma 已提交
1139 1140
}

I
isidor 已提交
1141
export class BreakpointsDataSource implements IDataSource {
E
Erich Gamma 已提交
1142

I
isidor 已提交
1143
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
1144 1145 1146
		return element.getId();
	}

I
isidor 已提交
1147 1148
	public hasChildren(tree: ITree, element: any): boolean {
		return element instanceof Model;
E
Erich Gamma 已提交
1149 1150
	}

I
isidor 已提交
1151 1152
	public getChildren(tree: ITree, element: any): TPromise<any> {
		const model = <Model>element;
1153
		const exBreakpoints = <debug.IEnablement[]>model.getExceptionBreakpoints();
E
Erich Gamma 已提交
1154

A
Alex Dima 已提交
1155
		return TPromise.as(exBreakpoints.concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints()));
E
Erich Gamma 已提交
1156 1157
	}

I
isidor 已提交
1158
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
1159
		return TPromise.as(null);
E
Erich Gamma 已提交
1160 1161 1162
	}
}

1163
interface IBaseBreakpointTemplateData {
1164
	breakpoint: HTMLElement;
E
Erich Gamma 已提交
1165 1166
	name: HTMLElement;
	checkbox: HTMLInputElement;
1167 1168
	context: debug.IEnablement;
	toDispose: lifecycle.IDisposable[];
E
Erich Gamma 已提交
1169 1170
}

1171
interface IBreakpointTemplateData extends IBaseBreakpointTemplateData {
E
Erich Gamma 已提交
1172 1173 1174 1175
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

I
isidor 已提交
1176
export class BreakpointsRenderer implements IRenderer {
E
Erich Gamma 已提交
1177 1178

	private static EXCEPTION_BREAKPOINT_TEMPLATE_ID = 'exceptionBreakpoint';
1179
	private static FUNCTION_BREAKPOINT_TEMPLATE_ID = 'functionBreakpoint';
E
Erich Gamma 已提交
1180 1181 1182 1183
	private static BREAKPOINT_TEMPLATE_ID = 'breakpoint';

	constructor(
		private actionProvider: BreakpointsActionProvider,
I
isidor 已提交
1184
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
1185
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
1186 1187
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
E
Erich Gamma 已提交
1188 1189 1190 1191
	) {
		// noop
	}

I
isidor 已提交
1192
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
1193
		return 22;
E
Erich Gamma 已提交
1194 1195
	}

I
isidor 已提交
1196 1197
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
E
Erich Gamma 已提交
1198 1199
			return BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1200
		if (element instanceof FunctionBreakpoint) {
1201 1202
			return BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1203
		if (element instanceof ExceptionBreakpoint) {
E
Erich Gamma 已提交
1204 1205 1206 1207 1208 1209
			return BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID;
		}

		return null;
	}

I
isidor 已提交
1210
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
1211
		const data: IBreakpointTemplateData = Object.create(null);
I
isidor 已提交
1212
		data.breakpoint = dom.append(container, $('.breakpoint'));
E
Erich Gamma 已提交
1213

1214
		data.checkbox = <HTMLInputElement>$('input');
E
Erich Gamma 已提交
1215
		data.checkbox.type = 'checkbox';
1216 1217 1218 1219
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));
1220

1221
		dom.append(data.breakpoint, data.checkbox);
E
Erich Gamma 已提交
1222

1223
		data.name = dom.append(data.breakpoint, $('span.name'));
E
Erich Gamma 已提交
1224 1225

		if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID) {
1226
			data.filePath = dom.append(data.breakpoint, $('span.file-path'));
I
isidor 已提交
1227 1228 1229 1230 1231
			const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container'));
			data.lineNumber = dom.append(lineNumberContainer, $('span.line-number'));
		}
		if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) {
			dom.addClass(data.breakpoint, 'exception');
E
Erich Gamma 已提交
1232 1233 1234 1235 1236
		}

		return data;
	}

I
isidor 已提交
1237
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
1238
		templateData.context = element;
E
Erich Gamma 已提交
1239 1240
		if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderExceptionBreakpoint(element, templateData);
1241 1242
		} else if (templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderFunctionBreakpoint(tree, element, templateData);
E
Erich Gamma 已提交
1243 1244 1245 1246 1247
		} else {
			this.renderBreakpoint(tree, element, templateData);
		}
	}

1248
	private renderExceptionBreakpoint(exceptionBreakpoint: debug.IExceptionBreakpoint, data: IBaseBreakpointTemplateData): void {
1249
		data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;;
1250
		data.breakpoint.title = data.name.textContent;
E
Erich Gamma 已提交
1251
		data.checkbox.checked = exceptionBreakpoint.enabled;
1252
	}
E
Erich Gamma 已提交
1253

1254
	private renderFunctionBreakpoint(tree: ITree, functionBreakpoint: debug.IFunctionBreakpoint, data: IBaseBreakpointTemplateData): void {
I
isidor 已提交
1255 1256
		const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
		if (!functionBreakpoint.name || (selected && selected.getId() === functionBreakpoint.getId())) {
I
isidor 已提交
1257 1258 1259 1260 1261
			renderRenameBox(this.debugService, this.contextViewService, tree, functionBreakpoint, data.breakpoint, {
				initialValue: functionBreakpoint.name,
				placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"),
				ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint")
			});
1262 1263 1264
		} else {
			data.name.textContent = functionBreakpoint.name;
			data.checkbox.checked = functionBreakpoint.enabled;
1265 1266 1267
			data.breakpoint.title = functionBreakpoint.name;

			// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
1268
			const process = this.debugService.getViewModel().focusedProcess;
I
isidor 已提交
1269
			if ((process && !process.session.configuration.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()) {
1270
				tree.addTraits('disabled', [functionBreakpoint]);
I
isidor 已提交
1271
				if (process && !process.session.configuration.capabilities.supportsFunctionBreakpoints) {
1272 1273 1274 1275 1276
					data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
				}
			} else {
				tree.removeTraits('disabled', [functionBreakpoint]);
			}
1277
		}
E
Erich Gamma 已提交
1278 1279
	}

I
isidor 已提交
1280
	private renderBreakpoint(tree: ITree, breakpoint: debug.IBreakpoint, data: IBreakpointTemplateData): void {
1281
		this.debugService.getModel().areBreakpointsActivated() ? tree.removeTraits('disabled', [breakpoint]) : tree.addTraits('disabled', [breakpoint]);
E
Erich Gamma 已提交
1282

1283
		data.name.textContent = getPathLabel(paths.basename(breakpoint.uri.fsPath), this.contextService);
1284
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
1285
		data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService);
E
Erich Gamma 已提交
1286
		data.checkbox.checked = breakpoint.enabled;
I
isidor 已提交
1287

I
isidor 已提交
1288
		const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing;
I
isidor 已提交
1289 1290 1291 1292 1293
		if (debugActive && !breakpoint.verified) {
			tree.addTraits('disabled', [breakpoint]);
			if (breakpoint.message) {
				data.breakpoint.title = breakpoint.message;
			}
1294 1295
		} else if (breakpoint.condition || breakpoint.hitCondition) {
			data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
1296
		}
E
Erich Gamma 已提交
1297 1298
	}

I
isidor 已提交
1299
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
1300
		lifecycle.dispose(templateData.toDispose);
E
Erich Gamma 已提交
1301 1302 1303
	}
}

I
isidor 已提交
1304
export class BreakpointsAccessibilityProvider implements IAccessibilityProvider {
1305

J
Johannes Rieken 已提交
1306
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
1307 1308 1309
		// noop
	}

I
isidor 已提交
1310 1311
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
1312
			return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (<Breakpoint>element).lineNumber, getPathLabel(paths.basename((<Breakpoint>element).uri.fsPath), this.contextService), this.contextService);
1313
		}
I
isidor 已提交
1314 1315
		if (element instanceof FunctionBreakpoint) {
			return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (<FunctionBreakpoint>element).name);
1316
		}
I
isidor 已提交
1317 1318
		if (element instanceof ExceptionBreakpoint) {
			return nls.localize('exceptionBreakpointAriaLabel', "Exception breakpoint {0}, breakpoints, debug", (<ExceptionBreakpoint>element).filter);
1319 1320 1321 1322 1323 1324
		}

		return null;
	}
}

E
Erich Gamma 已提交
1325 1326
export class BreakpointsController extends BaseDebugController {

I
isidor 已提交
1327
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
I
isidor 已提交
1328 1329
		super(debugService, contextMenuService, actionProvider);
		if (isMacintosh) {
A
Alexandru Dima 已提交
1330
			this.downKeyBindingDispatcher.set(KeyCode.Enter, this.onRename.bind(this));
I
isidor 已提交
1331
		} else {
A
Alexandru Dima 已提交
1332
			this.downKeyBindingDispatcher.set(KeyCode.F2, this.onRename.bind(this));
I
isidor 已提交
1333 1334 1335
		}
	}

I
isidor 已提交
1336 1337
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
		if (element instanceof FunctionBreakpoint && event.detail === 2) {
I
isidor 已提交
1338 1339 1340
			this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
			return true;
		}
I
isidor 已提交
1341
		if (element instanceof Breakpoint) {
1342 1343
			this.openBreakpointSource(element, event, true);
		}
I
isidor 已提交
1344 1345

		return super.onLeftClick(tree, element, event);
E
Erich Gamma 已提交
1346 1347
	}

I
isidor 已提交
1348
	protected onRename(tree: ITree, event: IKeyboardEvent): boolean {
1349
		const element = tree.getFocus();
I
isidor 已提交
1350
		if (element instanceof FunctionBreakpoint && element.name) {
1351 1352 1353
			this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
			return true;
		}
I
isidor 已提交
1354
		if (element instanceof Breakpoint) {
1355 1356 1357 1358 1359 1360
			this.openBreakpointSource(element, event, false);
		}

		return super.onEnter(tree, event);
	}

I
isidor 已提交
1361
	protected onSpace(tree: ITree, event: IKeyboardEvent): boolean {
1362 1363
		super.onSpace(tree, event);
		const element = <debug.IEnablement>tree.getFocus();
1364
		this.debugService.enableOrDisableBreakpoints(!element.enabled, element).done(null, errors.onUnexpectedError);
1365 1366 1367 1368

		return true;
	}

I
isidor 已提交
1369
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
1370
		const element = tree.getFocus();
I
isidor 已提交
1371 1372
		if (element instanceof Breakpoint) {
			this.debugService.removeBreakpoints((<Breakpoint>element).getId()).done(null, errors.onUnexpectedError);
1373
			return true;
I
isidor 已提交
1374 1375
		} else if (element instanceof FunctionBreakpoint) {
			const fbp = <FunctionBreakpoint>element;
1376
			this.debugService.removeFunctionBreakpoints(fbp.getId()).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
1377 1378 1379 1380 1381 1382

			return true;
		}

		return false;
	}
1383

1384
	private openBreakpointSource(breakpoint: Breakpoint, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
1385 1386
		const sideBySide = (event && (event.ctrlKey || event.metaKey));
		this.debugService.openOrRevealSource(breakpoint.uri, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
1387
	}
E
Erich Gamma 已提交
1388
}