breakpointsView.ts 28.5 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';
I
isidor 已提交
39

40
const $ = dom.$;
I
isidor 已提交
41

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

	return checkbox;
}

51
const MAX_VISIBLE_BREAKPOINTS = 9;
52
export function getExpandedBodySize(model: IDebugModel): number {
53 54 55 56
	const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length;
	return Math.min(MAX_VISIBLE_BREAKPOINTS, length) * 22;
}

I
isidor 已提交
57
export class BreakpointsView extends ViewPane {
I
isidor 已提交
58

I
isidor 已提交
59 60
	private list!: WorkbenchList<IEnablement>;
	private needsRefresh = false;
I
isidor 已提交
61 62 63 64

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

79
		this.minimumBodySize = this.maximumBodySize = getExpandedBodySize(this.debugService.getModel());
M
Matt Bierner 已提交
80
		this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
I
isidor 已提交
81 82 83
	}

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

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

90
		this.list = <WorkbenchList<IEnablement>>this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [
I
isidor 已提交
91 92
			this.instantiationService.createInstance(BreakpointsRenderer),
			new ExceptionBreakpointsRenderer(this.debugService),
I
isidor 已提交
93
			this.instantiationService.createInstance(FunctionBreakpointsRenderer),
I
isidor 已提交
94
			this.instantiationService.createInstance(DataBreakpointsRenderer),
95
			new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
I
isidor 已提交
96
		], {
M
Matt Bierner 已提交
97 98 99 100 101 102 103 104
			identityProvider: { getId: (element: IEnablement) => element.getId() },
			multipleSelectionSupport: false,
			keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IEnablement) => e },
			ariaProvider: {
				getSetSize: (_: IEnablement, index: number, listLength: number) => listLength,
				getPosInSet: (_: IEnablement, index: number) => index,
				getRole: (breakpoint: IEnablement) => 'checkbox',
				isChecked: (breakpoint: IEnablement) => breakpoint.enabled
105 106
			},
			overrideStyles: {
S
SteVen Batten 已提交
107
				listBackground: this.getBackgroundColor()
M
Matt Bierner 已提交
108 109
			}
		});
J
Joao Moreno 已提交
110 111

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

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

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

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

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

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

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

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

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

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

168
	protected layoutBody(height: number, width: number): void {
I
isidor 已提交
169
		if (this.list) {
170
			this.list.layout(height, width);
I
isidor 已提交
171
		}
I
isidor 已提交
172 173 174
	}

	private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
J
Joao Moreno 已提交
175 176 177 178
		if (!e.element) {
			return;
		}

I
isidor 已提交
179
		const actions: IAction[] = [];
I
isidor 已提交
180 181
		const element = e.element;

182
		const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
I
isidor 已提交
183
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
I
isidor 已提交
184
			actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => {
I
isidor 已提交
185
				if (element instanceof Breakpoint) {
I
isidor 已提交
186 187 188 189 190
					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 已提交
191
						}
I
isidor 已提交
192 193 194 195
					}
				} else {
					this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
					this.onBreakpointsChange();
I
isidor 已提交
196
				}
I
isidor 已提交
197 198 199 200
			}));
			actions.push(new Separator());
		}

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

I
isidor 已提交
203 204 205
		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 已提交
206

I
isidor 已提交
207 208 209
			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 已提交
210

I
isidor 已提交
211 212
		actions.push(new Separator());
		actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
I
isidor 已提交
213

I
isidor 已提交
214 215
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
216
			getActions: () => actions,
217 218
			getActionsContext: () => element,
			onHide: () => dispose(actions)
I
isidor 已提交
219
		});
I
isidor 已提交
220 221 222 223 224 225 226 227 228 229
	}

	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 已提交
230
	private onBreakpointsChange(): void {
231
		if (this.isBodyVisible()) {
232
			this.minimumBodySize = getExpandedBodySize(this.debugService.getModel());
I
isidor 已提交
233 234 235 236 237 238 239 240 241
			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 已提交
242 243 244
		}
	}

