breakpointsView.ts 30.0 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
import { IAction, Action } from 'vs/base/common/actions';
10
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IDebugModel, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
I
isidor 已提交
11
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
12
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/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
import { Constants } from 'vs/base/common/uint';
18 19
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 { IEditorPane } 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';
31
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
I
isidor 已提交
32
import { ILabelService } from 'vs/platform/label/common/label';
S
Sandeep Somavarapu 已提交
33
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
34
import { Gesture } from 'vs/base/browser/touch';
35
import { IViewDescriptorService } from 'vs/workbench/common/views';
36
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
37
import { IOpenerService } from 'vs/platform/opener/common/opener';
38
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
39
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
J
João Moreno 已提交
40
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
I
isidor 已提交
41

42
const $ = dom.$;
I
isidor 已提交
43

44 45 46 47
function createCheckbox(): HTMLInputElement {
	const checkbox = <HTMLInputElement>$('input');
	checkbox.type = 'checkbox';
	checkbox.tabIndex = -1;
48
	Gesture.ignoreTarget(checkbox);
49 50 51 52

	return checkbox;
}

53
const MAX_VISIBLE_BREAKPOINTS = 9;
54
export function getExpandedBodySize(model: IDebugModel, countLimit: number): number {
55
	const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length;
56
	return Math.min(countLimit, length) * 22;
57
}
58
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint;
59

I
isidor 已提交
60
export class BreakpointsView extends ViewPane {
I
isidor 已提交
61

62
	private list!: WorkbenchList<BreakpointItem>;
I
isidor 已提交
63
	private needsRefresh = false;
I
isidor 已提交
64
	private ignoreLayout = false;
I
isidor 已提交
65 66 67 68

	constructor(
		options: IViewletViewOptions,
		@IContextMenuService contextMenuService: IContextMenuService,
69
		@IDebugService private readonly debugService: IDebugService,
I
isidor 已提交
70
		@IKeybindingService keybindingService: IKeybindingService,
71
		@IInstantiationService instantiationService: IInstantiationService,
72
		@IThemeService themeService: IThemeService,
73 74
		@IEditorService private readonly editorService: IEditorService,
		@IContextViewService private readonly contextViewService: IContextViewService,
S
Sandeep Somavarapu 已提交
75
		@IConfigurationService configurationService: IConfigurationService,
76
		@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
S
Sandeep Somavarapu 已提交
77
		@IContextKeyService contextKeyService: IContextKeyService,
78
		@IOpenerService openerService: IOpenerService,
79
		@ITelemetryService telemetryService: ITelemetryService,
I
isidor 已提交
80
	) {
81
		super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
I
isidor 已提交
82

M
Matt Bierner 已提交
83
		this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
I
isidor 已提交
84 85 86
	}

	public renderBody(container: HTMLElement): void {
J
Joao Moreno 已提交
87 88
		super.renderBody(container);

I
isidor 已提交
89
		dom.addClass(this.element, 'debug-pane');
I
isidor 已提交
90
		dom.addClass(container, 'debug-breakpoints');
91
		const delegate = new BreakpointsDelegate(this.debugService);
I
isidor 已提交
92

93
		this.list = <WorkbenchList<BreakpointItem>>this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [
I
isidor 已提交
94 95
			this.instantiationService.createInstance(BreakpointsRenderer),
			new ExceptionBreakpointsRenderer(this.debugService),
I
isidor 已提交
96
			this.instantiationService.createInstance(FunctionBreakpointsRenderer),
I
isidor 已提交
97
			this.instantiationService.createInstance(DataBreakpointsRenderer),
98
			new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
I
isidor 已提交
99
		], {
M
Matt Bierner 已提交
100 101 102
			identityProvider: { getId: (element: IEnablement) => element.getId() },
			multipleSelectionSupport: false,
			keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IEnablement) => e },
103
			accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService),
104
			overrideStyles: {
S
SteVen Batten 已提交
105
				listBackground: this.getBackgroundColor()
M
Matt Bierner 已提交
106 107
			}
		});
