debugViewer.ts 45.1 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';
9
import { KeyCode } from 'vs/base/common/keyCodes';
I
isidor 已提交
10 11 12
import * as paths from 'vs/base/common/paths';
import * as errors from 'vs/base/common/errors';
import { equalsIgnoreCase } from 'vs/base/common/strings';
13
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
I
isidor 已提交
14
import * as dom from 'vs/base/browser/dom';
I
isidor 已提交
15
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
I
isidor 已提交
16 17
import { getPathLabel } from 'vs/base/common/labels';
import { IAction, IActionRunner } from 'vs/base/common/actions';
I
isidor 已提交
18
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
19
import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, DRAG_OVER_REJECT, IDragAndDropData, IDragOverReaction } from 'vs/base/parts/tree/browser/tree';
J
Johannes Rieken 已提交
20
import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox';
21
import { DefaultController, DefaultDragAndDrop, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
I
isidor 已提交
22
import { IActionProvider } from 'vs/base/parts/tree/browser/actionsRenderer';
23 24 25 26
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 { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
27 28
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
29
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
I
isidor 已提交
30
import * as debug from 'vs/workbench/parts/debug/common/debug';
31
import { Expression, Variable, FunctionBreakpoint, StackFrame, Thread, Process, Breakpoint, ExceptionBreakpoint, Model, Scope, ThreadAndProcessIds } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
32
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
33
import { ContinueAction, StepOverAction, PauseAction, ReapplyBreakpointsAction, DisableAllBreakpointsAction, RemoveBreakpointAction, RemoveWatchExpressionAction, AddWatchExpressionAction, RemoveAllBreakpointsAction, EnableAllBreakpointsAction, StepOutAction, StepIntoAction, SetValueAction, RemoveAllWatchExpressionsAction, RestartFrameAction, AddToWatchExpressionsAction, StopAction, RestartAction } from 'vs/workbench/parts/debug/browser/debugActions';
I
isidor 已提交
34
import { CopyValueAction, CopyStackTraceAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
J
Johannes Rieken 已提交
35
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
J
Joao Moreno 已提交
36
import { once } from 'vs/base/common/functional';
E
Erich Gamma 已提交
37

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

43
export interface IRenderValueOptions {
I
isidor 已提交
44 45
	preserveWhitespace?: boolean;
	showChanged?: boolean;
46
	maxValueLength?: number;
I
isidor 已提交
47
	showHover?: boolean;
48 49
}

I
isidor 已提交
50 51 52 53 54
function replaceWhitespace(value: string): string {
	const map = { '\n': '\\n', '\r': '\\r', '\t': '\\t' };
	return value.replace(/[\n\r\t]/g, char => map[char]);
}

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

I
isidor 已提交
58
	// remove stale classes
E
Erich Gamma 已提交
59
	container.className = 'value';
I
isidor 已提交
60
	// when resolving expressions we represent errors from the server as a variable with name === null.
I
isidor 已提交
61
	if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) {
E
Erich Gamma 已提交
62
		dom.addClass(container, 'unavailable');
I
isidor 已提交
63
		if (value !== Expression.DEFAULT_VALUE) {
64 65
			dom.addClass(container, 'error');
		}
E
Erich Gamma 已提交
66 67 68 69 70 71 72 73
	} 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');
	}

74
	if (options.showChanged && (<any>expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) {
75 76 77
		// value changed color has priority over other colors.
		container.className = 'value changed';
	}
I
isidor 已提交
78

79 80 81 82
	if (options.maxValueLength && value.length > options.maxValueLength) {
		value = value.substr(0, options.maxValueLength) + '...';
	}
	if (value && !options.preserveWhitespace) {
I
isidor 已提交
83
		container.textContent = replaceWhitespace(value);
84 85
	} else {
		container.textContent = value;
I
isidor 已提交
86
	}
I
isidor 已提交
87 88 89
	if (options.showHover) {
		container.title = value;
	}
E
Erich Gamma 已提交
90 91
}

I
isidor 已提交
92
export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void {
93
	if (variable.available) {
I
isidor 已提交
94
		data.name.textContent = replaceWhitespace(variable.name);
95
		data.name.title = variable.type ? variable.type : variable.name;
96 97
	}

E
Erich Gamma 已提交
98
	if (variable.value) {
I
isidor 已提交
99
		data.name.textContent += variable.name ? ':' : '';
100 101 102
		renderExpressionValue(variable, data.value, {
			showChanged,
			maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
I
isidor 已提交
103 104
			preserveWhitespace: false,
			showHover: true
105
		});
E
Erich Gamma 已提交
106 107 108 109 110 111
	} else {
		data.value.textContent = '';
		data.value.title = '';
	}
}

I
isidor 已提交
112 113 114 115
interface IRenameBoxOptions {
	initialValue: string;
	ariaLabel: string;
	placeholder?: string;
116
	validationOptions?: IInputValidationOptions;
I
isidor 已提交
117 118
}

I
isidor 已提交
119
function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
120
	let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
121
	let inputBox = new InputBox(inputBoxContainer, contextViewService, {
I
isidor 已提交
122 123 124
		validationOptions: options.validationOptions,
		placeholder: options.placeholder,
		ariaLabel: options.ariaLabel
125 126
	});

I
isidor 已提交
127
	inputBox.value = options.initialValue ? options.initialValue : '';
128
	inputBox.focus();
129
	inputBox.select();
130

I
isidor 已提交
131 132
	let disposed = false;
	const toDispose: [lifecycle.IDisposable] = [inputBox];
133

J
Joao Moreno 已提交
134
	const wrapUp = once((renamed: boolean) => {
135 136
		if (!disposed) {
			disposed = true;
I
isidor 已提交
137
			if (element instanceof Expression && renamed && inputBox.value) {
138
				debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
I
isidor 已提交
139
			} else if (element instanceof Expression && !element.name) {
140
				debugService.removeWatchExpressions(element.getId());
141 142
			} else if (element instanceof FunctionBreakpoint && inputBox.value) {
				debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, errors.onUnexpectedError);
I
isidor 已提交
143
			} else if (element instanceof FunctionBreakpoint && !element.name) {
144
				debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
I
isidor 已提交
145
			} else if (element instanceof Variable) {
146
				element.errorMessage = null;
147
				if (renamed && element.value !== inputBox.value) {
148
					element.setVariable(inputBox.value)
149
						// if everything went fine we need to refresh ui elements since the variable update can change watch and variables view
150
						.done(() => tree.refresh(element, false), errors.onUnexpectedError);
151
				}
152
			}
153

154 155
			tree.clearHighlight();
			tree.DOMFocus();
156
			tree.setFocus(element);
157

I
isidor 已提交
158
			// need to remove the input box since this template will be reused.
159
			container.removeChild(inputBoxContainer);
J
Joao Moreno 已提交
160
			lifecycle.dispose(toDispose);
161 162 163
		}
	});