I
isidor 已提交
245 246
	private get elements(): IEnablement[] {
		const model = this.debugService.getModel();
I
isidor 已提交
247
		const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints());
I
isidor 已提交
248 249 250

		return elements;
	}
I
isidor 已提交
251
}
252

J
Joao Moreno 已提交
253
class BreakpointsDelegate implements IListVirtualDelegate<IEnablement> {
254

255 256 257 258
	constructor(private debugService: IDebugService) {
		// noop
	}

I
isidor 已提交
259 260
	getHeight(element: IEnablement): number {
		return 22;
261 262
	}

I
isidor 已提交
263 264 265
	getTemplateId(element: IEnablement): string {
		if (element instanceof Breakpoint) {
			return BreakpointsRenderer.ID;
266
		}
I
isidor 已提交
267
		if (element instanceof FunctionBreakpoint) {
268
			const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
269 270 271 272
			if (!element.name || (selected && selected.getId() === element.getId())) {
				return FunctionBreakpointInputRenderer.ID;
			}

I
isidor 已提交
273 274 275 276
			return FunctionBreakpointsRenderer.ID;
		}
		if (element instanceof ExceptionBreakpoint) {
			return ExceptionBreakpointsRenderer.ID;
277
		}
I
isidor 已提交
278 279 280
		if (element instanceof DataBreakpoint) {
			return DataBreakpointsRenderer.ID;
		}
281

I
isidor 已提交
282
		return '';
283 284 285 286 287 288 289 290 291 292 293
	}
}

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

I
isidor 已提交
294 295 296 297 298
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
	icon: HTMLElement;
}

interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
299 300 301 302
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

303 304
interface IInputTemplateData {
	inputBox: InputBox;
I
isidor 已提交
305 306
	checkbox: HTMLInputElement;
	icon: HTMLElement;
307
	breakpoint: IFunctionBreakpoint;
I
isidor 已提交
308
	reactedOnEvent: boolean;
309 310 311
	toDispose: IDisposable[];
}

J
Joao Moreno 已提交
312
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
313 314

	constructor(
315 316
		@IDebugService private readonly debugService: IDebugService,
		@ILabelService private readonly labelService: ILabelService
317 318 319 320
	) {
		// noop
	}

321
	static readonly ID = 'breakpoints';
322

I
isidor 已提交
323 324
	get templateId() {
		return BreakpointsRenderer.ID;
325 326
	}

I
isidor 已提交
327
	renderTemplate(container: HTMLElement): IBreakpointTemplateData {
328 329 330
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));

I
isidor 已提交
331
		data.icon = $('.icon');
332
		data.checkbox = createCheckbox();
333 334 335 336 337
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
338
		dom.append(data.breakpoint, data.icon);
339 340 341 342
		dom.append(data.breakpoint, data.checkbox);

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

I
isidor 已提交
343 344 345
		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'));
346 347 348 349

		return data;
	}

I
isidor 已提交
350 351 352
	renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
		data.context = breakpoint;
		dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
353

I
isidor 已提交
354
		data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);
355 356 357 358
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
		if (breakpoint.column) {
			data.lineNumber.textContent += `:${breakpoint.column}`;
		}
359
		data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
360 361
		data.checkbox.checked = breakpoint.enabled;

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

366 367
		const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
		if (debugActive && !breakpoint.verified) {
I
isidor 已提交
368
			dom.addClass(data.breakpoint, 'disabled');
I
isidor 已提交
369
		}
370 371
	}

I
isidor 已提交
372
	disposeTemplate(templateData: IBreakpointTemplateData): void {
373 374 375 376
		dispose(templateData.toDispose);
	}
}

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

I
isidor 已提交
379 380 381
	constructor(
		private debugService: IDebugService
	) {
382 383 384
		// noop
	}

385
	static readonly ID = 'exceptionbreakpoints';