J
Joao Moreno 已提交
108 109

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

M
Matt Bierner 已提交
111
		this._register(this.list.onContextMenu(this.onListContextMenu, this));
J
Joao Moreno 已提交
112

I
isidor 已提交
113
		this._register(this.list.onDidOpen(async e => {
114 115
			let isSingleClick = false;
			let isDoubleClick = false;
116
			let isMiddleClick = false;
117 118 119 120 121 122
			let openToSide = false;

			const browserEvent = e.browserEvent;
			if (browserEvent instanceof MouseEvent) {
				isSingleClick = browserEvent.detail === 1;
				isDoubleClick = browserEvent.detail === 2;
123
				isMiddleClick = browserEvent.button === 1;
124 125 126
				openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
			}

127 128
			const focused = this.list.getFocusedElements();
			const element = focused.length ? focused[0] : undefined;
129 130 131

			if (isMiddleClick) {
				if (element instanceof Breakpoint) {
I
isidor 已提交
132
					await this.debugService.removeBreakpoints(element.getId());
133
				} else if (element instanceof FunctionBreakpoint) {
I
isidor 已提交
134 135 136
					await this.debugService.removeFunctionBreakpoints(element.getId());
				} else if (element instanceof DataBreakpoint) {
					await this.debugService.removeDataBreakpoints(element.getId());
137 138 139 140
				}
				return;
			}

141
			if (element instanceof Breakpoint) {
142
				openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService);
143 144 145 146
			}
			if (isDoubleClick && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
				this.onBreakpointsChange();
147 148
			}
		}));
I
isidor 已提交
149

I
isidor 已提交
150
		this.list.splice(0, this.list.length, this.elements);
151

M
Matt Bierner 已提交
152
		this._register(this.onDidChangeBodyVisibility(visible => {
153 154 155 156
			if (visible && this.needsRefresh) {
				this.onBreakpointsChange();
			}
		}));
I
isidor 已提交
157
	}
I
isidor 已提交
158

I
isidor 已提交
159
	public focus(): void {
S
Sandeep Somavarapu 已提交
160
		super.focus();
I
isidor 已提交
161 162 163 164 165
		if (this.list) {
			this.list.domFocus();
		}
	}

166
	protected layoutBody(height: number, width: number): void {
I
isidor 已提交
167 168 169 170
		if (this.ignoreLayout) {
			return;
		}

J
João Moreno 已提交
171
		super.layoutBody(height, width);
I
isidor 已提交
172
		if (this.list) {
173
			this.list.layout(height, width);
I
isidor 已提交
174
		}
I
isidor 已提交
175 176 177 178 179 180
		try {
			this.ignoreLayout = true;
			this.updateSize();
		} finally {
			this.ignoreLayout = false;
		}
I
isidor 已提交
181 182 183
	}

	private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
J
Joao Moreno 已提交
184 185 186 187
		if (!e.element) {
			return;
		}

I
isidor 已提交
188
		const actions: IAction[] = [];
I
isidor 已提交
189 190
		const element = e.element;

191
		const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
I
isidor 已提交
192
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
I
isidor 已提交
193
			actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => {
I
isidor 已提交
194
				if (element instanceof Breakpoint) {
I
isidor 已提交
195 196 197 198 199
					const editor = await openBreakpointSource(element, false, false, this.debugService, this.editorService);
					if (editor) {
						const codeEditor = editor.getControl();
						if (isCodeEditor(codeEditor)) {
							codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
I
isidor 已提交
200
						}
I
isidor 已提交
201 202 203 204
					}
				} else {
					this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
					this.onBreakpointsChange();
I
isidor 已提交
205
				}
I
isidor 已提交
206 207 208 209
			}));
			actions.push(new Separator());
		}

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

I
isidor 已提交
212 213 214
		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 已提交
215

I
isidor 已提交
216 217 218
			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 已提交
219

I
isidor 已提交
220 221
		actions.push(new Separator());
		actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
