debugViewer.ts 48.9 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
export function renderExpressionValue(expressionOrValue: debug.IExpression | string, container: HTMLElement, showChanged: boolean, maxValueRenderLength?: number): void {
41
	let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value;
E
Erich Gamma 已提交
42

I
isidor 已提交
43
	// remove stale classes
E
Erich Gamma 已提交
44
	container.className = 'value';
I
isidor 已提交
45
	// when resolving expressions we represent errors from the server as a variable with name === null.
I
isidor 已提交
46
	if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) {
E
Erich Gamma 已提交
47
		dom.addClass(container, 'unavailable');
I
isidor 已提交
48
		if (value !== Expression.DEFAULT_VALUE) {
49 50
			dom.addClass(container, 'error');
		}
E
Erich Gamma 已提交
51 52 53 54 55 56 57 58
	} 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');
	}

59
	if (showChanged && (<any>expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) {
60 61 62
		// value changed color has priority over other colors.
		container.className = 'value changed';
	}
I
isidor 已提交
63

64 65
	if (maxValueRenderLength && value.length > maxValueRenderLength) {
		value = value.substr(0, maxValueRenderLength) + '...';
I
isidor 已提交
66
	}
E
Erich Gamma 已提交
67 68 69 70
	container.textContent = value;
	container.title = value;
}

I
isidor 已提交
71
export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void {
72
	if (variable.available) {
73
		data.name.textContent = variable.name;
74
		data.name.title = variable.type ? variable.type : '';
75 76
	}

E
Erich Gamma 已提交
77
	if (variable.value) {
78
		data.name.textContent += ':';
79
		renderExpressionValue(variable, data.value, showChanged, MAX_VALUE_RENDER_LENGTH_IN_VIEWLET);
80
		data.value.title = variable.value;
E
Erich Gamma 已提交
81 82 83 84 85 86
	} else {
		data.value.textContent = '';
		data.value.title = '';
	}
}

I
isidor 已提交
87 88 89 90
interface IRenameBoxOptions {
	initialValue: string;
	ariaLabel: string;
	placeholder?: string;
91
	validationOptions?: IInputValidationOptions;
I
isidor 已提交
92 93
}

I
isidor 已提交
94
function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
95
	let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
96
	let inputBox = new InputBox(inputBoxContainer, contextViewService, {
I
isidor 已提交
97 98 99
		validationOptions: options.validationOptions,
		placeholder: options.placeholder,
		ariaLabel: options.ariaLabel
100 101
	});

I
isidor 已提交
102
	inputBox.value = options.initialValue ? options.initialValue : '';
103
	inputBox.focus();
104
	inputBox.select();
105

I
isidor 已提交
106 107
	let disposed = false;
	const toDispose: [lifecycle.IDisposable] = [inputBox];
108

J
Joao Moreno 已提交
109
	const wrapUp = async.once((renamed: boolean) => {
110 111
		if (!disposed) {
			disposed = true;
I
isidor 已提交
112
			if (element instanceof Expression && renamed && inputBox.value) {
113
				debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
I
isidor 已提交
114
			} else if (element instanceof Expression && !element.name) {
115
				debugService.removeWatchExpressions(element.getId());
I
isidor 已提交
116
			} else if (element instanceof FunctionBreakpoint && renamed && inputBox.value) {
117
				debugService.renameFunctionBreakpoint(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
I
isidor 已提交
118
			} else if (element instanceof FunctionBreakpoint && !element.name) {
119
				debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
I
isidor 已提交
120
			} else if (element instanceof Variable) {
121
				element.errorMessage = null;
122
				if (renamed && element.value !== inputBox.value) {
123
					element.setVariable(inputBox.value)
124 125 126
						// if everything went fine we need to refresh that tree element since his value updated
						.done(() => tree.refresh(element, false), errors.onUnexpectedError);
				}
127
			}
128

129 130
			tree.clearHighlight();
			tree.DOMFocus();
131
			tree.setFocus(element);
132

I
isidor 已提交
133
			// need to remove the input box since this template will be reused.
134
			container.removeChild(inputBoxContainer);
J
Joao Moreno 已提交
135
			lifecycle.dispose(toDispose);
136 137 138
		}
	});

A
Cleanup  
Alex Dima 已提交
139
	toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
A
Alexandru Dima 已提交
140 141
		const isEscape = e.equals(KeyCode.Escape);
		const isEnter = e.equals(KeyCode.Enter);
142 143 144 145 146 147 148 149 150
		if (isEscape || isEnter) {
			wrapUp(isEnter);
		}
	}));
	toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
		wrapUp(true);
	}));
}

151 152 153 154 155
function getSourceName(source: Source, contextService: IWorkspaceContextService): string {
	if (source.inMemory) {
		return source.name;
	}

I
isidor 已提交
156
	return getPathLabel(paths.basename(source.uri.fsPath), contextService);
157 158
}

I
isidor 已提交
159
export class BaseDebugController extends DefaultController {
E
Erich Gamma 已提交
160

I
isidor 已提交
161 162 163
	constructor(
		protected debugService: debug.IDebugService,
		private contextMenuService: IContextMenuService,
I
isidor 已提交
164
		private actionProvider: IActionProvider,
I
isidor 已提交
165 166
		private focusOnContextMenu = true
	) {
E
Erich Gamma 已提交
167 168 169
		super();

		if (isMacintosh) {
A
Alexandru Dima 已提交
170
			this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Backspace, this.onDelete.bind(this));
E
Erich Gamma 已提交
171
		} else {
A
Alexandru Dima 已提交
172 173
			this.downKeyBindingDispatcher.set(KeyCode.Delete, this.onDelete.bind(this));
			this.downKeyBindingDispatcher.set(KeyMod.Shift | KeyCode.Delete, this.onDelete.bind(this));
E
Erich Gamma 已提交
174 175 176
		}
	}

