breakpointsView.ts 28.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, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } 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 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';
S
Sandeep Somavarapu 已提交
33
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
I
isidor 已提交
34

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

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

	return checkbox;
}

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

	private static readonly MAX_VISIBLE_FILES = 9;
I
isidor 已提交
48 49
	private list!: WorkbenchList<IEnablement>;
	private needsRefresh = false;
I
isidor 已提交
50 51 52 53

	constructor(
		options: IViewletViewOptions,
		@IContextMenuService contextMenuService: IContextMenuService,
54
		@IDebugService private readonly debugService: IDebugService,
I
isidor 已提交
55
		@IKeybindingService keybindingService: IKeybindingService,
56 57 58 59
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IThemeService private readonly themeService: IThemeService,
		@IEditorService private readonly editorService: IEditorService,
		@IContextViewService private readonly contextViewService: IContextViewService,
S
Sandeep Somavarapu 已提交
60 61
		@IConfigurationService configurationService: IConfigurationService,
		@IContextKeyService contextKeyService: IContextKeyService,
I
isidor 已提交
62
	) {
S
Sandeep Somavarapu 已提交
63
		super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService);
I
isidor 已提交
64 65

		this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
M
Matt Bierner 已提交
66
		this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
I
isidor 已提交
67 68 69 70
	}

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

J
Joao Moreno 已提交
73
		this.list = this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [
I
isidor 已提交
74 75
			this.instantiationService.createInstance(BreakpointsRenderer),
			new ExceptionBreakpointsRenderer(this.debugService),
I
isidor 已提交
76
			this.instantiationService.createInstance(FunctionBreakpointsRenderer),
I
isidor 已提交
77
			this.instantiationService.createInstance(DataBreakpointsRenderer),
78
			new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
I
isidor 已提交
79
		], {
M
Matt Bierner 已提交
80 81 82 83 84 85 86 87 88 89
			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
			}
		});
J
Joao Moreno 已提交
90 91

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

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

I
isidor 已提交
95
		this._register(this.list.onDidOpen(async e => {
96 97
			let isSingleClick = false;
			let isDoubleClick = false;
98
			let isMiddleClick = false;
99 100 101 102 103 104
			let openToSide = false;

			const browserEvent = e.browserEvent;
			if (browserEvent instanceof MouseEvent) {
				isSingleClick = browserEvent.detail === 1;
				isDoubleClick = browserEvent.detail === 2;
105
				isMiddleClick = browserEvent.button === 1;
106 107 108
				openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
			}

109 110
			const focused = this.list.getFocusedElements();
			const element = focused.length ? focused[0] : undefined;
111 112 113

			if (isMiddleClick) {
				if (element instanceof Breakpoint) {
I
isidor 已提交
114
					await this.debugService.removeBreakpoints(element.getId());
115
				} else if (element instanceof FunctionBreakpoint) {
I
isidor 已提交
116 117 118
					await this.debugService.removeFunctionBreakpoints(element.getId());
				} else if (element instanceof DataBreakpoint) {
					await this.debugService.removeDataBreakpoints(element.getId());
119 120 121 122
				}
				return;
			}

123
			if (element instanceof Breakpoint) {
124
				openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService);
125 126 127 128
			}
			if (isDoubleClick && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
				this.onBreakpointsChange();
129 130
			}
		}));
I
isidor 已提交
131

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

M
Matt Bierner 已提交
134
		this._register(this.onDidChangeBodyVisibility(visible => {
135 136 137 138
			if (visible && this.needsRefresh) {
				this.onBreakpointsChange();
			}
		}));
I
isidor 已提交
139
	}
I
isidor 已提交
140

I
isidor 已提交
141
	public focus(): void {
S
Sandeep Somavarapu 已提交
142
		super.focus();
I
isidor 已提交
143 144 145 146 147
		if (this.list) {
			this.list.domFocus();
		}
	}

148
	protected layoutBody(height: number, width: number): void {
I
isidor 已提交
149
		if (this.list) {
150
			this.list.layout(height, width);
I
isidor 已提交
151
		}
I
isidor 已提交
152 153 154
	}

	private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
J
Joao Moreno 已提交
155 156 157 158
		if (!e.element) {
			return;
		}

I
isidor 已提交
159
		const actions: IAction[] = [];