I
isidor 已提交
222

I
isidor 已提交
223 224
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
225
			getActions: () => actions,
226 227
			getActionsContext: () => element,
			onHide: () => dispose(actions)
I
isidor 已提交
228
		});
I
isidor 已提交
229 230 231 232 233 234 235 236 237 238
	}

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

239 240
	private updateSize(): void {
		// Adjust expanded body size
241 242
		this.minimumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), MAX_VISIBLE_BREAKPOINTS) : 170;
		this.maximumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), Number.POSITIVE_INFINITY) : Number.POSITIVE_INFINITY;
243 244
	}

I
isidor 已提交
245
	private onBreakpointsChange(): void {
246
		if (this.isBodyVisible()) {
247
			this.updateSize();
I
isidor 已提交
248
			if (this.list) {
249 250
				const lastFocusIndex = this.list.getFocus()[0];
				// Check whether focused element was removed
251
				const needsRefocus = lastFocusIndex && !this.elements.includes(this.list.element(lastFocusIndex));
I
isidor 已提交
252 253
				this.list.splice(0, this.list.length, this.elements);
				this.needsRefresh = false;
254 255 256
				if (needsRefocus) {
					this.list.focusNth(Math.min(lastFocusIndex, this.list.length - 1));
				}
I
isidor 已提交
257 258 259
			}
		} else {
			this.needsRefresh = true;
I
isidor 已提交
260 261 262
		}
	}

263
	private get elements(): BreakpointItem[] {
I
isidor 已提交
264
		const model = this.debugService.getModel();
I
isidor 已提交
265
		const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints());
I
isidor 已提交
266

267
		return elements as BreakpointItem[];
I
isidor 已提交
268
	}
I
isidor 已提交
269
}
270

271
class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
272

273 274 275 276
	constructor(private debugService: IDebugService) {
		// noop
	}

277
	getHeight(_element: BreakpointItem): number {
I
isidor 已提交
278
		return 22;
279 280
	}

281
	getTemplateId(element: BreakpointItem): string {
I
isidor 已提交
282 283
		if (element instanceof Breakpoint) {
			return BreakpointsRenderer.ID;
284
		}
I
isidor 已提交
285
		if (element instanceof FunctionBreakpoint) {
286
			const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
287 288 289 290
			if (!element.name || (selected && selected.getId() === element.getId())) {
				return FunctionBreakpointInputRenderer.ID;
			}

I
isidor 已提交
291 292 293 294
			return FunctionBreakpointsRenderer.ID;
		}
		if (element instanceof ExceptionBreakpoint) {
			return ExceptionBreakpointsRenderer.ID;
295
		}
I
isidor 已提交
296 297 298
		if (element instanceof DataBreakpoint) {
			return DataBreakpointsRenderer.ID;
		}
299

I
isidor 已提交
300
		return '';
301 302 303 304 305 306 307
	}
}

interface IBaseBreakpointTemplateData {
	breakpoint: HTMLElement;
	name: HTMLElement;
	checkbox: HTMLInputElement;
308
	context: BreakpointItem;
309 310 311
	toDispose: IDisposable[];
}

I
isidor 已提交
312 313 314 315 316
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
	icon: HTMLElement;
}

interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
317 318 319 320
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

321 322
interface IInputTemplateData {
	inputBox: InputBox;
I
isidor 已提交
323 324
	checkbox: HTMLInputElement;
	icon: HTMLElement;
325
	breakpoint: IFunctionBreakpoint;
I
isidor 已提交
326
	reactedOnEvent: boolean;
327 328 329
	toDispose: IDisposable[];
}

J
Joao Moreno 已提交
330
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
331 332

	constructor(
333 334
		@IDebugService private readonly debugService: IDebugService,
		@ILabelService private readonly labelService: ILabelService
335 336 337 338
	) {
		// noop
	}

339
	static readonly ID = 'breakpoints';
340

I
isidor 已提交
341 342
	get templateId() {
		return BreakpointsRenderer.ID;
343 344
	}