I
isidor 已提交
177
	public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean {
E
Erich Gamma 已提交
178 179 180 181 182 183 184 185 186 187 188 189
		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 已提交
190
			const anchor = { x: event.posx + 1, y: event.posy };
E
Erich Gamma 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
			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 已提交
208
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
209 210 211 212
		return false;
	}
}

I
isidor 已提交
213
// call stack
E
Erich Gamma 已提交
214

I
isidor 已提交
215
class ThreadAndProcessIds implements debug.ITreeElement {
I
isidor 已提交
216
	constructor(public processId: string, public threadId: number) { }
I
isidor 已提交
217 218 219 220

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

223 224
export class CallStackController extends BaseDebugController {

I
isidor 已提交
225
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
226
		if (element instanceof ThreadAndProcessIds) {
227 228
			return this.showMoreStackFrames(tree, element);
		}
I
isidor 已提交
229
		if (element instanceof StackFrame) {
230 231
			this.focusStackFrame(element, event, true);
		}
232 233 234 235

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

I
isidor 已提交
236
	protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
237
		const element = tree.getFocus();
I
isidor 已提交
238
		if (element instanceof ThreadAndProcessIds) {
239 240
			return this.showMoreStackFrames(tree, element);
		}
I
isidor 已提交
241
		if (element instanceof StackFrame) {
242 243
			this.focusStackFrame(element, event, false);
		}
244 245 246 247

		return super.onEnter(tree, event);
	}

I
isidor 已提交
248
	protected onUp(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
249 250 251 252 253 254
		super.onUp(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
255
	protected onPageUp(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
256 257 258 259 260 261
		super.onPageUp(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
262
	protected onDown(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
263 264 265 266 267 268
		super.onDown(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

I
isidor 已提交
269
	protected onPageDown(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
270 271 272 273 274 275
		super.onPageDown(tree, event);
		this.focusStackFrame(tree.getFocus(), event, true);

		return true;
	}

276
	// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
I
isidor 已提交
277
	private showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
I
isidor 已提交
278 279
		const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
		const thread = process && process.getThread(threadAndProcessIds.threadId);
280
		if (thread) {
281
			thread.getCallStack(true)
282 283 284 285 286
				.done(() => tree.refresh(), errors.onUnexpectedError);
		}

		return true;
	}
287

I
isidor 已提交
288
	private focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
289 290
		this.debugService.setFocusedStackFrameAndEvaluate(stackFrame).done(null, errors.onUnexpectedError);

I
isidor 已提交
291 292
		if (stackFrame) {
			const sideBySide = (event && (event.ctrlKey || event.metaKey));
293
			this.debugService.openOrRevealSource(stackFrame.source.uri, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
I
isidor 已提交
294
		}
295
	}
296 297 298
}


I
isidor 已提交
299
export class CallStackActionProvider implements IActionProvider {
I
isidor 已提交
300

J
Johannes Rieken 已提交
301
	constructor( @IInstantiationService private instantiationService: IInstantiationService, @debug.IDebugService private debugService: debug.IDebugService) {
I
isidor 已提交
302 303 304
		// noop
	}

I
isidor 已提交
305
	public hasActions(tree: ITree, element: any): boolean {
I
isidor 已提交
306 307 308
		return false;
	}

I
isidor 已提交
309
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
I
isidor 已提交
310 311 312
		return TPromise.as([]);
	}

I
isidor 已提交
313 314
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Thread || element instanceof StackFrame;
I
isidor 已提交
315 316
	}

I
isidor 已提交
317 318 319 320
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		const actions: IAction[] = [];
		if (element instanceof Thread) {
			const thread = <Thread>element;
A
Andre Weinand 已提交
321
			if (thread.stopped) {
I
isidor 已提交
322 323 324 325
				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 已提交
326
			} else {
I
isidor 已提交
327
				actions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL));
A
Andre Weinand 已提交
328
			}
I
isidor 已提交
329
		} else if (element instanceof StackFrame) {
I
isidor 已提交
330
			const capabilities = this.debugService.getViewModel().focusedProcess.session.configuration.capabilities;
I
isidor 已提交
331
			if (typeof capabilities.supportsRestartFrame === 'boolean' && capabilities.supportsRestartFrame) {
I
isidor 已提交
332
				actions.push(this.instantiationService.createInstance(RestartFrameAction, RestartFrameAction.ID, RestartFrameAction.LABEL));
333
			}
I
isidor 已提交
334 335 336 337 338
		}

		return TPromise.as(actions);
	}

I
isidor 已提交
339
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
340 341 342 343
		return null;
	}
}

I
isidor 已提交
344
export class CallStackDataSource implements IDataSource {
E
Erich Gamma 已提交
345

I
isidor 已提交
346
	public getId(tree: ITree, element: any): string {
347 348 349
		if (typeof element === 'string') {
			return element;
		}
I
isidor 已提交
350

E
Erich Gamma 已提交
351 352 353
		return element.getId();
	}

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

I
isidor 已提交
358 359
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Thread) {
I
isidor 已提交
360
			return this.getThreadChildren(element);
E
Erich Gamma 已提交
361
		}
I
isidor 已提交
362
		if (element instanceof Model) {
363
			return TPromise.as(element.getProcesses());
364 365
		}

366
		const process = <debug.IProcess>element;
I
isidor 已提交
367
		return TPromise.as(process.getAllThreads());
E
Erich Gamma 已提交
368 369
	}

I
isidor 已提交
370
	private getThreadChildren(thread: debug.IThread): TPromise<any> {
371
		return thread.getCallStack().then((callStack: any[]) => {
I
isidor 已提交
372
			if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) {
373 374
				return callStack.concat([thread.stoppedDetails.framesErrorMessage]);
			}
I
isidor 已提交
375
			if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length) {
I
isidor 已提交
376
				return callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]);