A
Cleanup  
Alex Dima 已提交
164
	toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
A
Alexandru Dima 已提交
165 166
		const isEscape = e.equals(KeyCode.Escape);
		const isEnter = e.equals(KeyCode.Enter);
167
		if (isEscape || isEnter) {
168 169
			e.preventDefault();
			e.stopPropagation();
170 171 172 173 174 175 176 177
			wrapUp(isEnter);
		}
	}));
	toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
		wrapUp(true);
	}));
}

178
function getSourceName(source: Source, contextService: IWorkspaceContextService): string {
I
isidor 已提交
179
	if (source.name) {
180 181 182
		return source.name;
	}

I
isidor 已提交
183
	return getPathLabel(paths.basename(source.uri.fsPath), contextService);
184 185
}

I
isidor 已提交
186
export class BaseDebugController extends DefaultController {
E
Erich Gamma 已提交
187

188 189
	private contributedContextMenu: IMenu;

I
isidor 已提交
190
	constructor(
I
isidor 已提交
191
		private actionProvider: IActionProvider,
192
		menuId: MenuId,
193
		@debug.IDebugService protected debugService: debug.IDebugService,
194
		@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
195
		@IContextMenuService private contextMenuService: IContextMenuService,
196 197
		@IContextKeyService contextKeyService: IContextKeyService,
		@IMenuService menuService: IMenuService
I
isidor 已提交
198
	) {
199
		super({ clickBehavior: ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: false });
E
Erich Gamma 已提交
200

201
		this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
E
Erich Gamma 已提交
202 203
	}

I
isidor 已提交
204
	public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean {
E
Erich Gamma 已提交
205 206 207 208 209 210 211
		if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
			return false;
		}

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

212
		tree.setFocus(element);
E
Erich Gamma 已提交
213 214

		if (this.actionProvider.hasSecondaryActions(tree, element)) {
I
isidor 已提交
215
			const anchor = { x: event.posx + 1, y: event.posy };
E
Erich Gamma 已提交
216 217
			this.contextMenuService.showContextMenu({
				getAnchor: () => anchor,
218 219 220 221
				getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => {
					fillInActions(this.contributedContextMenu, this.getContext(element), actions);
					return actions;
				}),
E
Erich Gamma 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235
				onHide: (wasCancelled?: boolean) => {
					if (wasCancelled) {
						tree.DOMFocus();
					}
				},
				getActionsContext: () => element
			});

			return true;
		}

		return false;
	}

236 237 238
	protected getContext(element: any): any {
		return undefined;
	}
E
Erich Gamma 已提交
239 240
}

I
isidor 已提交
241
// call stack
E
Erich Gamma 已提交
242