I
isidor 已提交
345
	renderTemplate(container: HTMLElement): IBreakpointTemplateData {
346 347 348
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));

I
isidor 已提交
349
		data.icon = $('.icon');
350
		data.checkbox = createCheckbox();
351 352 353 354 355
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
356
		dom.append(data.breakpoint, data.icon);
357 358 359 360
		dom.append(data.breakpoint, data.checkbox);

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

I
isidor 已提交
361 362
		data.filePath = dom.append(data.breakpoint, $('span.file-path'));
		const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container'));
363
		data.lineNumber = dom.append(lineNumberContainer, $('span.line-number.monaco-count-badge'));
364 365 366 367

		return data;
	}

I
isidor 已提交
368 369 370
	renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
		data.context = breakpoint;
		dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
371

I
isidor 已提交
372
		data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);
373 374 375 376
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
		if (breakpoint.column) {
			data.lineNumber.textContent += `:${breakpoint.column}`;
		}
377
		data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
378 379
		data.checkbox.checked = breakpoint.enabled;

380
		const { message, className } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint);
M
Miguel Solorio 已提交
381
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
382
		data.breakpoint.title = breakpoint.message || message || '';
I
isidor 已提交
383

384 385
		const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
		if (debugActive && !breakpoint.verified) {
I
isidor 已提交
386
			dom.addClass(data.breakpoint, 'disabled');
I
isidor 已提交
387
		}
388 389
	}

I
isidor 已提交
390
	disposeTemplate(templateData: IBreakpointTemplateData): void {
391 392 393 394
		dispose(templateData.toDispose);
	}
}

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

I
isidor 已提交
397 398 399
	constructor(
		private debugService: IDebugService
	) {
400 401 402
		// noop
	}

403
	static readonly ID = 'exceptionbreakpoints';
404

I
isidor 已提交
405 406 407 408 409 410 411 412
	get templateId() {
		return ExceptionBreakpointsRenderer.ID;
	}

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

413
		data.checkbox = createCheckbox();
I
isidor 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
		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;
	}

	disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
		dispose(templateData.toDispose);
436 437 438
	}
}

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

I
isidor 已提交
441
	constructor(
442
		@IDebugService private readonly debugService: IDebugService
I
isidor 已提交
443 444 445 446
	) {
		// noop
	}

447
	static readonly ID = 'functionbreakpoints';
448

I
isidor 已提交
449 450
	get templateId() {
		return FunctionBreakpointsRenderer.ID;
451 452
	}

I
isidor 已提交
453
	renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData {
I
isidor 已提交
454 455
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));
456

I
isidor 已提交
457
		data.icon = $('.icon');
458
		data.checkbox = createCheckbox();
I
isidor 已提交
459 460 461 462 463
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
464
		dom.append(data.breakpoint, data.icon);
I
isidor 已提交
465 466 467 468 469 470 471
		dom.append(data.breakpoint, data.checkbox);

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

		return data;
	}

472
	renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
473
		data.context = functionBreakpoint;
474
		data.name.textContent = functionBreakpoint.name;
475
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint);
M
Miguel Solorio 已提交
476
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
477
		data.icon.title = message ? message : '';
478
		data.checkbox.checked = functionBreakpoint.enabled;
I
isidor 已提交
479
		data.breakpoint.title = message ? message : '';
480 481

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

I
isidor 已提交
489
	disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
490
		dispose(templateData.toDispose);
491 492
	}
}
I
isidor 已提交
493

I
isidor 已提交
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IBaseBreakpointWithIconTemplateData> {

	constructor(
		@IDebugService private readonly debugService: IDebugService
	) {
		// noop
	}

	static readonly ID = 'databreakpoints';

	get templateId() {
		return DataBreakpointsRenderer.ID;
	}

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

		data.icon = $('.icon');
		data.checkbox = createCheckbox();
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

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

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

		return data;
	}

