breakpointsView.ts 24.6 KB
Newer Older
I
isidor 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as resources from 'vs/base/common/resources';
import * as dom from 'vs/base/browser/dom';
I
isidor 已提交
9 10
import { IAction, Action } from 'vs/base/common/actions';
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
I
isidor 已提交
11
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint } from 'vs/workbench/parts/debug/common/debugModel';
12
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions';
13
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
I
isidor 已提交
14 15 16
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
17 18 19
import { Constants } from 'vs/editor/common/core/uint';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
J
Joao Moreno 已提交
20
import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list';
21
import { IEditor } from 'vs/workbench/common/editor';
22
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
23
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
24
import { KeyCode } from 'vs/base/common/keyCodes';
25
import { WorkbenchList } from 'vs/platform/list/browser/listService';
26
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
27
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
I
isidor 已提交
28
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
29
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
30
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
I
isidor 已提交
31
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
I
isidor 已提交
32
import { ILabelService } from 'vs/platform/label/common/label';
I
isidor 已提交
33

34
const $ = dom.$;
I
isidor 已提交
35

36 37 38 39 40 41 42 43
function createCheckbox(): HTMLInputElement {
	const checkbox = <HTMLInputElement>$('input');
	checkbox.type = 'checkbox';
	checkbox.tabIndex = -1;

	return checkbox;
}

44
export class BreakpointsView extends ViewletPanel {
I
isidor 已提交
45 46

	private static readonly MAX_VISIBLE_FILES = 9;
J
Joao Moreno 已提交
47
	private list: WorkbenchList<IEnablement>;
I
isidor 已提交
48
	private needsRefresh: boolean;
I
isidor 已提交
49 50 51 52 53 54 55

	constructor(
		options: IViewletViewOptions,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IDebugService private debugService: IDebugService,
		@IKeybindingService keybindingService: IKeybindingService,
		@IInstantiationService private instantiationService: IInstantiationService,
I
isidor 已提交
56
		@IThemeService private themeService: IThemeService,
57
		@IEditorService private editorService: IEditorService,
J
Joao Moreno 已提交
58
		@IContextViewService private contextViewService: IContextViewService,
59
		@IConfigurationService configurationService: IConfigurationService
I
isidor 已提交
60
	) {
I
isidor 已提交
61
		super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService);
I
isidor 已提交
62 63 64 65 66 67 68

		this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
		this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
	}

	public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'debug-breakpoints');
69
		const delegate = new BreakpointsDelegate(this.debugService);
I
isidor 已提交
70

71
		this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [
I
isidor 已提交
72 73
			this.instantiationService.createInstance(BreakpointsRenderer),
			new ExceptionBreakpointsRenderer(this.debugService),
I
isidor 已提交
74
			this.instantiationService.createInstance(FunctionBreakpointsRenderer),
75
			new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
I
isidor 已提交
76
		], {
J
Joao Moreno 已提交
77
				identityProvider: { getId: element => element.getId() },
78
				multipleSelectionSupport: false
79
			}) as WorkbenchList<IEnablement>;
J
Joao Moreno 已提交
80 81

		CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.list.contextKeyService);
I
isidor 已提交
82

I
isidor 已提交
83
		this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
J
Joao Moreno 已提交
84

85 86 87
		this.disposables.push(this.list.onOpen(e => {
			let isSingleClick = false;
			let isDoubleClick = false;
88
			let isMiddleClick = false;
89 90 91 92 93 94
			let openToSide = false;

			const browserEvent = e.browserEvent;
			if (browserEvent instanceof MouseEvent) {
				isSingleClick = browserEvent.detail === 1;
				isDoubleClick = browserEvent.detail === 2;
95
				isMiddleClick = browserEvent.button === 1;
96 97 98
				openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
			}

99 100
			const focused = this.list.getFocusedElements();
			const element = focused.length ? focused[0] : undefined;
101 102 103

			if (isMiddleClick) {
				if (element instanceof Breakpoint) {
104
					this.debugService.removeBreakpoints(element.getId());
105
				} else if (element instanceof FunctionBreakpoint) {
106
					this.debugService.removeFunctionBreakpoints(element.getId());
107 108 109 110
				}
				return;
			}

111
			if (element instanceof Breakpoint) {
112
				openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService);
113 114 115 116
			}
			if (isDoubleClick && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
				this.onBreakpointsChange();
117 118
			}
		}));
I
isidor 已提交
119

I
isidor 已提交
120
		this.list.splice(0, this.list.length, this.elements);
I
isidor 已提交
121
	}
I
isidor 已提交
122