243 244
export class CallStackController extends BaseDebugController {

I
isidor 已提交
245
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
246
		if (element instanceof ThreadAndProcessIds) {
247 248
			return this.showMoreStackFrames(tree, element);
		}
249 250 251
		if (element instanceof StackFrame) {
			this.focusStackFrame(element, event, true);
		}
252 253 254 255

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

256 257
	protected getContext(element: any): any {
		if (element instanceof StackFrame) {
258 259 260 261
			if (element.source.inMemory) {
				return element.source.raw.path || element.source.reference;
			}

262 263 264
			return element.source.uri.toString();
		}
	}
I
isidor 已提交
265

266
	// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
267
	public showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
I
isidor 已提交
268 269
		const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
		const thread = process && process.getThread(threadAndProcessIds.threadId);
270
		if (thread) {
271
			(<Thread>thread).fetchCallStack(true)
272 273 274 275 276
				.done(() => tree.refresh(), errors.onUnexpectedError);
		}

		return true;
	}
277

278
	public focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
279
		this.debugService.focusStackFrameAndEvaluate(stackFrame).then(() => {
I
isidor 已提交
280
			const sideBySide = (event && (event.ctrlKey || event.metaKey));
I
isidor 已提交
281
			return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide);
282
		}, errors.onUnexpectedError);
283
	}
284 285 286
}


I
isidor 已提交
287
export class CallStackActionProvider implements IActionProvider {
I
isidor 已提交
288

J
Johannes Rieken 已提交
289
	constructor( @IInstantiationService private instantiationService: IInstantiationService, @debug.IDebugService private debugService: debug.IDebugService) {
I
isidor 已提交
290 291 292
		// noop
	}

I
isidor 已提交
293
	public hasActions(tree: ITree, element: any): boolean {
I
isidor 已提交
294 295 296
		return false;
	}

I
isidor 已提交
297
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
I
isidor 已提交
298 299 300
		return TPromise.as([]);
	}

I
isidor 已提交
301
	public hasSecondaryActions(tree: ITree, element: any): boolean {
302
		return element !== tree.getInput();
I
isidor 已提交
303 304
	}

I
isidor 已提交
305 306
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
		const actions: IAction[] = [];
307 308 309 310
		if (element instanceof Process) {
			actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL));
			actions.push(this.instantiationService.createInstance(StopAction, StopAction.ID, StopAction.LABEL));
		} else if (element instanceof Thread) {
I
isidor 已提交
311
			const thread = <Thread>element;
A
Andre Weinand 已提交
312
			if (thread.stopped) {
I
isidor 已提交
313 314 315 316
				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 已提交
317
			} else {
I
isidor 已提交
318
				actions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL));
A
Andre Weinand 已提交
319
			}
I
isidor 已提交
320
		} else if (element instanceof StackFrame) {
321
			if (element.thread.process.session.capabilities.supportsRestartFrame) {
I
isidor 已提交
322
				actions.push(this.instantiationService.createInstance(RestartFrameAction, RestartFrameAction.ID, RestartFrameAction.LABEL));
323
			}
I
isidor 已提交
324
			actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL));
I
isidor 已提交
325 326 327 328 329
		}

		return TPromise.as(actions);
	}

I
isidor 已提交
330
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
331 332 333 334
		return null;
	}
}

I
isidor 已提交
335
export class CallStackDataSource implements IDataSource {
E
Erich Gamma 已提交
336

I
isidor 已提交
337
	public getId(tree: ITree, element: any): string {
338 339 340
		if (typeof element === 'string') {
			return element;
		}
I
isidor 已提交
341

E
Erich Gamma 已提交
342 343 344
		return element.getId();
	}

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

I
isidor 已提交
349 350
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Thread) {
I
isidor 已提交
351
			return this.getThreadChildren(element);
E
Erich Gamma 已提交
352
		}
I
isidor 已提交
353
		if (element instanceof Model) {
354
			return TPromise.as(element.getProcesses());
355 356
		}

357
		const process = <debug.IProcess>element;
I
isidor 已提交
358
		return TPromise.as(process.getAllThreads());
E
Erich Gamma 已提交
359 360
	}

361 362
	private getThreadChildren(thread: Thread): TPromise<any> {
		return thread.fetchCallStack().then((callStack: any[]) => {
I
isidor 已提交
363
			if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) {
364 365
				return callStack.concat([thread.stoppedDetails.framesErrorMessage]);
			}
I
isidor 已提交
366
			if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length) {
I
isidor 已提交
367
				return callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]);
I
isidor 已提交
368 369 370 371 372 373
			}

			return callStack;
		});
	}

I
isidor 已提交
374
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
375
		return TPromise.as(null);
E
Erich Gamma 已提交
376 377 378 379
	}
}

interface IThreadTemplateData {
I
isidor 已提交
380
	thread: HTMLElement;
E
Erich Gamma 已提交
381
	name: HTMLElement;
I
isidor 已提交
382 383
	state: HTMLElement;
	stateLabel: HTMLSpanElement;
E
Erich Gamma 已提交
384 385
}