I
isidor 已提交
160 161
		const element = e.element;

162
		const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
I
isidor 已提交
163
		if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
I
isidor 已提交
164
			actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, () => {
I
isidor 已提交
165 166
				if (element instanceof Breakpoint) {
					return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
167 168 169 170 171
						if (editor) {
							const codeEditor = editor.getControl();
							if (isCodeEditor(codeEditor)) {
								codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
							}
I
isidor 已提交
172 173 174 175 176 177
						}
					});
				}

				this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
				this.onBreakpointsChange();
I
isidor 已提交
178
				return Promise.resolve(undefined);
I
isidor 已提交
179 180 181 182
			}));
			actions.push(new Separator());
		}

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

I
isidor 已提交
185 186 187
		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 已提交
188

I
isidor 已提交
189 190 191
			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 已提交
192

I
isidor 已提交
193 194
		actions.push(new Separator());
		actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
I
isidor 已提交
195

I
isidor 已提交
196 197
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
198
			getActions: () => actions,
I
isidor 已提交
199
			getActionsContext: () => element
I
isidor 已提交
200
		});
I
isidor 已提交
201 202 203 204 205 206 207 208 209 210
	}

	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 已提交
211
	private onBreakpointsChange(): void {
212
		if (this.isBodyVisible()) {
I
isidor 已提交
213 214 215 216 217 218 219 220 221 222
			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 已提交
223 224 225
		}
	}

I
isidor 已提交
226 227
	private get elements(): IEnablement[] {
		const model = this.debugService.getModel();
I
isidor 已提交
228
		const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints());
I
isidor 已提交
229 230 231 232

		return elements;
	}

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

J
Joao Moreno 已提交
240
class BreakpointsDelegate implements IListVirtualDelegate<IEnablement> {
241

242 243 244 245
	constructor(private debugService: IDebugService) {
		// noop
	}

I
isidor 已提交
246 247
	getHeight(element: IEnablement): number {
		return 22;
248 249
	}

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

I
isidor 已提交
260 261 262 263
			return FunctionBreakpointsRenderer.ID;
		}
		if (element instanceof ExceptionBreakpoint) {
			return ExceptionBreakpointsRenderer.ID;
264
		}
I
isidor 已提交
265 266 267
		if (element instanceof DataBreakpoint) {
			return DataBreakpointsRenderer.ID;
		}
268

I
isidor 已提交
269
		return '';
270 271 272 273 274 275 276 277 278 279 280
	}
}

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

I
isidor 已提交
281 282 283 284 285
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
	icon: HTMLElement;
}

interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
286 287 288 289
	lineNumber: HTMLElement;
	filePath: HTMLElement;
}

290 291
interface IInputTemplateData {
	inputBox: InputBox;
I
isidor 已提交
292 293
	checkbox: HTMLInputElement;
	icon: HTMLElement;
294
	breakpoint: IFunctionBreakpoint;
I
isidor 已提交
295
	reactedOnEvent: boolean;
296 297 298
	toDispose: IDisposable[];
}

J
Joao Moreno 已提交
299
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
300 301

	constructor(
302 303
		@IDebugService private readonly debugService: IDebugService,
		@ILabelService private readonly labelService: ILabelService
304 305 306 307
	) {
		// noop
	}

308
	static readonly ID = 'breakpoints';
309

I
isidor 已提交
310 311
	get templateId() {
		return BreakpointsRenderer.ID;
312 313
	}

I
isidor 已提交
314
	renderTemplate(container: HTMLElement): IBreakpointTemplateData {
315 316 317
		const data: IBreakpointTemplateData = Object.create(null);
		data.breakpoint = dom.append(container, $('.breakpoint'));

I
isidor 已提交
318
		data.icon = $('.icon');
319
		data.checkbox = createCheckbox();
320 321 322 323 324
		data.toDispose = [];
		data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
			this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
		}));

I
isidor 已提交
325
		dom.append(data.breakpoint, data.icon);
326 327 328 329
		dom.append(data.breakpoint, data.checkbox);

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

I
isidor 已提交
330 331 332
		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'));
333 334 335 336

		return data;
	}

I
isidor 已提交
337 338 339
	renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
		data.context = breakpoint;
		dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
340

I
isidor 已提交
341
		data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);