I
isidor 已提交
377 378 379 380 381 382
			}

			return callStack;
		});
	}

I
isidor 已提交
383
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
384
		return TPromise.as(null);
E
Erich Gamma 已提交
385 386 387 388
	}
}

interface IThreadTemplateData {
I
isidor 已提交
389
	thread: HTMLElement;
E
Erich Gamma 已提交
390
	name: HTMLElement;
I
isidor 已提交
391 392
	state: HTMLElement;
	stateLabel: HTMLSpanElement;
E
Erich Gamma 已提交
393 394
}

I
isidor 已提交
395 396 397 398 399
interface IProcessTemplateData {
	process: HTMLElement;
	name: HTMLElement;
}

400 401 402 403
interface IErrorTemplateData {
	label: HTMLElement;
}

I
isidor 已提交
404 405 406 407
interface ILoadMoreTemplateData {
	label: HTMLElement;
}

E
Erich Gamma 已提交
408 409
interface IStackFrameTemplateData {
	stackFrame: HTMLElement;
410 411 412 413
	label: HTMLElement;
	file: HTMLElement;
	fileName: HTMLElement;
	lineNumber: HTMLElement;
E
Erich Gamma 已提交
414 415
}

I
isidor 已提交
416
export class CallStackRenderer implements IRenderer {
E
Erich Gamma 已提交
417 418 419

	private static THREAD_TEMPLATE_ID = 'thread';
	private static STACK_FRAME_TEMPLATE_ID = 'stackFrame';
420
	private static ERROR_TEMPLATE_ID = 'error';
I
isidor 已提交
421
	private static LOAD_MORE_TEMPLATE_ID = 'loadMore';
I
isidor 已提交
422
	private static PROCESS_TEMPLATE_ID = 'process';
E
Erich Gamma 已提交
423

J
Johannes Rieken 已提交
424
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
E
Erich Gamma 已提交
425 426 427
		// noop
	}

I
isidor 已提交
428
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
429
		return 22;
E
Erich Gamma 已提交
430 431
	}

I
isidor 已提交
432 433
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Process) {
I
isidor 已提交
434 435
			return CallStackRenderer.PROCESS_TEMPLATE_ID;
		}
I
isidor 已提交
436
		if (element instanceof Thread) {
E
Erich Gamma 已提交
437 438
			return CallStackRenderer.THREAD_TEMPLATE_ID;
		}
I
isidor 已提交
439
		if (element instanceof StackFrame) {
E
Erich Gamma 已提交
440 441
			return CallStackRenderer.STACK_FRAME_TEMPLATE_ID;
		}
442 443 444
		if (typeof element === 'string') {
			return CallStackRenderer.ERROR_TEMPLATE_ID;
		}
E
Erich Gamma 已提交
445

I
isidor 已提交
446
		return CallStackRenderer.LOAD_MORE_TEMPLATE_ID;
E
Erich Gamma 已提交
447 448
	}

I
isidor 已提交
449
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
450 451 452 453 454 455 456 457
		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'));

			return data;
		}

I
isidor 已提交
458 459 460 461 462 463
		if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.load-more'));

			return data;
		}
464 465 466 467 468 469
		if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.error'));

			return data;
		}
E
Erich Gamma 已提交
470 471
		if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
			let data: IThreadTemplateData = Object.create(null);
I
isidor 已提交
472 473 474 475
			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 已提交
476 477 478 479 480 481

			return data;
		}

		let data: IStackFrameTemplateData = Object.create(null);
		data.stackFrame = dom.append(container, $('.stack-frame'));
482
		data.label = dom.append(data.stackFrame, $('span.label.expression'));
E
Erich Gamma 已提交
483 484 485 486 487 488 489
		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 已提交
490
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
491
		if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) {
I
isidor 已提交
492 493
			this.renderProcess(element, templateData);
		} else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
E
Erich Gamma 已提交
494
			this.renderThread(element, templateData);
I
isidor 已提交
495
		} else if (templateId === CallStackRenderer.STACK_FRAME_TEMPLATE_ID) {
E
Erich Gamma 已提交
496
			this.renderStackFrame(element, templateData);
497 498
		} else if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
			this.renderError(element, templateData);
I
isidor 已提交
499
		} else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
I
isidor 已提交
500
			this.renderLoadMore(element, templateData);
E
Erich Gamma 已提交
501 502 503
		}
	}

I
isidor 已提交
504
	private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void {
I
isidor 已提交
505
		data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process");
I
isidor 已提交
506 507 508
		data.name.textContent = process.name;
	}

E
Erich Gamma 已提交
509
	private renderThread(thread: debug.IThread, data: IThreadTemplateData): void {
I
isidor 已提交
510
		data.thread.title = nls.localize('thread', "Thread");
E
Erich Gamma 已提交
511
		data.name.textContent = thread.name;
I
isidor 已提交
512 513
		data.stateLabel.textContent = thread.stopped ? nls.localize('paused', "paused")
			: nls.localize({ key: 'running', comment: ['indicates state'] }, "running");
E
Erich Gamma 已提交
514 515
	}

516 517
	private renderError(element: string, data: IErrorTemplateData) {
		data.label.textContent = element;
I
isidor 已提交
518
		data.label.title = element;
519 520
	}

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

E
Erich Gamma 已提交
525 526 527 528
	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;
529
		data.label.title = stackFrame.name;
530
		data.fileName.textContent = getSourceName(stackFrame.source, this.contextService);
I
isidor 已提交
531
		if (stackFrame.lineNumber !== undefined) {
532
			data.lineNumber.textContent = `${stackFrame.lineNumber}`;
I
isidor 已提交
533 534 535 536
			dom.removeClass(data.lineNumber, 'unavailable');
		} else {
			dom.addClass(data.lineNumber, 'unavailable');
		}