I
isidor 已提交
386 387 388
interface IProcessTemplateData {
	process: HTMLElement;
	name: HTMLElement;
I
isidor 已提交
389 390
	state: HTMLElement;
	stateLabel: HTMLSpanElement;
I
isidor 已提交
391 392
}

393 394 395 396
interface IErrorTemplateData {
	label: HTMLElement;
}

I
isidor 已提交
397 398 399 400
interface ILoadMoreTemplateData {
	label: HTMLElement;
}

E
Erich Gamma 已提交
401 402
interface IStackFrameTemplateData {
	stackFrame: HTMLElement;
403 404 405 406
	label: HTMLElement;
	file: HTMLElement;
	fileName: HTMLElement;
	lineNumber: HTMLElement;
E
Erich Gamma 已提交
407 408
}

I
isidor 已提交
409
export class CallStackRenderer implements IRenderer {
E
Erich Gamma 已提交
410 411 412

	private static THREAD_TEMPLATE_ID = 'thread';
	private static STACK_FRAME_TEMPLATE_ID = 'stackFrame';
413
	private static ERROR_TEMPLATE_ID = 'error';
I
isidor 已提交
414
	private static LOAD_MORE_TEMPLATE_ID = 'loadMore';
I
isidor 已提交
415
	private static PROCESS_TEMPLATE_ID = 'process';
E
Erich Gamma 已提交
416

J
Johannes Rieken 已提交
417
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
E
Erich Gamma 已提交
418 419 420
		// noop
	}

I
isidor 已提交
421
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
422
		return 22;
E
Erich Gamma 已提交
423 424
	}

I
isidor 已提交
425 426
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Process) {
I
isidor 已提交
427 428
			return CallStackRenderer.PROCESS_TEMPLATE_ID;
		}
I
isidor 已提交
429
		if (element instanceof Thread) {
E
Erich Gamma 已提交
430 431
			return CallStackRenderer.THREAD_TEMPLATE_ID;
		}
I
isidor 已提交
432
		if (element instanceof StackFrame) {
E
Erich Gamma 已提交
433 434
			return CallStackRenderer.STACK_FRAME_TEMPLATE_ID;
		}
435 436 437
		if (typeof element === 'string') {
			return CallStackRenderer.ERROR_TEMPLATE_ID;
		}
E
Erich Gamma 已提交
438

I
isidor 已提交
439
		return CallStackRenderer.LOAD_MORE_TEMPLATE_ID;
E
Erich Gamma 已提交
440 441
	}

I
isidor 已提交
442
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
443 444 445 446
		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 已提交
447 448
			data.state = dom.append(data.process, $('.state'));
			data.stateLabel = dom.append(data.state, $('span.label'));
I
isidor 已提交
449 450 451 452

			return data;
		}

I
isidor 已提交
453 454 455 456 457 458
		if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.load-more'));

			return data;
		}
459 460 461 462 463 464
		if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
			let data: ILoadMoreTemplateData = Object.create(null);
			data.label = dom.append(container, $('.error'));

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

			return data;
		}

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

I
isidor 已提交
499
	private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void {
I
isidor 已提交
500
		data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process");
I
isidor 已提交
501
		data.name.textContent = process.name;
I
isidor 已提交
502 503 504 505
		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 已提交
506 507
	}

E
Erich Gamma 已提交
508
	private renderThread(thread: debug.IThread, data: IThreadTemplateData): void {
I
isidor 已提交
509
		data.thread.title = nls.localize('thread', "Thread");
E
Erich Gamma 已提交
510
		data.name.textContent = thread.name;
I
isidor 已提交
511

I
isidor 已提交
512
		data.stateLabel.textContent = thread.stopped ? nls.localize({ key: 'pausedOn', comment: ['indicates reason for program being paused'] }, "Paused on {0}", thread.stoppedDetails.reason)
I
isidor 已提交
513
			: 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
	private renderStackFrame(stackFrame: debug.IStackFrame, data: IStackFrameTemplateData): void {
526
		stackFrame.source.deemphasize ? dom.addClass(data.stackFrame, 'disabled') : dom.removeClass(data.stackFrame, 'disabled');
527
		data.file.title = stackFrame.source.raw.path || stackFrame.source.name;
528 529 530
		if (stackFrame.source.raw.origin) {
			data.file.title += `\n${stackFrame.source.raw.origin}`;
		}
E
Erich Gamma 已提交
531
		data.label.textContent = stackFrame.name;
532
		data.label.title = stackFrame.name;
533
		data.fileName.textContent = getSourceName(stackFrame.source, this.contextService);
I
isidor 已提交
534
		if (stackFrame.lineNumber !== undefined) {
535
			data.lineNumber.textContent = `${stackFrame.lineNumber}`;
I
isidor 已提交
536 537 538 539
			dom.removeClass(data.lineNumber, 'unavailable');
		} else {
			dom.addClass(data.lineNumber, 'unavailable');
		}
E
Erich Gamma 已提交
540 541
	}

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

I
isidor 已提交
547
export class CallstackAccessibilityProvider implements IAccessibilityProvider {
548

J
Johannes Rieken 已提交
549
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
550 551 552
		// noop
	}

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

		return null;
	}
}