342 343 344 345
		data.lineNumber.textContent = breakpoint.lineNumber.toString();
		if (breakpoint.column) {
			data.lineNumber.textContent += `:${breakpoint.column}`;
		}
346
		data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
347 348
		data.checkbox.checked = breakpoint.enabled;

349
		const { message, className } = getBreakpointMessageAndClassName(this.debugService, breakpoint);
I
isidor 已提交
350
		data.icon.className = className + ' icon';
I
isidor 已提交
351
		data.breakpoint.title = breakpoint.message || message || '';
I
isidor 已提交
352

353 354
		const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
		if (debugActive && !breakpoint.verified) {
I
isidor 已提交
355
			dom.addClass(data.breakpoint, 'disabled');
I
isidor 已提交
356
		}
357 358
	}

I
isidor 已提交
359
	disposeTemplate(templateData: IBreakpointTemplateData): void {
360 361 362 363
		dispose(templateData.toDispose);
	}
}

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

I
isidor 已提交
366 367 368
	constructor(
		private debugService: IDebugService
	) {
369 370 371
		// noop
	}

372
	static readonly ID = 'exceptionbreakpoints';
373

I
isidor 已提交
374 375 376 377 378 379 380 381
	get templateId() {
		return ExceptionBreakpointsRenderer.ID;
	}

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

382
		data.checkbox = createCheckbox();
I
isidor 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
		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);
405 406 407
	}
}

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

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

416
	static readonly ID = 'functionbreakpoints';
417

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

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

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

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

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

		return data;
	}

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

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

I
isidor 已提交
458
	disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
I
isidor 已提交
459
		dispose(templateData.toDispose);
460 461
	}
}
I
isidor 已提交
462

I
isidor 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 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
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;
	}

	renderElement(dataBreakpoint: DataBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void {
		data.context = dataBreakpoint;
		data.name.textContent = dataBreakpoint.label;
		const { className, message } = getBreakpointMessageAndClassName(this.debugService, dataBreakpoint);
		data.icon.className = className + ' icon';
		data.icon.title = message ? message : '';
		data.checkbox.checked = dataBreakpoint.enabled;
I
isidor 已提交
503
		data.breakpoint.title = message ? message : '';
I
isidor 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517

		// 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 已提交
518
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IInputTemplateData> {
519 520 521 522 523 524 525 526 527

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

528
	static readonly ID = 'functionbreakpointinput';
529 530 531 532 533 534 535

	get templateId() {
		return FunctionBreakpointInputRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IInputTemplateData {
		const template: IInputTemplateData = Object.create(null);
I
isidor 已提交
536 537 538 539 540 541 542 543

		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'));
544 545 546 547 548 549 550 551
		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 已提交
552 553 554 555
			if (!template.reactedOnEvent) {
				template.reactedOnEvent = true;
				this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
				if (inputBox.value && (renamed || template.breakpoint.name)) {
556
					this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name);
I
isidor 已提交
557
				} else {
558
					this.debugService.removeFunctionBreakpoints(template.breakpoint.getId());
I
isidor 已提交
559
				}
560 561 562 563 564 565 566 567 568 569 570 571 572
			}
		};

		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', () => {
573 574 575 576 577 578
			// Need to react with a timeout on the blur event due to possible concurent splices #56443
			setTimeout(() => {
				if (!template.breakpoint.name) {
					wrapUp(true);
				}
			});
579 580 581 582 583 584 585
		}));

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

I
isidor 已提交
586
	renderElement(functionBreakpoint: FunctionBreakpoint, index: number, data: IInputTemplateData): void {
587
		data.breakpoint = functionBreakpoint;
I
isidor 已提交
588
		data.reactedOnEvent = false;
I
isidor 已提交
589 590 591 592 593 594
		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;
595
		data.inputBox.value = functionBreakpoint.name || '';
I
isidor 已提交
596 597 598 599
		setTimeout(() => {
			data.inputBox.focus();
			data.inputBox.select();
		}, 0);
600 601 602 603 604 605 606
	}

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