386

I
isidor 已提交
387 388 389 390 391 392 393 394
	get templateId() {
		return ExceptionBreakpointsRenderer.ID;
	}

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

395
		data.checkbox = createCheckbox();
I
isidor 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
		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);
418 419 420
	}
}

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

I
isidor 已提交
423
	constructor(
424
		@IDebugService private readonly debugService: IDebugService
I
isidor 已提交
425 426 427 428
	) {
		// noop
	}

429
	static readonly ID = 'functionbreakpoints';
430

I
isidor 已提交
431 432
	get templateId() {
		return FunctionBreakpointsRenderer.ID;
433 434
	}

I
isidor 已提交
435
	renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData {
I
isidor 已提交
436 437
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));
438

I
isidor 已提交
439
		data.icon = $('.icon');
440
		data.checkbox = createCheckbox();
I
isidor 已提交
441 442 443 444 445
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
446
		dom.append(data.breakpoint, data.icon);
I
isidor 已提交
447 448 449 450 451 452 453
		dom.append(data.breakpoint, data.checkbox);

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

		return data;
	}

454
	renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
455
		data.context = functionBreakpoint;
456
		data.name.textContent = functionBreakpoint.name;
457
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint);
M
Miguel Solorio 已提交
458
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
459
		data.icon.title = message ? message : '';
460
		data.checkbox.checked = functionBreakpoint.enabled;
I
isidor 已提交
461
		data.breakpoint.title = message ? message : '';
462 463

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

I
isidor 已提交
471
	disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
472
		dispose(templateData.toDispose);
473 474
	}
}
I
isidor 已提交
475

I
isidor 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
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;
	}

509
	renderElement(dataBreakpoint: DataBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
510
		data.context = dataBreakpoint;
511
		data.name.textContent = dataBreakpoint.description;
512
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint);
M
Miguel Solorio 已提交
513
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
514 515
		data.icon.title = message ? message : '';
		data.checkbox.checked = dataBreakpoint.enabled;
I
isidor 已提交
516
		data.breakpoint.title = message ? message : '';
I
isidor 已提交
517 518 519 520 521 522 523 524 525 526 527 528 529 530

		// 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 已提交
531
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IInputTemplateData> {
532 533 534 535 536 537 538 539 540

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

541
	static readonly ID = 'functionbreakpointinput';
542 543 544 545 546 547 548

	get templateId() {
		return FunctionBreakpointInputRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IInputTemplateData {
		const template: IInputTemplateData = Object.create(null);
I
isidor 已提交
549 550 551 552 553 554 555 556

		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'));
557 558 559 560 561 562 563 564
		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 已提交
565 566 567 568
			if (!template.reactedOnEvent) {
				template.reactedOnEvent = true;
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
				if (inputBox.value && (renamed || template.breakpoint.name)) {
569
					this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name);
I
isidor 已提交
570
				} else {
571
					this.debugService.removeFunctionBreakpoints(template.breakpoint.getId());
I
isidor 已提交
572
				}
573 574 575 576 577 578 579 580 581 582 583 584 585
			}
		};

		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', () => {
586 587 588 589 590 591
			// Need to react with a timeout on the blur event due to possible concurent splices #56443
			setTimeout(() => {
				if (!template.breakpoint.name) {
					wrapUp(true);
				}
			});
592 593 594 595 596 597 598
		}));

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

599
	renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IInputTemplateData): void {
600
		data.breakpoint = functionBreakpoint;
I
isidor 已提交
601
		data.reactedOnEvent = false;
602
		const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint);
I
isidor 已提交
603

M
Miguel Solorio 已提交
604
		data.icon.className = `codicon ${className}`;
I
isidor 已提交
605 606 607
		data.icon.title = message ? message : '';
		data.checkbox.checked = functionBreakpoint.enabled;
		data.checkbox.disabled = true;
608
		data.inputBox.value = functionBreakpoint.name || '';