I
isidor 已提交
565
// variables
E
Erich Gamma 已提交
566

I
isidor 已提交
567
export class VariablesActionProvider implements IActionProvider {
E
Erich Gamma 已提交
568

I
isidor 已提交
569 570
	constructor(private instantiationService: IInstantiationService) {
		// noop
E
Erich Gamma 已提交
571 572
	}

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

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

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

I
isidor 已提交
585
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
586
		const actions: IAction[] = [];
I
isidor 已提交
587
		const variable = <Variable>element;
588 589 590
		actions.push(this.instantiationService.createInstance(SetValueAction, SetValueAction.ID, SetValueAction.LABEL, variable));
		actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable));
		actions.push(new Separator());
I
isidor 已提交
591
		actions.push(this.instantiationService.createInstance(AddToWatchExpressionsAction, AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable));
592

A
Alex Dima 已提交
593
		return TPromise.as(actions);
E
Erich Gamma 已提交
594
	}
I
isidor 已提交
595

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

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

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

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

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

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

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

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

interface IScopeTemplateData {
	name: HTMLElement;
}

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

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

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

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

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

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

		return null;
	}

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

		return data;
	}

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

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

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

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

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

		return null;
	}
}

726 727
export class VariablesController extends BaseDebugController {

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

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

I
isidor 已提交
742
// watch expressions
E
Erich Gamma 已提交
743

I
isidor 已提交
744
export class WatchExpressionsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
745 746 747 748 749 750 751

	private instantiationService: IInstantiationService;

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

I
isidor 已提交
752 753
	public hasActions(tree: ITree, element: any): boolean {
		return element instanceof Expression && !!element.name;
E
Erich Gamma 已提交
754 755
	}

I
isidor 已提交
756
	public hasSecondaryActions(tree: ITree, element: any): boolean {
E
Erich Gamma 已提交
757 758 759
		return true;
	}

I
isidor 已提交
760
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
I
isidor 已提交
761
		return TPromise.as([]);
E
Erich Gamma 已提交
762 763
	}

I
isidor 已提交
764 765 766 767 768
	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));
769
			if (!expression.hasChildren) {
770
				actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value));
E
Erich Gamma 已提交
771
			}
I
isidor 已提交
772
			actions.push(new Separator());
E
Erich Gamma 已提交
773

I
isidor 已提交
774 775
			actions.push(this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL));
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
776
		} else {
I
isidor 已提交
777 778 779
			actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL));
			if (element instanceof Variable) {
				const variable = <Variable>element;
780
				if (!variable.hasChildren) {
781
					actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable.value));
E
Erich Gamma 已提交
782
				}
I
isidor 已提交
783
				actions.push(new Separator());
E
Erich Gamma 已提交
784
			}
I
isidor 已提交
785
			actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL));
E
Erich Gamma 已提交
786 787
		}

A
Alex Dima 已提交
788
		return TPromise.as(actions);
E
Erich Gamma 已提交
789
	}
I
isidor 已提交
790

I
isidor 已提交
791
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
792 793
		return null;
	}
E
Erich Gamma 已提交
794 795
}

I
isidor 已提交
796
export class WatchExpressionsDataSource implements IDataSource {
E
Erich Gamma 已提交
797

I
isidor 已提交
798
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
799 800 801
		return element.getId();
	}

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

I
isidor 已提交
807
		const watchExpression = <Expression>element;
808
		return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null');
E
Erich Gamma 已提交
809 810
	}

I
isidor 已提交
811 812 813
	public getChildren(tree: ITree, element: any): TPromise<any> {
		if (element instanceof Model) {
			return TPromise.as((<Model>element).getWatchExpressions());
E
Erich Gamma 已提交
814 815
		}

I
isidor 已提交
816
		let expression = <Expression>element;
817
		return expression.getChildren();
E
Erich Gamma 已提交
818 819
	}

I
isidor 已提交
820
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
821
		return TPromise.as(null);
E
Erich Gamma 已提交
822 823 824
	}
}

I
isidor 已提交
825 826 827 828 829
interface IWatchExpressionTemplateData {
	watchExpression: HTMLElement;
	expression: HTMLElement;
	name: HTMLSpanElement;
	value: HTMLSpanElement;
E
Erich Gamma 已提交
830 831
}