E
Erich Gamma 已提交
537 538
	}

I
isidor 已提交
539
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
540 541 542 543
		// noop
	}
}

I
isidor 已提交
544
export class CallstackAccessibilityProvider implements IAccessibilityProvider {
545

J
Johannes Rieken 已提交
546
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
547 548 549
		// noop
	}

I
isidor 已提交
550 551 552
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Thread) {
			return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (<Thread>element).name);
553
		}
I
isidor 已提交
554 555
		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));
556 557 558 559 560 561
		}

		return null;
	}
}

I
isidor 已提交
562
// variables
E
Erich Gamma 已提交
563

I
isidor 已提交
564
export class VariablesActionProvider implements IActionProvider {
E
Erich Gamma 已提交
565

I
isidor 已提交
566 567
	constructor(private instantiationService: IInstantiationService) {
		// noop
E
Erich Gamma 已提交
568 569
	}

I
isidor 已提交
570
	public hasActions(tree: ITree, element: any): boolean {
E
Erich Gamma 已提交
571 572 573
		return false;
	}

I
isidor 已提交
574
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
A
Alex Dima 已提交
575
		return TPromise.as([]);
I
isidor 已提交
576 577
	}

I
isidor 已提交
578 579
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Variable;
E
Erich Gamma 已提交
580 581
	}

I
isidor 已提交
582 583 584
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		let actions: IAction[] = [];
		const variable = <Variable>element;
585
		if (!variable.hasChildren) {
I
isidor 已提交
586
			actions.push(this.instantiationService.createInstance(SetValueAction, SetValueAction.ID, SetValueAction.LABEL, variable));
587
			actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable));
I
isidor 已提交
588
			actions.push(new Separator());
E
Erich Gamma 已提交
589 590
		}

I
isidor 已提交
591
		actions.push(this.instantiationService.createInstance(AddToWatchExpressionsAction, AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable));
A
Alex Dima 已提交
592
		return TPromise.as(actions);
E
Erich Gamma 已提交
593
	}
I
isidor 已提交
594

I
isidor 已提交
595
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
596 597
		return null;
	}
E
Erich Gamma 已提交
598 599
}

I
isidor 已提交
600
export class VariablesDataSource implements IDataSource {
E
Erich Gamma 已提交
601

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

I
isidor 已提交
606 607
	public hasChildren(tree: ITree, element: any): boolean {
		if (element instanceof ViewModel || element instanceof Scope) {
E
Erich Gamma 已提交
608 609 610
			return true;
		}

I
isidor 已提交
611
		let variable = <Variable>element;
612
		return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null');
E
Erich Gamma 已提交
613 614
	}

I
isidor 已提交
615 616 617
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof ViewModel) {
			const focusedStackFrame = (<ViewModel>element).focusedStackFrame;
618
			return focusedStackFrame ? focusedStackFrame.getScopes() : TPromise.as([]);
E
Erich Gamma 已提交
619 620
		}

I
isidor 已提交
621
		let scope = <Scope>element;
622
		return scope.getChildren();
E
Erich Gamma 已提交
623 624
	}

I
isidor 已提交
625
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
626
		return TPromise.as(null);
E
Erich Gamma 已提交
627 628 629 630 631 632 633 634 635 636 637 638 639
	}
}

interface IScopeTemplateData {
	name: HTMLElement;
}

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

I
isidor 已提交
640
export class VariablesRenderer implements IRenderer {
E
Erich Gamma 已提交
641 642 643 644

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

I
isidor 已提交
645 646 647 648 649 650 651
	constructor(
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
	) {
		// noop
	}

I
isidor 已提交
652
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
653
		return 22;
E
Erich Gamma 已提交
654 655
	}

I
isidor 已提交
656 657
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Scope) {
E
Erich Gamma 已提交
658 659
			return VariablesRenderer.SCOPE_TEMPLATE_ID;
		}
I
isidor 已提交
660
		if (element instanceof Variable) {
E
Erich Gamma 已提交
661 662 663 664 665 666
			return VariablesRenderer.VARIABLE_TEMPLATE_ID;
		}

		return null;
	}

I
isidor 已提交
667
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
E
Erich Gamma 已提交
668 669 670 671 672 673 674 675
		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);
676
		data.expression = dom.append(container, $('.expression'));
E
Erich Gamma 已提交
677 678 679 680 681 682
		data.name = dom.append(data.expression, $('span.name'));
		data.value = dom.append(data.expression, $('span.value'));

		return data;
	}

I
isidor 已提交
683
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
684 685 686
		if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
			this.renderScope(element, templateData);
		} else {
I
isidor 已提交
687
			const variable = <Variable>element;
688 689 690 691 692 693 694
			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 已提交
695
				});
I
isidor 已提交
696
			} else {
697
				renderVariable(tree, variable, templateData, true);
I
isidor 已提交
698
			}
E
Erich Gamma 已提交
699 700 701
		}
	}

I
isidor 已提交
702
	private renderScope(scope: Scope, data: IScopeTemplateData): void {
E
Erich Gamma 已提交
703 704 705
		data.name.textContent = scope.name;
	}

I
isidor 已提交
706
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
707 708 709 710
		// noop
	}
}

I
isidor 已提交
711
export class VariablesAccessibilityProvider implements IAccessibilityProvider {
712

I
isidor 已提交
713 714 715
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Scope) {
			return nls.localize('variableScopeAriaLabel', "Scope {0}, variables, debug", (<Scope>element).name);
716
		}
I
isidor 已提交
717 718
		if (element instanceof Variable) {
			return nls.localize('variableAriaLabel', "{0} value {1}, variables, debug", (<Variable>element).name, (<Variable>element).value);
719 720 721 722 723 724
		}

		return null;
	}
}