I
isidor 已提交
123
	public focus(): void {
S
Sandeep Somavarapu 已提交
124
		super.focus();
I
isidor 已提交
125 126 127 128 129
		if (this.list) {
			this.list.domFocus();
		}
	}

I
isidor 已提交
130
	protected layoutBody(size: number): void {
I
isidor 已提交
131 132 133
		if (this.list) {
			this.list.layout(size);
		}
I
isidor 已提交
134 135 136
	}

	private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
J
Joao Moreno 已提交
137 138 139 140
		if (!e.element) {
			return;
		}

I
isidor 已提交
141
		const actions: IAction[] = [];
I
isidor 已提交
142 143
		const element = e.element;

144
		const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
I
isidor 已提交
145
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
I
isidor 已提交
146
			actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), undefined, true, () => {
I
isidor 已提交
147 148
				if (element instanceof Breakpoint) {
					return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
149 150 151 152 153
						if (editor) {
							const codeEditor = editor.getControl();
							if (isCodeEditor(codeEditor)) {
								codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
							}
I
isidor 已提交
154 155 156 157 158 159 160
						}
					});
				}

				this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
				this.onBreakpointsChange();
				return undefined;
I
isidor 已提交
161 162 163 164
			}));
			actions.push(new Separator());
		}

I
isidor 已提交
165
		actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService));
I
isidor 已提交
166

I
isidor 已提交
167 168 169
		if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
			actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
			actions.push(new Separator());
I
isidor 已提交
170

I
isidor 已提交
171 172 173
			actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
			actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
		}
I
isidor 已提交
174

I
isidor 已提交
175 176
		actions.push(new Separator());
		actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
I
isidor 已提交
177

I
isidor 已提交
178 179
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
180
			getActions: () => actions,
I
isidor 已提交
181
			getActionsContext: () => element
I
isidor 已提交
182
		});
I
isidor 已提交
183 184 185 186 187 188 189 190 191 192
	}

	public getActions(): IAction[] {
		return [
			new AddFunctionBreakpointAction(AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL, this.debugService, this.keybindingService),
			new ToggleBreakpointsActivatedAction(ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL, this.debugService, this.keybindingService),
			new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)
		];
	}

I
isidor 已提交
193 194 195 196
	public setExpanded(expanded: boolean): void {
		super.setExpanded(expanded);
		if (expanded && this.needsRefresh) {
			this.onBreakpointsChange();
I
isidor 已提交
197
		}
I
isidor 已提交
198 199
	}

200 201 202 203 204
	public setVisible(visible: boolean): void {
		super.setVisible(visible);
		if (visible && this.needsRefresh) {
			this.onBreakpointsChange();
		}
I
isidor 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218
	}

	private onBreakpointsChange(): void {
		if (this.isExpanded() && this.isVisible()) {
			this.minimumBodySize = this.getExpandedBodySize();
			if (this.maximumBodySize < Number.POSITIVE_INFINITY) {
				this.maximumBodySize = this.minimumBodySize;
			}
			if (this.list) {
				this.list.splice(0, this.list.length, this.elements);
				this.needsRefresh = false;
			}
		} else {
			this.needsRefresh = true;
I
isidor 已提交
219 220 221
		}
	}

I
isidor 已提交
222 223
	private get elements(): IEnablement[] {
		const model = this.debugService.getModel();
I
isidor 已提交
224
		const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints());
I
isidor 已提交
225 226 227 228

		return elements;
	}

I
isidor 已提交
229 230 231 232 233 234
	private getExpandedBodySize(): number {
		const model = this.debugService.getModel();
		const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length;
		return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
	}
}
235

J
Joao Moreno 已提交
236
class BreakpointsDelegate implements IListVirtualDelegate<IEnablement> {
237

238 239 240 241
	constructor(private debugService: IDebugService) {
		// noop
	}

I
isidor 已提交
242 243
	getHeight(element: IEnablement): number {
		return 22;
244 245
	}

I
isidor 已提交
246 247 248
	getTemplateId(element: IEnablement): string {
		if (element instanceof Breakpoint) {
			return BreakpointsRenderer.ID;
249
		}
I
isidor 已提交
250
		if (element instanceof FunctionBreakpoint) {
251
			const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
252 253 254 255
			if (!element.name || (selected && selected.getId() === element.getId())) {
				return FunctionBreakpointInputRenderer.ID;
			}

I
isidor 已提交
256 257 258 259
			return FunctionBreakpointsRenderer.ID;
		}
		if (element instanceof ExceptionBreakpoint) {
			return ExceptionBreakpointsRenderer.ID;
260 261
		}

I
isidor 已提交
262
		return undefined;
263 264 265 266 267 268 269 270 271 272 273
	}
}