607
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditor | undefined> {
I
isidor 已提交
608
	if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
609
		return Promise.resolve(undefined);
I
isidor 已提交
610 611 612 613 614
	}

	const selection = breakpoint.endLineNumber ? {
		startLineNumber: breakpoint.lineNumber,
		endLineNumber: breakpoint.endLineNumber,
I
isidor 已提交
615 616
		startColumn: breakpoint.column || 1,
		endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER
I
isidor 已提交
617 618 619 620 621 622 623
	} : {
			startLineNumber: breakpoint.lineNumber,
			startColumn: breakpoint.column || 1,
			endLineNumber: breakpoint.lineNumber,
			endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
		};

I
isidor 已提交
624
	return editorService.openEditor({
I
isidor 已提交
625 626 627 628
		resource: breakpoint.uri,
		options: {
			preserveFocus,
			selection,
I
isidor 已提交
629
			revealIfOpened: true,
I
isidor 已提交
630 631 632
			revealInCenterIfOutsideViewport: true,
			pinned: !preserveFocus
		}
I
isidor 已提交
633
	}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
I
isidor 已提交
634
}
635

I
isidor 已提交
636
export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint | DataBreakpoint): { message?: string, className: string } {
637 638 639 640 641
	const state = debugService.state;
	const debugActive = state === State.Running || state === State.Stopped;

	if (!breakpoint.enabled || !debugService.getModel().areBreakpointsActivated()) {
		return {
I
isidor 已提交
642
			className: breakpoint instanceof DataBreakpoint ? 'debug-data-breakpoint-disabled' : breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-disabled' : breakpoint.logMessage ? 'debug-breakpoint-log-disabled' : 'debug-breakpoint-disabled',
I
isidor 已提交
643
			message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"),
644 645 646 647
		};
	}

	const appendMessage = (text: string): string => {
I
isidor 已提交
648
		return !(breakpoint instanceof FunctionBreakpoint) && !(breakpoint instanceof DataBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text;
649 650 651
	};
	if (debugActive && !breakpoint.verified) {
		return {
I
isidor 已提交
652
			className: breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-unverified' : breakpoint.logMessage ? 'debug-breakpoint-log-unverified' : 'debug-breakpoint-unverified',
I
isidor 已提交
653
			message: breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint"),
654 655 656
		};
	}

I
isidor 已提交
657
	const session = debugService.getViewModel().focusedSession;
658
	if (breakpoint instanceof FunctionBreakpoint) {
659
		if (session && !session.capabilities.supportsFunctionBreakpoints) {
660
			return {
661
				className: 'debug-function-breakpoint-unverified',
662 663 664 665
				message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
666
		return {
667
			className: 'debug-function-breakpoint',
I
isidor 已提交
668
			message: nls.localize('functionBreakpoint', "Function Breakpoint")
I
isidor 已提交
669
		};
670 671
	}

I
isidor 已提交
672 673 674 675 676 677 678 679 680 681
	if (breakpoint instanceof DataBreakpoint) {
		if (session && !session.capabilities.supportsDataBreakpoints) {
			return {
				className: 'debug-data-breakpoint-unverified',
				message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),
			};
		}

		return {
			className: 'debug-data-breakpoint',
I
isidor 已提交
682
			message: nls.localize('dataBreakpoint', "Data Breakpoint")
I
isidor 已提交
683 684 685
		};
	}

I
isidor 已提交
686
	if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) {
M
Matt Bierner 已提交
687
		const messages: string[] = [];
I
isidor 已提交
688
		if (breakpoint.logMessage) {
689
			if (session && !session.capabilities.supportsLogPoints) {
I
isidor 已提交
690 691 692 693 694 695 696 697
				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 已提交
698

699
		if (session && breakpoint.condition && !session.capabilities.supportsConditionalBreakpoints) {
700
			return {
701
				className: 'debug-breakpoint-unsupported',
702 703 704
				message: nls.localize('conditionalBreakpointUnsupported', "Conditional breakpoints not supported by this debug type"),
			};
		}
705
		if (session && breakpoint.hitCondition && !session.capabilities.supportsHitConditionalBreakpoints) {
706
			return {
707
				className: 'debug-breakpoint-unsupported',
708 709 710 711
				message: nls.localize('hitBreakpointUnsupported', "Hit conditional breakpoints not supported by this debug type"),
			};
		}

I
isidor 已提交
712 713 714 715 716
		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));
717 718 719
		}

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

	return {
726
		className: 'debug-breakpoint',
I
isidor 已提交
727
		message: breakpoint.message || nls.localize('breakpoint', "Breakpoint")
728 729
	};
}