725 726
export class VariablesController extends BaseDebugController {

I
isidor 已提交
727
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
728
		super(debugService, contextMenuService, actionProvider);
A
Alexandru Dima 已提交
729
		this.downKeyBindingDispatcher.set(KeyCode.Enter, this.setSelectedExpression.bind(this));
730 731
	}

I
isidor 已提交
732
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
733
		// double click on primitive value: open input box to be able to set the value
I
isidor 已提交
734
		if (element instanceof Variable && event.detail === 2) {
735
			const expression = <debug.IExpression>element;
736
			if (!expression.hasChildren) {
737 738 739 740 741 742 743
				this.debugService.getViewModel().setSelectedExpression(expression);
			}
			return true;
		}

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

I
isidor 已提交
745
	protected setSelectedExpression(tree: ITree, event: KeyboardEvent): boolean {
I
isidor 已提交
746
		const element = tree.getFocus();
747
		if (element instanceof Variable && !element.hasChildren) {
I
isidor 已提交
748
			this.debugService.getViewModel().setSelectedExpression(element);
749
			return true;
I
isidor 已提交
750 751
		}

752
		return false;
I
isidor 已提交
753
	}
754 755
}

I
isidor 已提交
756
// watch expressions
E
Erich Gamma 已提交
757

I
isidor 已提交
758
export class WatchExpressionsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
759 760 761 762 763 764 765

	private instantiationService: IInstantiationService;

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

I
isidor 已提交
766 767
	public hasActions(tree: ITree, element: any): boolean {
		return element instanceof Expression && !!element.name;
E
Erich Gamma 已提交
768 769
	}

I
isidor 已提交
770
	public hasSecondaryActions(tree: ITree, element: any): boolean {
E
Erich Gamma 已提交
771 772 773
		return true;
	}

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

I
isidor 已提交
778 779
	public getExpressionActions(): IAction[] {
		return [this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL)];
E
Erich Gamma 已提交
780 781
	}

I
isidor 已提交
782 783 784 785 786 787
	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));
788
			if (!expression.hasChildren) {
789
				actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value));
E
Erich Gamma 已提交
790
			}
I
isidor 已提交
791
			actions.push(new Separator());
E
Erich Gamma 已提交
792

I
isidor 已提交
793 794
			actions.push(this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL));
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
795
		} else {
I
isidor 已提交
796 797 798
			actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL));
			if (element instanceof Variable) {
				const variable = <Variable>element;
799
				if (!variable.hasChildren) {
800
					actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable.value));
E
Erich Gamma 已提交
801
				}
I
isidor 已提交
802
				actions.push(new Separator());
E
Erich Gamma 已提交
803
			}
I
isidor 已提交
804
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
805 806
		}

A
Alex Dima 已提交
807
		return TPromise.as(actions);
E
Erich Gamma 已提交
808
	}
I
isidor 已提交
809

I
isidor 已提交
810
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
811 812
		return null;
	}
E
Erich Gamma 已提交
813 814
}

I
isidor 已提交
815
export class WatchExpressionsDataSource implements IDataSource {
E
Erich Gamma 已提交
816

I
isidor 已提交
817
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
818 819 820
		return element.getId();
	}

I
isidor 已提交
821 822
	public hasChildren(tree: ITree, element: any): boolean {
		if (element instanceof Model) {
E
Erich Gamma 已提交
823 824 825
			return true;
		}

I
isidor 已提交
826
		const watchExpression = <Expression>element;
827
		return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null');
E
Erich Gamma 已提交
828 829
	}

I
isidor 已提交
830 831 832
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Model) {
			return TPromise.as((<Model>element).getWatchExpressions());
E
Erich Gamma 已提交
833 834
		}

I
isidor 已提交
835
		let expression = <Expression>element;
836
		return expression.getChildren();
E
Erich Gamma 已提交
837 838
	}

I
isidor 已提交
839
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
840
		return TPromise.as(null);
E
Erich Gamma 已提交
841 842 843 844
	}
}

interface IWatchExpressionTemplateData extends IVariableTemplateData {
I
isidor 已提交
845
	actionBar: ActionBar;
E
Erich Gamma 已提交
846 847
}

I
isidor 已提交
848
export class WatchExpressionsRenderer implements IRenderer {
E
Erich Gamma 已提交
849 850 851 852 853 854

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

I
isidor 已提交
855
	constructor(
I
isidor 已提交
856 857
		actionProvider: IActionProvider,
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
858 859 860 861
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
	) {
		this.toDispose = [];
862
		this.actionProvider = <WatchExpressionsActionProvider>actionProvider;
E
Erich Gamma 已提交
863 864
	}

I
isidor 已提交
865
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
866
		return 22;
E
Erich Gamma 已提交
867 868
	}

I
isidor 已提交
869 870
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Expression) {
E
Erich Gamma 已提交
871 872 873 874 875 876
			return WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID;
		}

		return WatchExpressionsRenderer.VARIABLE_TEMPLATE_ID;
	}

I
isidor 已提交
877
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
E
Erich Gamma 已提交
878 879
		let data: IWatchExpressionTemplateData = Object.create(null);
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
I
isidor 已提交
880
			data.actionBar = new ActionBar(container, { actionRunner: this.actionRunner });
881
			data.actionBar.push(this.actionProvider.getExpressionActions(), { icon: true, label: false });
E
Erich Gamma 已提交
882 883
		}

884
		data.expression = dom.append(container, $('.expression'));
E
Erich Gamma 已提交
885 886 887 888 889 890
		data.name = dom.append(data.expression, $('span.name'));
		data.value = dom.append(data.expression, $('span.value'));

		return data;
	}

I
isidor 已提交
891
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
892 893 894
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
			this.renderWatchExpression(tree, element, templateData);
		} else {
895
			renderVariable(tree, element, templateData, true);
E
Erich Gamma 已提交
896 897 898
		}
	}