interface IBaseBreakpointTemplateData {
	breakpoint: HTMLElement;
	name: HTMLElement;
	checkbox: HTMLInputElement;
	context: IEnablement;
	toDispose: IDisposable[];
}

I
isidor 已提交
274 275 276 277 278
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
	icon: HTMLElement;
}

interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
279 280 281 282
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

283 284
interface IInputTemplateData {
	inputBox: InputBox;
I
isidor 已提交
285 286
	checkbox: HTMLInputElement;
	icon: HTMLElement;
287
	breakpoint: IFunctionBreakpoint;
I
isidor 已提交
288
	reactedOnEvent: boolean;
289 290 291
	toDispose: IDisposable[];
}

J
Joao Moreno 已提交
292
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
293 294 295

	constructor(
		@IDebugService private debugService: IDebugService,
296
		@ILabelService private labelService: ILabelService
297 298 299 300
	) {
		// noop
	}

301
	static readonly ID = 'breakpoints';
302

I
isidor 已提交
303 304
	get templateId() {
		return BreakpointsRenderer.ID;
305 306
	}

I
isidor 已提交
307
	renderTemplate(container: HTMLElement): IBreakpointTemplateData {
308 309 310
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));

I
isidor 已提交
311
		data.icon = $('.icon');
312
		data.checkbox = createCheckbox();
313 314 315 316 317
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
318
		dom.append(data.breakpoint, data.icon);
319 320 321 322
		dom.append(data.breakpoint, data.checkbox);

		data.name = dom.append(data.breakpoint, $('span.name'));

I
isidor 已提交
323 324 325
		data.filePath = dom.append(data.breakpoint, $('span.file-path'));
		const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container'));
		data.lineNumber = dom.append(lineNumberContainer, $('span.line-number'));
326 327 328 329

		return data;
	}

I
isidor 已提交
330 331 332
	renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
		data.context = breakpoint;
		dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
333

I
isidor 已提交
334
		data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);
335 336 337 338
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
		if (breakpoint.column) {
			data.lineNumber.textContent += `:${breakpoint.column}`;
		}
339
		data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
340 341
		data.checkbox.checked = breakpoint.enabled;

342
		const { message, className } = getBreakpointMessageAndClassName(this.debugService, breakpoint);
I
isidor 已提交
343
		data.icon.className = className + ' icon';
I
isidor 已提交
344
		data.breakpoint.title = breakpoint.message || message || '';
I
isidor 已提交
345

346 347
		const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
		if (debugActive && !breakpoint.verified) {
I
isidor 已提交
348
			dom.addClass(data.breakpoint, 'disabled');
I
isidor 已提交
349
		}
350 351
	}

J
Joao Moreno 已提交
352 353 354 355
	disposeElement(): void {
		// noop
	}

I
isidor 已提交
356
	disposeTemplate(templateData: IBreakpointTemplateData): void {
357 358 359 360
		dispose(templateData.toDispose);
	}
}

J
Joao Moreno 已提交
361
class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint, IBaseBreakpointTemplateData> {
362

I
isidor 已提交
363 364 365
	constructor(
		private debugService: IDebugService
	) {
366 367 368
		// noop
	}

369
	static readonly ID = 'exceptionbreakpoints';
370

I
isidor 已提交
371 372 373 374 375 376 377 378
	get templateId() {
		return ExceptionBreakpointsRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));

379
		data.checkbox = createCheckbox();
I
isidor 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

		dom.append(data.breakpoint, data.checkbox);

		data.name = dom.append(data.breakpoint, $('span.name'));
		dom.addClass(data.breakpoint, 'exception');

		return data;
	}

	renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
		data.context = exceptionBreakpoint;
		data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
		data.breakpoint.title = data.name.textContent;
		data.checkbox.checked = exceptionBreakpoint.enabled;
	}

J
Joao Moreno 已提交
400 401 402 403
	disposeElement(): void {
		// noop
	}

I
isidor 已提交
404 405
	disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
		dispose(templateData.toDispose);
406 407 408
	}
}

J
Joao Moreno 已提交
409
class FunctionBreakpointsRenderer implements IListRenderer<FunctionBreakpoint, IBaseBreakpointWithIconTemplateData> {
410

I
isidor 已提交
411
	constructor(
412
		@IDebugService private debugService: IDebugService
I
isidor 已提交
413 414 415 416
	) {
		// noop
	}

417
	static readonly ID = 'functionbreakpoints';
418

I
isidor 已提交
419 420
	get templateId() {
		return FunctionBreakpointsRenderer.ID;
421 422
	}

I
isidor 已提交
423
	renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData {
I
isidor 已提交
424 425
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));
426