527
	renderElement(dataBreakpoint: DataBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
528
		data.context = dataBreakpoint;
529
		data.name.textContent = dataBreakpoint.description;
530
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint);
M
Miguel Solorio 已提交
531
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
532 533
		data.icon.title = message ? message : '';
		data.checkbox.checked = dataBreakpoint.enabled;
I
isidor 已提交
534
		data.breakpoint.title = message ? message : '';
I
isidor 已提交
535 536 537 538 539 540 541 542 543 544 545 546 547 548

		// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
		const session = this.debugService.getViewModel().focusedSession;
		dom.toggleClass(data.breakpoint, 'disabled', (session && !session.capabilities.supportsDataBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
		if (session && !session.capabilities.supportsDataBreakpoints) {
			data.breakpoint.title = nls.localize('dataBreakpointsNotSupported', "Data breakpoints are not supported by this debug type");
		}
	}

	disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
		dispose(templateData.toDispose);
	}
}

J
Joao Moreno 已提交
549
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IInputTemplateData> {
550 551 552 553 554 555 556 557 558

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

559
	static readonly ID = 'functionbreakpointinput';
560 561 562 563 564 565 566

	get templateId() {
		return FunctionBreakpointInputRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IInputTemplateData {
		const template: IInputTemplateData = Object.create(null);
I
isidor 已提交
567 568 569 570 571 572 573 574

		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'));
575 576 577 578 579 580 581 582
		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 已提交
583 584 585 586
			if (!template.reactedOnEvent) {
				template.reactedOnEvent = true;
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
				if (inputBox.value && (renamed || template.breakpoint.name)) {
587
					this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name);
I
isidor 已提交
588
				} else {
589
					this.debugService.removeFunctionBreakpoints(template.breakpoint.getId());
I
isidor 已提交
590
				}
591 592 593 594 595 596 597 598 599 600 601 602 603
			}
		};

		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', () => {
604 605 606 607 608 609
			// Need to react with a timeout on the blur event due to possible concurent splices #56443
			setTimeout(() => {
				if (!template.breakpoint.name) {
					wrapUp(true);
				}
			});
610 611 612 613 614 615 616
		}));

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

617
	renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IInputTemplateData): void {
618
		data.breakpoint = functionBreakpoint;
I
isidor 已提交
619
		data.reactedOnEvent = false;
620
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint);
I
isidor 已提交
621

M
Miguel Solorio 已提交
622
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
623 624 625
		data.icon.title = message ? message : '';
		data.checkbox.checked = functionBreakpoint.enabled;
		data.checkbox.disabled = true;
626
		data.inputBox.value = functionBreakpoint.name || '';
I
isidor 已提交
627 628 629 630
		setTimeout(() => {
			data.inputBox.focus();
			data.inputBox.select();
		}, 0);
631 632 633 634 635 636 637
	}

	disposeTemplate(templateData: IInputTemplateData): void {
		dispose(templateData.toDispose);
	}
}

J
João Moreno 已提交
638
class BreakpointsAccessibilityProvider implements IListAccessibilityProvider<BreakpointItem> {
639 640 641

	constructor(private readonly debugService: IDebugService) { }

642 643 644 645
	getWidgetAriaLabel(): string {
		return nls.localize('breakpoints', "Breakpoints");
	}

J
João Moreno 已提交
646 647 648 649 650 651 652 653
	getRole() {
		return 'checkbox';
	}

	isChecked(breakpoint: IEnablement) {
		return breakpoint.enabled;
	}

654 655 656 657 658 659 660 661
	getAriaLabel(element: BreakpointItem): string | null {
		if (element instanceof ExceptionBreakpoint) {
			return element.toString();
		}

		const { message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint);
		const toString = element.toString();

I
isidor 已提交
662
		return message ? `${toString}, ${message}` : toString;
663 664 665
	}
}