I
isidor 已提交
832
export class WatchExpressionsRenderer implements IRenderer {
E
Erich Gamma 已提交
833 834 835 836 837 838

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

I
isidor 已提交
839
	constructor(
I
isidor 已提交
840 841
		actionProvider: IActionProvider,
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
842 843 844 845
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
	) {
		this.toDispose = [];
846
		this.actionProvider = <WatchExpressionsActionProvider>actionProvider;
E
Erich Gamma 已提交
847 848
	}

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

I
isidor 已提交
853 854
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Expression) {
E
Erich Gamma 已提交
855 856 857 858 859 860
			return WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID;
		}

		return WatchExpressionsRenderer.VARIABLE_TEMPLATE_ID;
	}

I
isidor 已提交
861
	public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
I
isidor 已提交
862 863 864 865 866 867
		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 已提交
868
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
I
isidor 已提交
869 870 871 872 873
			const data: IWatchExpressionTemplateData = Object.create(null);
			data.watchExpression = dom.append(container, $('.watch-expression'));
			createVariableTemplate(data, data.watchExpression);

			return data;
E
Erich Gamma 已提交
874 875
		}

I
isidor 已提交
876 877
		const data: IVariableTemplateData = Object.create(null);
		createVariableTemplate(data, container);
E
Erich Gamma 已提交
878 879 880 881

		return data;
	}

I
isidor 已提交
882
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
E
Erich Gamma 已提交
883 884 885
		if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
			this.renderWatchExpression(tree, element, templateData);
		} else {
886
			renderVariable(tree, element, templateData, true);
E
Erich Gamma 已提交
887 888 889
		}
	}

I
isidor 已提交
890
	private renderWatchExpression(tree: ITree, watchExpression: debug.IExpression, data: IWatchExpressionTemplateData): void {
E
Erich Gamma 已提交
891
		let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
I
isidor 已提交
892
		if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
I
isidor 已提交
893 894 895 896 897
			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 已提交
898 899
		}

900
		data.name.textContent = watchExpression.name;
901
		if (watchExpression.value) {
902
			data.name.textContent += ':';
903 904 905
			renderExpressionValue(watchExpression, data.value, {
				showChanged: true,
				maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
I
isidor 已提交
906 907
				preserveWhitespace: false,
				showHover: true
908
			});
909
			data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value;
E
Erich Gamma 已提交
910 911 912
		}
	}

I
isidor 已提交
913
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
I
isidor 已提交
914
		// noop
E
Erich Gamma 已提交
915 916 917
	}

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

I
isidor 已提交
922
export class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider {
923

I
isidor 已提交
924 925 926
	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);
927
		}
I
isidor 已提交
928 929
		if (element instanceof Variable) {
			return nls.localize('watchVariableAriaLabel', "{0} value {1}, watch, debug", (<Variable>element).name, (<Variable>element).value);
930 931 932 933 934 935
		}

		return null;
	}
}

E
Erich Gamma 已提交
936 937
export class WatchExpressionsController extends BaseDebugController {

I
isidor 已提交
938
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
I
isidor 已提交
939
		// double click on primitive value: open input box to be able to select and copy value.
I
isidor 已提交
940
		if (element instanceof Expression && event.detail === 2) {
941
			const expression = <debug.IExpression>element;
942
			if (!expression.hasChildren) {
E
Erich Gamma 已提交
943 944 945 946 947 948 949 950 951
				this.debugService.getViewModel().setSelectedExpression(expression);
			}
			return true;
		}

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

I
isidor 已提交
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
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 {
975
		if (target instanceof Expression || target instanceof Model) {
976 977 978 979
			return {
				accept: true,
				autoExpand: false
			};
980 981 982
		}

		return DRAG_OVER_REJECT;
I
isidor 已提交
983 984 985 986 987 988 989 990 991 992 993 994 995
	}

	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 已提交
996
// breakpoints
E
Erich Gamma 已提交
997

I
isidor 已提交
998
export class BreakpointsActionProvider implements IActionProvider {
E
Erich Gamma 已提交
999

I
isidor 已提交
1000
	constructor(private instantiationService: IInstantiationService, private debugService: debug.IDebugService) {
I
isidor 已提交
1001
		// noop
E
Erich Gamma 已提交
1002 1003
	}

I
isidor 已提交
1004
	public hasActions(tree: ITree, element: any): boolean {
I
isidor 已提交
1005
		return false;;
E
Erich Gamma 已提交
1006 1007
	}

I
isidor 已提交
1008 1009
	public hasSecondaryActions(tree: ITree, element: any): boolean {
		return element instanceof Breakpoint || element instanceof ExceptionBreakpoint || element instanceof FunctionBreakpoint;
E
Erich Gamma 已提交
1010 1011
	}

I
isidor 已提交
1012
	public getActions(tree: ITree, element: any): TPromise<IAction[]> {
A
Alex Dima 已提交
1013
		return TPromise.as([]);
E
Erich Gamma 已提交
1014 1015
	}

I
isidor 已提交
1016
	public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
I
isidor 已提交
1017
		const actions: IAction[] = [];
E
Erich Gamma 已提交
1018

I
isidor 已提交
1019 1020
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
			actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
1021
		}
I
isidor 已提交
1022 1023 1024
		if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
			actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL));
			actions.push(new Separator());