I
isidor 已提交
427
		data.icon = $('.icon');
428
		data.checkbox = createCheckbox();
I
isidor 已提交
429 430 431 432 433
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
434
		dom.append(data.breakpoint, data.icon);
I
isidor 已提交
435 436 437 438 439 440 441
		dom.append(data.breakpoint, data.checkbox);

		data.name = dom.append(data.breakpoint, $('span.name'));

		return data;
	}

I
isidor 已提交
442
	renderElement(functionBreakpoint: FunctionBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
443
		data.context = functionBreakpoint;
444
		data.name.textContent = functionBreakpoint.name;
445
		const { className, message } = getBreakpointMessageAndClassName(this.debugService, functionBreakpoint);
I
isidor 已提交
446 447
		data.icon.className = className + ' icon';
		data.icon.title = message ? message : '';
448 449 450 451
		data.checkbox.checked = functionBreakpoint.enabled;
		data.breakpoint.title = functionBreakpoint.name;

		// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
I
isidor 已提交
452
		const session = this.debugService.getViewModel().focusedSession;
A
Andre Weinand 已提交
453
		dom.toggleClass(data.breakpoint, 'disabled', (session && !session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
454
		if (session && !session.capabilities.supportsFunctionBreakpoints) {
455
			data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
I
isidor 已提交
456 457 458
		}
	}

J
Joao Moreno 已提交
459 460 461 462
	disposeElement(): void {
		// noop
	}

I
isidor 已提交
463
	disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
464
		dispose(templateData.toDispose);
465 466
	}
}
I
isidor 已提交
467

J
Joao Moreno 已提交
468
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IInputTemplateData> {
469 470 471 472 473 474 475 476 477

	constructor(
		private debugService: IDebugService,
		private contextViewService: IContextViewService,
		private themeService: IThemeService
	) {
		// noop
	}

478
	static readonly ID = 'functionbreakpointinput';
479 480 481 482 483 484 485

	get templateId() {
		return FunctionBreakpointInputRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IInputTemplateData {
		const template: IInputTemplateData = Object.create(null);
I
isidor 已提交
486 487 488 489 490 491 492 493

		const breakpoint = dom.append(container, $('.breakpoint'));
		template.icon = $('.icon');
		template.checkbox = createCheckbox();

		dom.append(breakpoint, template.icon);
		dom.append(breakpoint, template.checkbox);
		const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
494 495 496 497 498 499 500 501
		const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
			placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"),
			ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint")
		});
		const styler = attachInputBoxStyler(inputBox, this.themeService);
		const toDispose: IDisposable[] = [inputBox, styler];

		const wrapUp = (renamed: boolean) => {
I
isidor 已提交
502 503 504 505
			if (!template.reactedOnEvent) {
				template.reactedOnEvent = true;
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
				if (inputBox.value && (renamed || template.breakpoint.name)) {
506
					this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name);
I
isidor 已提交
507
				} else {
508
					this.debugService.removeFunctionBreakpoints(template.breakpoint.getId());
I
isidor 已提交
509
				}
510 511 512 513 514 515 516 517 518 519 520 521 522
			}
		};

		toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
			const isEscape = e.equals(KeyCode.Escape);
			const isEnter = e.equals(KeyCode.Enter);
			if (isEscape || isEnter) {
				e.preventDefault();
				e.stopPropagation();
				wrapUp(isEnter);
			}
		}));
		toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
523 524 525 526 527 528
			// Need to react with a timeout on the blur event due to possible concurent splices #56443
			setTimeout(() => {
				if (!template.breakpoint.name) {
					wrapUp(true);
				}
			});
529 530 531 532 533 534 535
		}));

		template.inputBox = inputBox;
		template.toDispose = toDispose;
		return template;
	}

I
isidor 已提交
536
	renderElement(functionBreakpoint: FunctionBreakpoint, index: number, data: IInputTemplateData): void {
537
		data.breakpoint = functionBreakpoint;
I
isidor 已提交
538
		data.reactedOnEvent = false;
I
isidor 已提交
539 540 541 542 543 544
		const { className, message } = getBreakpointMessageAndClassName(this.debugService, functionBreakpoint);

		data.icon.className = className + ' icon';
		data.icon.title = message ? message : '';
		data.checkbox.checked = functionBreakpoint.enabled;
		data.checkbox.disabled = true;
545
		data.inputBox.value = functionBreakpoint.name || '';
I
isidor 已提交
546 547 548 549
		setTimeout(() => {
			data.inputBox.focus();
			data.inputBox.select();
		}, 0);
550 551
	}