666
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {
I
isidor 已提交
667
	if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
668
		return Promise.resolve(undefined);
I
isidor 已提交
669 670 671 672 673
	}

	const selection = breakpoint.endLineNumber ? {
		startLineNumber: breakpoint.lineNumber,
		endLineNumber: breakpoint.endLineNumber,
I
isidor 已提交
674 675
		startColumn: breakpoint.column || 1,
		endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER
I
isidor 已提交
676 677 678 679 680 681 682
	} : {
			startLineNumber: breakpoint.lineNumber,
			startColumn: breakpoint.column || 1,
			endLineNumber: breakpoint.lineNumber,
			endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
		};

I
isidor 已提交
683
	return editorService.openEditor({
I
isidor 已提交
684 685 686 687
		resource: breakpoint.uri,
		options: {
			preserveFocus,
			selection,
I
isidor 已提交
688
			revealIfOpened: true,
689
			selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
I
isidor 已提交
690 691
			pinned: !preserveFocus
		}
I
isidor 已提交
692
	}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
I
isidor 已提交
693
}
694

695
export function getBreakpointMessageAndClassName(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint): { message?: string, className: string } {
696 697
	const debugActive = state === State.Running || state === State.Stopped;

698
	if (!breakpoint.enabled || !breakpointsActivated) {
699
		return {
M
Miguel Solorio 已提交
700
			className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-disabled' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-disabled' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-disabled' : 'codicon-debug-breakpoint-disabled',
I
isidor 已提交
701
			message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"),
702 703 704 705
		};
	}

	const appendMessage = (text: string): string => {
706
		return ('message' in breakpoint && breakpoint.message) ? text.concat(', ' + breakpoint.message) : text;
707 708 709
	};
	if (debugActive && !breakpoint.verified) {
		return {
M
Miguel Solorio 已提交
710
			className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-unverified' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-unverified' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-unverified' : 'codicon-debug-breakpoint-unverified',
711
			message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint")),
712 713 714 715
		};
	}

	if (breakpoint instanceof FunctionBreakpoint) {
716
		if (!breakpoint.supported) {
717
			return {
M
Miguel Solorio 已提交
718
				className: 'codicon-debug-breakpoint-function-unverified',
719 720 721 722
				message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
723
		return {
M
Miguel Solorio 已提交
724
			className: 'codicon-debug-breakpoint-function',
I
isidor 已提交
725
			message: breakpoint.message || nls.localize('functionBreakpoint', "Function Breakpoint")
I
isidor 已提交
726
		};
727 728
	}

I
isidor 已提交
729
	if (breakpoint instanceof DataBreakpoint) {
730
		if (!breakpoint.supported) {
I
isidor 已提交
731
			return {
M
Miguel Solorio 已提交
732
				className: 'codicon-debug-breakpoint-data-unverified',
I
isidor 已提交
733 734 735 736 737
				message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),
			};
		}

		return {
M
Miguel Solorio 已提交
738
			className: 'codicon-debug-breakpoint-data',
I
isidor 已提交
739
			message: breakpoint.message || nls.localize('dataBreakpoint', "Data Breakpoint")
I
isidor 已提交
740 741 742
		};
	}

I
isidor 已提交
743
	if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) {
M
Matt Bierner 已提交
744
		const messages: string[] = [];
I
isidor 已提交
745

746
		if (!breakpoint.supported) {
747
			return {
M
Miguel Solorio 已提交
748
				className: 'codicon-debug-breakpoint-unsupported',
749
				message: nls.localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"),
750 751 752
			};
		}

753 754 755
		if (breakpoint.logMessage) {
			messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage));
		}
I
isidor 已提交
756 757 758 759 760
		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));
761 762 763
		}

		return {
M
Miguel Solorio 已提交
764
			className: breakpoint.logMessage ? 'codicon-debug-breakpoint-log' : 'codicon-debug-breakpoint-conditional',
I
isidor 已提交
765
			message: appendMessage(messages.join('\n'))
766 767 768 769
		};
	}

	return {
M
Miguel Solorio 已提交
770
		className: 'codicon-debug-breakpoint',
771
		message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : nls.localize('breakpoint', "Breakpoint")
772 773
	};
}