I
isidor 已提交
899
	private renderWatchExpression(tree: ITree, watchExpression: debug.IExpression, data: IWatchExpressionTemplateData): void {
E
Erich Gamma 已提交
900
		let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
I
isidor 已提交
901
		if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
I
isidor 已提交
902 903 904 905 906
			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 已提交
907 908 909
		}
		data.actionBar.context = watchExpression;

910
		data.name.textContent = watchExpression.name;
911
		if (watchExpression.value) {
912
			data.name.textContent += ':';
913
			renderExpressionValue(watchExpression, data.value, true, MAX_VALUE_RENDER_LENGTH_IN_VIEWLET);
914
			data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value;
E
Erich Gamma 已提交
915 916 917
		}
	}

I
isidor 已提交
918
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
919 920 921
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
			(<IWatchExpressionTemplateData>templateData).actionBar.dispose();
		}
E
Erich Gamma 已提交
922 923 924
	}

	public dispose(): void {
J
Joao Moreno 已提交
925
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
926 927 928
	}
}

I
isidor 已提交
929
export class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider {
930

I
isidor 已提交
931 932 933
	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);
934
		}
I
isidor 已提交
935 936
		if (element instanceof Variable) {
			return nls.localize('watchVariableAriaLabel', "{0} value {1}, watch, debug", (<Variable>element).name, (<Variable>element).value);
937 938 939 940 941 942
		}

		return null;
	}
}

E
Erich Gamma 已提交
943 944
export class WatchExpressionsController extends BaseDebugController {

I
isidor 已提交
945
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
E
Erich Gamma 已提交
946 947 948
		super(debugService, contextMenuService, actionProvider);

		if (isMacintosh) {
A
Alexandru Dima 已提交
949
			this.downKeyBindingDispatcher.set(KeyCode.Enter, this.onRename.bind(this));
E
Erich Gamma 已提交
950
		} else {
A
Alexandru Dima 已提交
951
			this.downKeyBindingDispatcher.set(KeyCode.F2, this.onRename.bind(this));
E
Erich Gamma 已提交
952 953 954
		}
	}

I
isidor 已提交
955
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
956
		// double click on primitive value: open input box to be able to select and copy value.
I
isidor 已提交
957
		if (element instanceof Expression && event.detail === 2) {
958
			const expression = <debug.IExpression>element;
959
			if (!expression.hasChildren) {
E
Erich Gamma 已提交
960 961 962 963 964 965 966 967
				this.debugService.getViewModel().setSelectedExpression(expression);
			}
			return true;
		}

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

I
isidor 已提交
968
	protected onRename(tree: ITree, event: KeyboardEvent): boolean {
I
isidor 已提交
969
		const element = tree.getFocus();
I
isidor 已提交
970 971
		if (element instanceof Expression) {
			const watchExpression = <Expression>element;
972
			if (!watchExpression.hasChildren) {
E
Erich Gamma 已提交
973 974 975 976 977 978 979 980
				this.debugService.getViewModel().setSelectedExpression(watchExpression);
			}
			return true;
		}

		return false;
	}

I
isidor 已提交
981
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
I
isidor 已提交
982
		const element = tree.getFocus();
I
isidor 已提交
983 984
		if (element instanceof Expression) {
			const we = <Expression>element;
985
			this.debugService.removeWatchExpressions(we.getId());
E
Erich Gamma 已提交
986 987 988 989 990 991 992 993

			return true;
		}

		return false;
	}
}

I
isidor 已提交
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
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 已提交
1031
// breakpoints
E
Erich Gamma 已提交
1032

I
isidor 已提交
1033
export class BreakpointsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
1034 1035

	constructor(private instantiationService: IInstantiationService) {
I
isidor 已提交
1036
		// noop
E
Erich Gamma 已提交
1037 1038
	}

I
isidor 已提交
1039 1040
	public hasActions(tree: ITree, element: any): boolean {
		return element instanceof Breakpoint;
E
Erich Gamma 已提交
1041 1042
	}

I
isidor 已提交
1043 1044
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Breakpoint || element instanceof ExceptionBreakpoint || element instanceof FunctionBreakpoint;
E
Erich Gamma 已提交
1045 1046
	}

I
isidor 已提交
1047 1048
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
		if (element instanceof Breakpoint) {
A
Alex Dima 已提交
1049
			return TPromise.as(this.getBreakpointActions());
E
Erich Gamma 已提交
1050 1051
		}

A
Alex Dima 已提交
1052
		return TPromise.as([]);
E
Erich Gamma 已提交
1053 1054
	}

I
isidor 已提交
1055 1056
	public getBreakpointActions(): IAction[] {
		return [this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)];
E
Erich Gamma 已提交
1057 1058
	}

I
isidor 已提交
1059 1060 1061
	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 已提交
1062

I
isidor 已提交
1063 1064
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
			actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
1065
		}
I
isidor 已提交
1066 1067
		actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL));
		actions.push(new Separator());
E
Erich Gamma 已提交
1068

I
isidor 已提交
1069 1070
		actions.push(this.instantiationService.createInstance(ToggleBreakpointsActivatedAction, ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL));
		actions.push(new Separator());
E
Erich Gamma 已提交
1071

I
isidor 已提交
1072 1073 1074
		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 已提交
1075

I
isidor 已提交
1076 1077 1078
		actions.push(this.instantiationService.createInstance(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL));
		if (element instanceof FunctionBreakpoint) {
			actions.push(this.instantiationService.createInstance(RenameFunctionBreakpointAction, RenameFunctionBreakpointAction.ID, RenameFunctionBreakpointAction.LABEL));
1079
		}
I
isidor 已提交
1080
		actions.push(new Separator());
I
isidor 已提交
1081

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