J
Joao Moreno 已提交
552 553 554 555
	disposeElement(): void {
		// noop
	}

556 557 558 559 560
	disposeTemplate(templateData: IInputTemplateData): void {
		dispose(templateData.toDispose);
	}
}

561
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Thenable<IEditor> {
I
isidor 已提交
562
	if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
I
isidor 已提交
563
		return Promise.resolve(null);
I
isidor 已提交
564 565 566 567 568 569 570 571 572 573 574 575 576 577
	}

	const selection = breakpoint.endLineNumber ? {
		startLineNumber: breakpoint.lineNumber,
		endLineNumber: breakpoint.endLineNumber,
		startColumn: breakpoint.column,
		endColumn: breakpoint.endColumn
	} : {
			startLineNumber: breakpoint.lineNumber,
			startColumn: breakpoint.column || 1,
			endLineNumber: breakpoint.lineNumber,
			endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
		};

I
isidor 已提交
578
	return editorService.openEditor({
I
isidor 已提交
579 580 581 582 583 584 585 586
		resource: breakpoint.uri,
		options: {
			preserveFocus,
			selection,
			revealIfVisible: true,
			revealInCenterIfOutsideViewport: true,
			pinned: !preserveFocus
		}
I
isidor 已提交
587
	}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
I
isidor 已提交
588
}
589

590
export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } {
591 592 593 594 595
	const state = debugService.state;
	const debugActive = state === State.Running || state === State.Stopped;

	if (!breakpoint.enabled || !debugService.getModel().areBreakpointsActivated()) {
		return {
I
isidor 已提交
596
			className: breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-disabled' : breakpoint.logMessage ? 'debug-breakpoint-log-disabled' : 'debug-breakpoint-disabled',
597
			message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled logpoint") : nls.localize('disabledBreakpoint', "Disabled breakpoint"),
598 599 600 601
		};
	}

	const appendMessage = (text: string): string => {
I
isidor 已提交
602
		return !(breakpoint instanceof FunctionBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text;
603 604 605
	};
	if (debugActive && !breakpoint.verified) {
		return {
I
isidor 已提交
606
			className: breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-unverified' : breakpoint.logMessage ? 'debug-breakpoint-log-unverified' : 'debug-breakpoint-unverified',
607
			message: breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified logpoint") : nls.localize('unverifiedBreakopint', "Unverified breakpoint"),
608 609 610
		};
	}

I
isidor 已提交
611
	const session = debugService.getViewModel().focusedSession;
612
	if (breakpoint instanceof FunctionBreakpoint) {
613
		if (session && !session.capabilities.supportsFunctionBreakpoints) {
614
			return {
615
				className: 'debug-function-breakpoint-unverified',
616 617 618 619
				message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
620
		return {
621
			className: 'debug-function-breakpoint',
I
isidor 已提交
622
		};
623 624
	}

I
isidor 已提交
625
	if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) {
M
Matt Bierner 已提交
626
		const messages: string[] = [];
I
isidor 已提交
627
		if (breakpoint.logMessage) {
628
			if (session && !session.capabilities.supportsLogPoints) {
I
isidor 已提交
629 630 631 632 633 634 635 636
				return {
					className: 'debug-breakpoint-unsupported',
					message: nls.localize('logBreakpointUnsupported', "Logpoints not supported by this debug type"),
				};
			}

			messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage));
		}
I
isidor 已提交
637

638
		if (session && breakpoint.condition && !session.capabilities.supportsConditionalBreakpoints) {
639
			return {
640
				className: 'debug-breakpoint-unsupported',
641 642 643
				message: nls.localize('conditionalBreakpointUnsupported', "Conditional breakpoints not supported by this debug type"),
			};
		}
644
		if (session && breakpoint.hitCondition && !session.capabilities.supportsHitConditionalBreakpoints) {
645
			return {
646
				className: 'debug-breakpoint-unsupported',
647 648 649 650
				message: nls.localize('hitBreakpointUnsupported', "Hit conditional breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
651 652 653 654 655
		if (breakpoint.condition) {
			messages.push(nls.localize('expression', "Expression: {0}", breakpoint.condition));
		}
		if (breakpoint.hitCondition) {
			messages.push(nls.localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
656 657 658
		}

		return {
I
isidor 已提交
659 660
			className: breakpoint.logMessage ? 'debug-breakpoint-log' : 'debug-breakpoint-conditional',
			message: appendMessage(messages.join('\n'))
661 662 663 664
		};
	}

	return {
665
		className: 'debug-breakpoint',
666 667 668
		message: breakpoint.message
	};
}