E
Erich Gamma 已提交
1025

I
isidor 已提交
1026 1027
			actions.push(this.instantiationService.createInstance(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL));
			actions.push(this.instantiationService.createInstance(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL));
1028
		}
I
isidor 已提交
1029

I
isidor 已提交
1030
		actions.push(new Separator());
I
isidor 已提交
1031
		actions.push(this.instantiationService.createInstance(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL));
E
Erich Gamma 已提交
1032

A
Alex Dima 已提交
1033
		return TPromise.as(actions);
E
Erich Gamma 已提交
1034
	}
I
isidor 已提交
1035

I
isidor 已提交
1036
	public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
I
isidor 已提交
1037 1038
		return null;
	}
E
Erich Gamma 已提交
1039 1040
}

I
isidor 已提交
1041
export class BreakpointsDataSource implements IDataSource {
E
Erich Gamma 已提交
1042

I
isidor 已提交
1043
	public getId(tree: ITree, element: any): string {
E
Erich Gamma 已提交
1044 1045 1046
		return element.getId();
	}

I
isidor 已提交
1047 1048
	public hasChildren(tree: ITree, element: any): boolean {
		return element instanceof Model;
E
Erich Gamma 已提交
1049 1050
	}

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

A
Alex Dima 已提交
1055
		return TPromise.as(exBreakpoints.concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints()));
E
Erich Gamma 已提交
1056 1057
	}

I
isidor 已提交
1058
	public getParent(tree: ITree, element: any): TPromise<any> {
A
Alex Dima 已提交
1059
		return TPromise.as(null);
E
Erich Gamma 已提交
1060 1061 1062
	}
}

1063
interface IBaseBreakpointTemplateData {
1064
	breakpoint: HTMLElement;
E
Erich Gamma 已提交
1065 1066
	name: HTMLElement;
	checkbox: HTMLInputElement;
1067 1068
	context: debug.IEnablement;
	toDispose: lifecycle.IDisposable[];
E
Erich Gamma 已提交
1069 1070
}

1071
interface IBreakpointTemplateData extends IBaseBreakpointTemplateData {
E
Erich Gamma 已提交
1072 1073 1074 1075
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

I
isidor 已提交
1076
export class BreakpointsRenderer implements IRenderer {
E
Erich Gamma 已提交
1077 1078

	private static EXCEPTION_BREAKPOINT_TEMPLATE_ID = 'exceptionBreakpoint';
1079
	private static FUNCTION_BREAKPOINT_TEMPLATE_ID = 'functionBreakpoint';
E
Erich Gamma 已提交
1080 1081 1082 1083
	private static BREAKPOINT_TEMPLATE_ID = 'breakpoint';

	constructor(
		private actionProvider: BreakpointsActionProvider,
I
isidor 已提交
1084
		private actionRunner: IActionRunner,
E
Erich Gamma 已提交
1085
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
1086 1087
		@debug.IDebugService private debugService: debug.IDebugService,
		@IContextViewService private contextViewService: IContextViewService
E
Erich Gamma 已提交
1088 1089 1090 1091
	) {
		// noop
	}

I
isidor 已提交
1092
	public getHeight(tree: ITree, element: any): number {
I
isidor 已提交
1093
		return 22;
E
Erich Gamma 已提交
1094 1095
	}

I
isidor 已提交
1096 1097
	public getTemplateId(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
E
Erich Gamma 已提交
1098 1099
			return BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1100
		if (element instanceof FunctionBreakpoint) {
1101 1102
			return BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID;
		}
I
isidor 已提交
1103
		if (element instanceof ExceptionBreakpoint) {
E
Erich Gamma 已提交
1104 1105 1106 1107 1108 1109
			return BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID;
		}

		return null;
	}

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

1114
		data.checkbox = <HTMLInputElement>$('input');
E
Erich Gamma 已提交
1115
		data.checkbox.type = 'checkbox';
1116 1117 1118 1119
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));
1120

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

1123
		data.name = dom.append(data.breakpoint, $('span.name'));
E
Erich Gamma 已提交
1124 1125

		if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID) {
1126
			data.filePath = dom.append(data.breakpoint, $('span.file-path'));
I
isidor 已提交
1127 1128 1129 1130 1131
			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 已提交
1132 1133 1134 1135 1136
		}

		return data;
	}

I
isidor 已提交
1137
	public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
1138
		templateData.context = element;
E
Erich Gamma 已提交
1139 1140
		if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderExceptionBreakpoint(element, templateData);
1141 1142
		} else if (templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) {
			this.renderFunctionBreakpoint(tree, element, templateData);
E
Erich Gamma 已提交
1143 1144 1145 1146 1147
		} else {
			this.renderBreakpoint(tree, element, templateData);
		}
	}