A
Alex Dima 已提交
1084
		return TPromise.as(actions);
E
Erich Gamma 已提交
1085
	}
I
isidor 已提交
1086

I
isidor 已提交
1087
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
1088 1089
		return null;
	}
E
Erich Gamma 已提交
1090 1091
}

I
isidor 已提交
1092
export class BreakpointsDataSource implements IDataSource {
E
Erich Gamma 已提交
1093

I
isidor 已提交
1094
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
1095 1096 1097
		return element.getId();
	}

I
isidor 已提交
1098 1099
	public hasChildren(tree: ITree, element: any): boolean {
		return element instanceof Model;
E
Erich Gamma 已提交
1100 1101
	}

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

A
Alex Dima 已提交
1106
		return TPromise.as(exBreakpoints.concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints()));
E
Erich Gamma 已提交
1107 1108
	}

I
isidor 已提交
1109
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
1110
		return TPromise.as(null);
E
Erich Gamma 已提交
1111 1112 1113 1114
	}
}

interface IExceptionBreakpointTemplateData {
1115
	breakpoint: HTMLElement;
E
Erich Gamma 已提交
1116 1117 1118 1119 1120 1121
	name: HTMLElement;
	checkbox: HTMLInputElement;
	toDisposeBeforeRender: lifecycle.IDisposable[];
}

interface IBreakpointTemplateData extends IExceptionBreakpointTemplateData {
I
isidor 已提交
1122
	actionBar: ActionBar;
E
Erich Gamma 已提交
1123 1124 1125 1126
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

1127
interface IFunctionBreakpointTemplateData extends IExceptionBreakpointTemplateData {
I
isidor 已提交
1128
	actionBar: ActionBar;
1129 1130
}

I
isidor 已提交
1131
export class BreakpointsRenderer implements IRenderer {
E
Erich Gamma 已提交
1132 1133

	private static EXCEPTION_BREAKPOINT_TEMPLATE_ID = 'exceptionBreakpoint';
1134
	private static FUNCTION_BREAKPOINT_TEMPLATE_ID = 'functionBreakpoint';
E
Erich Gamma 已提交
1135 1136 1137 1138
	private static BREAKPOINT_TEMPLATE_ID = 'breakpoint';

	constructor(
		private actionProvider: BreakpointsActionProvider,
I
isidor 已提交
1139
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
1140
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
1141 1142
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
E
Erich Gamma 已提交
1143 1144 1145 1146
	) {
		// noop
	}

I
isidor 已提交
1147
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
1148
		return 22;
E
Erich Gamma 已提交
1149 1150
	}

I
isidor 已提交
1151 1152
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
E
Erich Gamma 已提交
1153 1154
			return BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1155
		if (element instanceof FunctionBreakpoint) {
1156 1157
			return BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1158
		if (element instanceof ExceptionBreakpoint) {
E
Erich Gamma 已提交
1159 1160 1161 1162 1163 1164
			return BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID;
		}

		return null;
	}

I
isidor 已提交
1165
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
1166
		const data: IBreakpointTemplateData = Object.create(null);
1167
		if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID || templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) {
I
isidor 已提交
1168
			data.actionBar = new ActionBar(container, { actionRunner: this.actionRunner });
E
Erich Gamma 已提交
1169 1170 1171
			data.actionBar.push(this.actionProvider.getBreakpointActions(), { icon: true, label: false });
		}

1172
		data.breakpoint = dom.append(container, $('.breakpoint'));
E
Erich Gamma 已提交
1173 1174
		data.toDisposeBeforeRender = [];

1175
		data.checkbox = <HTMLInputElement>$('input');
E
Erich Gamma 已提交
1176
		data.checkbox.type = 'checkbox';
1177

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

1180
		data.name = dom.append(data.breakpoint, $('span.name'));
E
Erich Gamma 已提交
1181 1182

		if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID) {
1183 1184
			data.lineNumber = dom.append(data.breakpoint, $('span.line-number'));
			data.filePath = dom.append(data.breakpoint, $('span.file-path'));
E
Erich Gamma 已提交
1185 1186 1187 1188 1189
		}

		return data;
	}

I
isidor 已提交
1190
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
J
Joao Moreno 已提交
1191
		templateData.toDisposeBeforeRender = lifecycle.dispose(templateData.toDisposeBeforeRender);
1192
		templateData.toDisposeBeforeRender.push(dom.addStandardDisposableListener(templateData.checkbox, 'change', (e) => {
1193
			this.debugService.enableOrDisableBreakpoints(!element.enabled, element);
1194 1195
		}));

E
Erich Gamma 已提交
1196 1197
		if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderExceptionBreakpoint(element, templateData);
1198 1199
		} else if (templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderFunctionBreakpoint(tree, element, templateData);
E
Erich Gamma 已提交
1200 1201 1202 1203 1204 1205
		} else {
			this.renderBreakpoint(tree, element, templateData);
		}
	}

	private renderExceptionBreakpoint(exceptionBreakpoint: debug.IExceptionBreakpoint, data: IExceptionBreakpointTemplateData): void {
1206
		data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;;
1207
		data.breakpoint.title = data.name.textContent;
E
Erich Gamma 已提交
1208
		data.checkbox.checked = exceptionBreakpoint.enabled;
1209
	}
E
Erich Gamma 已提交
1210

I
isidor 已提交
1211
	private renderFunctionBreakpoint(tree: ITree, functionBreakpoint: debug.IFunctionBreakpoint, data: IFunctionBreakpointTemplateData): void {
I
isidor 已提交
1212 1213
		const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
		if (!functionBreakpoint.name || (selected && selected.getId() === functionBreakpoint.getId())) {
I
isidor 已提交
1214 1215 1216 1217 1218
			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")
			});