I
isidor 已提交
609 610 611 612
		setTimeout(() => {
			data.inputBox.focus();
			data.inputBox.select();
		}, 0);
613 614 615 616 617 618 619
	}

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

620
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {
I
isidor 已提交
621
	if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
622
		return Promise.resolve(undefined);
I
isidor 已提交
623 624 625 626 627
	}

	const selection = breakpoint.endLineNumber ? {
		startLineNumber: breakpoint.lineNumber,
		endLineNumber: breakpoint.endLineNumber,
I
isidor 已提交
628 629
		startColumn: breakpoint.column || 1,
		endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER
I
isidor 已提交
630 631 632 633 634 635 636
	} : {
			startLineNumber: breakpoint.lineNumber,
			startColumn: breakpoint.column || 1,
			endLineNumber: breakpoint.lineNumber,
			endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
		};

I
isidor 已提交
637
	return editorService.openEditor({
I
isidor 已提交
638 639 640 641
		resource: breakpoint.uri,
		options: {
			preserveFocus,
			selection,
I
isidor 已提交
642
			revealIfOpened: true,
643
			selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
I
isidor 已提交
644 645
			pinned: !preserveFocus
		}
I
isidor 已提交
646
	}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
I
isidor 已提交
647
}
648

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

652
	if (!breakpoint.enabled || !breakpointsActivated) {
653
		return {
M
Miguel Solorio 已提交
654
			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 已提交
655
			message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"),
656 657 658 659
		};
	}

	const appendMessage = (text: string): string => {
660
		return ('message' in breakpoint && breakpoint.message) ? text.concat(', ' + breakpoint.message) : text;
661 662 663
	};
	if (debugActive && !breakpoint.verified) {
		return {
M
Miguel Solorio 已提交
664
			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',
665
			message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint")),
666 667 668 669
		};
	}

	if (breakpoint instanceof FunctionBreakpoint) {
670
		if (!breakpoint.supported) {
671
			return {
M
Miguel Solorio 已提交
672
				className: 'codicon-debug-breakpoint-function-unverified',
673 674 675 676
				message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
677
		return {
M
Miguel Solorio 已提交
678
			className: 'codicon-debug-breakpoint-function',
I
isidor 已提交
679
			message: breakpoint.message || nls.localize('functionBreakpoint', "Function Breakpoint")
I
isidor 已提交
680
		};
681 682
	}

I
isidor 已提交
683
	if (breakpoint instanceof DataBreakpoint) {
684
		if (!breakpoint.supported) {
I
isidor 已提交
685
			return {
M
Miguel Solorio 已提交
686
				className: 'codicon-debug-breakpoint-data-unverified',
I
isidor 已提交
687 688 689 690 691
				message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),
			};
		}

		return {
M
Miguel Solorio 已提交
692
			className: 'codicon-debug-breakpoint-data',
I
isidor 已提交
693
			message: breakpoint.message || nls.localize('dataBreakpoint', "Data Breakpoint")
I
isidor 已提交
694 695 696
		};
	}

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

700
		if (!breakpoint.supported) {
701
			return {
M
Miguel Solorio 已提交
702
				className: 'codicon-debug-breakpoint-unsupported',
703
				message: nls.localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"),
704 705 706
			};
		}

707 708 709
		if (breakpoint.logMessage) {
			messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage));
		}
I
isidor 已提交
710 711 712 713 714
		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));
715 716 717
		}

		return {
M
Miguel Solorio 已提交
718
			className: breakpoint.logMessage ? 'codicon-debug-breakpoint-log' : 'codicon-debug-breakpoint-conditional',
I
isidor 已提交
719
			message: appendMessage(messages.join('\n'))
720 721 722 723
		};
	}

	return {
M
Miguel Solorio 已提交
724
		className: 'codicon-debug-breakpoint',
725
		message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : nls.localize('breakpoint', "Breakpoint")
726 727
	};
}