1148
	private renderExceptionBreakpoint(exceptionBreakpoint: debug.IExceptionBreakpoint, data: IBaseBreakpointTemplateData): void {
1149
		data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;;
1150
		data.breakpoint.title = data.name.textContent;
E
Erich Gamma 已提交
1151
		data.checkbox.checked = exceptionBreakpoint.enabled;
1152
	}
E
Erich Gamma 已提交
1153

1154
	private renderFunctionBreakpoint(tree: ITree, functionBreakpoint: debug.IFunctionBreakpoint, data: IBaseBreakpointTemplateData): void {
I
isidor 已提交
1155 1156
		const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
		if (!functionBreakpoint.name || (selected && selected.getId() === functionBreakpoint.getId())) {
1157
			data.name.textContent = '';
I
isidor 已提交
1158 1159 1160 1161 1162
			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")
			});
1163 1164 1165
		} else {
			data.name.textContent = functionBreakpoint.name;
			data.checkbox.checked = functionBreakpoint.enabled;
1166 1167 1168
			data.breakpoint.title = functionBreakpoint.name;

			// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
1169
			const process = this.debugService.getViewModel().focusedProcess;
1170
			if ((process && !process.session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()) {
1171
				tree.addTraits('disabled', [functionBreakpoint]);
1172
				if (process && !process.session.capabilities.supportsFunctionBreakpoints) {
1173 1174 1175 1176 1177
					data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
				}
			} else {
				tree.removeTraits('disabled', [functionBreakpoint]);
			}
1178
		}
E
Erich Gamma 已提交
1179 1180
	}

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

1184
		data.name.textContent = getPathLabel(paths.basename(breakpoint.uri.fsPath), this.contextService);
1185
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
I
isidor 已提交
1186 1187 1188
		if (breakpoint.column) {
			data.lineNumber.textContent += `:${breakpoint.column}`;
		}
1189
		data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService);
E
Erich Gamma 已提交
1190
		data.checkbox.checked = breakpoint.enabled;
I
isidor 已提交
1191

I
isidor 已提交
1192
		const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing;
I
isidor 已提交
1193 1194 1195 1196 1197
		if (debugActive && !breakpoint.verified) {
			tree.addTraits('disabled', [breakpoint]);
			if (breakpoint.message) {
				data.breakpoint.title = breakpoint.message;
			}
1198 1199
		} else if (breakpoint.condition || breakpoint.hitCondition) {
			data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
1200
		}
E
Erich Gamma 已提交
1201 1202
	}

I
isidor 已提交
1203
	public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
1204
		lifecycle.dispose(templateData.toDispose);
E
Erich Gamma 已提交
1205 1206 1207
	}
}

I
isidor 已提交
1208
export class BreakpointsAccessibilityProvider implements IAccessibilityProvider {
1209

J
Johannes Rieken 已提交
1210
	constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
1211 1212 1213
		// noop
	}

I
isidor 已提交
1214 1215
	public getAriaLabel(tree: ITree, element: any): string {
		if (element instanceof Breakpoint) {
1216
			return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (<Breakpoint>element).lineNumber, getPathLabel(paths.basename((<Breakpoint>element).uri.fsPath), this.contextService), this.contextService);
1217
		}
I
isidor 已提交
1218 1219
		if (element instanceof FunctionBreakpoint) {
			return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (<FunctionBreakpoint>element).name);
1220
		}
I
isidor 已提交
1221 1222
		if (element instanceof ExceptionBreakpoint) {
			return nls.localize('exceptionBreakpointAriaLabel', "Exception breakpoint {0}, breakpoints, debug", (<ExceptionBreakpoint>element).filter);
1223 1224 1225 1226 1227 1228
		}

		return null;
	}
}

E
Erich Gamma 已提交
1229 1230
export class BreakpointsController extends BaseDebugController {

I
isidor 已提交
1231 1232
	protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
		if (element instanceof FunctionBreakpoint && event.detail === 2) {
I
isidor 已提交
1233 1234 1235
			this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
			return true;
		}
I
isidor 已提交
1236
		if (element instanceof Breakpoint) {
1237 1238
			this.openBreakpointSource(element, event, true);
		}
I
isidor 已提交
1239 1240

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

1243
	public openBreakpointSource(breakpoint: Breakpoint, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
1244
		const sideBySide = (event && (event.ctrlKey || event.metaKey));
1245 1246 1247 1248
		this.editorService.openEditor({
			resource: breakpoint.uri,
			options: {
				preserveFocus,
I
isidor 已提交
1249
				selection: { startLineNumber: breakpoint.lineNumber, startColumn: 1 },
I
isidor 已提交
1250 1251
				revealIfVisible: true,
				revealInCenterIfOutsideViewport: true
1252 1253
			}
		}, sideBySide).done(undefined, errors.onUnexpectedError);
1254
	}
E
Erich Gamma 已提交
1255
}