1219 1220 1221
		} else {
			data.name.textContent = functionBreakpoint.name;
			data.checkbox.checked = functionBreakpoint.enabled;
1222 1223 1224
			data.breakpoint.title = functionBreakpoint.name;

			// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
1225
			const process = this.debugService.getViewModel().focusedProcess;
I
isidor 已提交
1226
			if ((process && !process.session.configuration.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()) {
1227
				tree.addTraits('disabled', [functionBreakpoint]);
I
isidor 已提交
1228
				if (process && !process.session.configuration.capabilities.supportsFunctionBreakpoints) {
1229 1230 1231 1232 1233
					data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
				}
			} else {
				tree.removeTraits('disabled', [functionBreakpoint]);
			}
1234
		}
1235
		data.actionBar.context = functionBreakpoint;
E
Erich Gamma 已提交
1236 1237
	}

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

1241
		data.name.textContent = getPathLabel(paths.basename(breakpoint.uri.fsPath), this.contextService);
E
Erich Gamma 已提交
1242
		data.lineNumber.textContent = breakpoint.desiredLineNumber !== breakpoint.lineNumber ? breakpoint.desiredLineNumber + ' \u2192 ' + breakpoint.lineNumber : '' + breakpoint.lineNumber;
1243
		data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService);
E
Erich Gamma 已提交
1244 1245
		data.checkbox.checked = breakpoint.enabled;
		data.actionBar.context = breakpoint;
I
isidor 已提交
1246

I
isidor 已提交
1247
		const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing;
I
isidor 已提交
1248 1249 1250 1251 1252
		if (debugActive && !breakpoint.verified) {
			tree.addTraits('disabled', [breakpoint]);
			if (breakpoint.message) {
				data.breakpoint.title = breakpoint.message;
			}
1253 1254
		} else if (breakpoint.condition || breakpoint.hitCondition) {
			data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
1255
		}
E
Erich Gamma 已提交
1256 1257
	}

I
isidor 已提交
1258
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
1259
		if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID || templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) {
E
Erich Gamma 已提交
1260 1261 1262 1263 1264
			templateData.actionBar.dispose();
		}
	}
}

I
isidor 已提交
1265
export class BreakpointsAccessibilityProvider implements IAccessibilityProvider {
1266

J
Johannes Rieken 已提交
1267
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
1268 1269 1270
		// noop
	}

I
isidor 已提交
1271 1272
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
1273
			return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (<Breakpoint>element).lineNumber, getPathLabel(paths.basename((<Breakpoint>element).uri.fsPath), this.contextService), this.contextService);
1274
		}
I
isidor 已提交
1275 1276
		if (element instanceof FunctionBreakpoint) {
			return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (<FunctionBreakpoint>element).name);
1277
		}
I
isidor 已提交
1278 1279
		if (element instanceof ExceptionBreakpoint) {
			return nls.localize('exceptionBreakpointAriaLabel', "Exception breakpoint {0}, breakpoints, debug", (<ExceptionBreakpoint>element).filter);
1280 1281 1282 1283 1284 1285
		}

		return null;
	}
}

E
Erich Gamma 已提交
1286 1287
export class BreakpointsController extends BaseDebugController {

I
isidor 已提交
1288
	constructor(debugService: debug.IDebugService, contextMenuService: IContextMenuService, actionProvider: IActionProvider) {
I
isidor 已提交
1289 1290
		super(debugService, contextMenuService, actionProvider);
		if (isMacintosh) {
A
Alexandru Dima 已提交
1291
			this.downKeyBindingDispatcher.set(KeyCode.Enter, this.onRename.bind(this));
I
isidor 已提交
1292
		} else {
A
Alexandru Dima 已提交
1293
			this.downKeyBindingDispatcher.set(KeyCode.F2, this.onRename.bind(this));
I
isidor 已提交
1294 1295 1296
		}
	}

I
isidor 已提交
1297 1298
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
		if (element instanceof FunctionBreakpoint && event.detail === 2) {
I
isidor 已提交
1299 1300 1301
			this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
			return true;
		}
I
isidor 已提交
1302
		if (element instanceof Breakpoint) {
1303 1304
			this.openBreakpointSource(element, event, true);
		}
I
isidor 已提交
1305 1306

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

I
isidor 已提交
1309
	protected onRename(tree: ITree, event: IKeyboardEvent): boolean {
1310
		const element = tree.getFocus();
I
isidor 已提交
1311
		if (element instanceof FunctionBreakpoint && element.name) {
1312 1313 1314
			this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
			return true;
		}
I
isidor 已提交
1315
		if (element instanceof Breakpoint) {
1316 1317 1318 1319 1320 1321
			this.openBreakpointSource(element, event, false);
		}

		return super.onEnter(tree, event);
	}

I
isidor 已提交
1322
	protected onSpace(tree: ITree, event: IKeyboardEvent): boolean {
1323 1324
		super.onSpace(tree, event);
		const element = <debug.IEnablement>tree.getFocus();
1325
		this.debugService.enableOrDisableBreakpoints(!element.enabled, element).done(null, errors.onUnexpectedError);
1326 1327 1328 1329

		return true;
	}

I
isidor 已提交
1330
	protected onDelete(tree: ITree, event: IKeyboardEvent): boolean {
1331
		const element = tree.getFocus();
I
isidor 已提交
1332 1333
		if (element instanceof Breakpoint) {
			this.debugService.removeBreakpoints((<Breakpoint>element).getId()).done(null, errors.onUnexpectedError);
1334
			return true;
I
isidor 已提交
1335 1336
		} else if (element instanceof FunctionBreakpoint) {
			const fbp = <FunctionBreakpoint>element;
1337
			this.debugService.removeFunctionBreakpoints(fbp.getId()).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
1338 1339 1340 1341 1342 1343

			return true;
		}

		return false;
	}
1344

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