Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts
5221 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../../../base/browser/dom.js';
7
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
8
import { Button } from '../../../../base/browser/ui/button/button.js';
9
import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';
10
import { ISelectOptionItem, SelectBox } from '../../../../base/browser/ui/selectBox/selectBox.js';
11
import { CancellationToken } from '../../../../base/common/cancellation.js';
12
import { onUnexpectedError } from '../../../../base/common/errors.js';
13
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
14
import * as lifecycle from '../../../../base/common/lifecycle.js';
15
import { URI as uri } from '../../../../base/common/uri.js';
16
import { IActiveCodeEditor, ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
17
import { EditorCommand, ServicesAccessor, registerEditorCommand } from '../../../../editor/browser/editorExtensions.js';
18
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
19
import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js';
20
import { EditorOption, IEditorOptions } from '../../../../editor/common/config/editorOptions.js';
21
import { IPosition, Position } from '../../../../editor/common/core/position.js';
22
import { IRange, Range } from '../../../../editor/common/core/range.js';
23
import { IDecorationOptions } from '../../../../editor/common/editorCommon.js';
24
import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';
25
import { CompletionContext, CompletionItemKind, CompletionList } from '../../../../editor/common/languages.js';
26
import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js';
27
import { ITextModel } from '../../../../editor/common/model.js';
28
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
29
import { IModelService } from '../../../../editor/common/services/model.js';
30
import { ITextModelService } from '../../../../editor/common/services/resolverService.js';
31
import { CompletionOptions, provideSuggestionItems } from '../../../../editor/contrib/suggest/browser/suggest.js';
32
import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js';
33
import * as nls from '../../../../nls.js';
34
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
35
import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js';
36
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
37
import { IInstantiationService, createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
38
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
39
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
40
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
41
import { ILabelService } from '../../../../platform/label/common/label.js';
42
import { defaultButtonStyles, defaultSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
43
import { editorForeground } from '../../../../platform/theme/common/colorRegistry.js';
44
import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js';
45
import { hasNativeContextMenu } from '../../../../platform/window/common/window.js';
46
import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from '../../codeEditor/browser/simpleEditorOptions.js';
47
import { BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, CONTEXT_IN_BREAKPOINT_WIDGET, BreakpointWidgetContext as Context, DEBUG_SCHEME, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugService } from '../common/debug.js';
48
import './media/breakpointWidget.css';
49
50
const $ = dom.$;
51
const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakpointWidgetService');
52
interface IPrivateBreakpointWidgetService {
53
readonly _serviceBrand: undefined;
54
close(success: boolean): void;
55
}
56
const DECORATION_KEY = 'breakpointwidgetdecoration';
57
58
function isPositionInCurlyBracketBlock(input: IActiveCodeEditor): boolean {
59
const model = input.getModel();
60
const bracketPairs = model.bracketPairs.getBracketPairsInRange(Range.fromPositions(input.getPosition()));
61
return bracketPairs.some(p => p.openingBracketInfo.bracketText === '{');
62
}
63
64
function createDecorations(theme: IColorTheme, placeHolder: string): IDecorationOptions[] {
65
const transparentForeground = theme.getColor(editorForeground)?.transparent(0.4);
66
return [{
67
range: {
68
startLineNumber: 0,
69
endLineNumber: 0,
70
startColumn: 0,
71
endColumn: 1
72
},
73
renderOptions: {
74
after: {
75
contentText: placeHolder,
76
color: transparentForeground ? transparentForeground.toString() : undefined
77
}
78
}
79
}];
80
}
81
82
export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWidgetService {
83
declare readonly _serviceBrand: undefined;
84
85
private selectContainer!: HTMLElement;
86
private inputContainer!: HTMLElement;
87
private selectBreakpointContainer!: HTMLElement;
88
private input!: IActiveCodeEditor;
89
private selectBreakpointBox!: SelectBox;
90
private selectModeBox?: SelectBox;
91
private store: lifecycle.DisposableStore;
92
private conditionInput = '';
93
private hitCountInput = '';
94
private logMessageInput = '';
95
private modeInput?: DebugProtocol.BreakpointMode;
96
private breakpoint: IBreakpoint | undefined;
97
private context: Context;
98
private heightInPx: number | undefined;
99
private triggeredByBreakpointInput: IBreakpoint | undefined;
100
private availableBreakpoints: IBreakpoint[] = [];
101
102
constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, context: Context | undefined,
103
@IContextViewService private readonly contextViewService: IContextViewService,
104
@IDebugService private readonly debugService: IDebugService,
105
@IThemeService private readonly themeService: IThemeService,
106
@IInstantiationService private readonly instantiationService: IInstantiationService,
107
@IModelService private readonly modelService: IModelService,
108
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
109
@IConfigurationService private readonly _configurationService: IConfigurationService,
110
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
111
@IKeybindingService private readonly keybindingService: IKeybindingService,
112
@ILabelService private readonly labelService: ILabelService,
113
@ITextModelService private readonly textModelService: ITextModelService,
114
@IHoverService private readonly hoverService: IHoverService
115
) {
116
super(editor, { showFrame: true, showArrow: false, frameWidth: 1, isAccessible: true });
117
118
this.store = new lifecycle.DisposableStore();
119
const model = this.editor.getModel();
120
if (model) {
121
const uri = model.uri;
122
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, column: this.column, uri });
123
this.breakpoint = breakpoints.length ? breakpoints[0] : undefined;
124
}
125
126
if (context === undefined) {
127
if (this.breakpoint && !this.breakpoint.condition && !this.breakpoint.hitCondition && this.breakpoint.logMessage) {
128
this.context = Context.LOG_MESSAGE;
129
} else if (this.breakpoint && !this.breakpoint.condition && this.breakpoint.hitCondition) {
130
this.context = Context.HIT_COUNT;
131
} else if (this.breakpoint && this.breakpoint.triggeredBy) {
132
this.context = Context.TRIGGER_POINT;
133
} else {
134
this.context = Context.CONDITION;
135
}
136
} else {
137
this.context = context;
138
}
139
140
this.store.add(this.debugService.getModel().onDidChangeBreakpoints(e => {
141
if (this.breakpoint && e && e.removed && e.removed.indexOf(this.breakpoint) >= 0) {
142
this.dispose();
143
}
144
// Update the breakpoint list when in trigger point context
145
if (this.context === Context.TRIGGER_POINT && this.selectBreakpointBox) {
146
this.updateTriggerBreakpointList();
147
}
148
}));
149
this.store.add(this.codeEditorService.registerDecorationType('breakpoint-widget', DECORATION_KEY, {}));
150
151
this.create();
152
}
153
154
private get placeholder(): string {
155
const acceptString = this.keybindingService.lookupKeybinding(AcceptBreakpointWidgetInputAction.ID)?.getLabel() || 'Enter';
156
const closeString = this.keybindingService.lookupKeybinding(CloseBreakpointWidgetCommand.ID)?.getLabel() || 'Escape';
157
switch (this.context) {
158
case Context.LOG_MESSAGE:
159
return nls.localize('breakpointWidgetLogMessagePlaceholder', "Message to log when breakpoint is hit. Expressions within {} are interpolated. '{0}' to accept, '{1}' to cancel.", acceptString, closeString);
160
case Context.HIT_COUNT:
161
return nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. '{0}' to accept, '{1}' to cancel.", acceptString, closeString);
162
default:
163
return nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. '{0}' to accept, '{1}' to cancel.", acceptString, closeString);
164
}
165
}
166
167
private getInputValue(breakpoint: IBreakpoint | undefined): string {
168
switch (this.context) {
169
case Context.LOG_MESSAGE:
170
return breakpoint && breakpoint.logMessage ? breakpoint.logMessage : this.logMessageInput;
171
case Context.HIT_COUNT:
172
return breakpoint && breakpoint.hitCondition ? breakpoint.hitCondition : this.hitCountInput;
173
default:
174
return breakpoint && breakpoint.condition ? breakpoint.condition : this.conditionInput;
175
}
176
}
177
178
private rememberInput(): void {
179
if (this.context !== Context.TRIGGER_POINT) {
180
const value = this.input.getModel().getValue();
181
switch (this.context) {
182
case Context.LOG_MESSAGE:
183
this.logMessageInput = value;
184
break;
185
case Context.HIT_COUNT:
186
this.hitCountInput = value;
187
break;
188
default:
189
this.conditionInput = value;
190
}
191
}
192
}
193
194
private setInputMode(): void {
195
if (this.editor.hasModel()) {
196
// Use plaintext language for log messages, otherwise respect underlying editor language #125619
197
const languageId = this.context === Context.LOG_MESSAGE ? PLAINTEXT_LANGUAGE_ID : this.editor.getModel().getLanguageId();
198
this.input.getModel().setLanguage(languageId);
199
}
200
}
201
202
override show(rangeOrPos: IRange | IPosition): void {
203
const lineNum = this.input.getModel().getLineCount();
204
super.show(rangeOrPos, lineNum + 1);
205
}
206
207
fitHeightToContent(): void {
208
const lineNum = this.input.getModel().getLineCount();
209
this._relayout(lineNum + 1);
210
}
211
212
protected _fillContainer(container: HTMLElement): void {
213
this.setCssClass('breakpoint-widget');
214
const selectBox = this.store.add(new SelectBox([
215
{ text: nls.localize('expression', "Expression") },
216
{ text: nls.localize('hitCount', "Hit Count") },
217
{ text: nls.localize('logMessage', "Log Message") },
218
{ text: nls.localize('triggeredBy', "Wait for Breakpoint") },
219
] satisfies ISelectOptionItem[], this.context, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('breakpointType', 'Breakpoint Type'), useCustomDrawn: !hasNativeContextMenu(this._configurationService) }));
220
this.selectContainer = $('.breakpoint-select-container');
221
selectBox.render(dom.append(container, this.selectContainer));
222
this.store.add(selectBox.onDidSelect(e => {
223
this.rememberInput();
224
this.context = e.index;
225
this.updateContextInput();
226
}));
227
228
this.createModesInput(container);
229
230
this.inputContainer = $('.inputContainer');
231
this.store.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.inputContainer, this.placeholder));
232
this.createBreakpointInput(dom.append(container, this.inputContainer));
233
234
this.input.getModel().setValue(this.getInputValue(this.breakpoint));
235
this.store.add(this.input.getModel().onDidChangeContent(() => {
236
this.fitHeightToContent();
237
}));
238
this.input.setPosition({ lineNumber: 1, column: this.input.getModel().getLineMaxColumn(1) });
239
240
this.createTriggerBreakpointInput(container);
241
242
this.updateContextInput();
243
// Due to an electron bug we have to do the timeout, otherwise we do not get focus
244
setTimeout(() => this.focusInput(), 150);
245
}
246
247
private createModesInput(container: HTMLElement) {
248
const modes = this.debugService.getModel().getBreakpointModes('source');
249
if (modes.length <= 1) {
250
return;
251
}
252
253
const sb = this.selectModeBox = new SelectBox(
254
[
255
{ text: nls.localize('bpMode', 'Mode'), isDisabled: true },
256
...modes.map(mode => ({ text: mode.label, description: mode.description })),
257
],
258
modes.findIndex(m => m.mode === this.breakpoint?.mode) + 1,
259
this.contextViewService,
260
defaultSelectBoxStyles,
261
{ useCustomDrawn: !hasNativeContextMenu(this._configurationService) }
262
);
263
this.store.add(sb);
264
this.store.add(sb.onDidSelect(e => {
265
this.modeInput = modes[e.index - 1];
266
}));
267
268
const modeWrapper = $('.select-mode-container');
269
const selectionWrapper = $('.select-box-container');
270
dom.append(modeWrapper, selectionWrapper);
271
sb.render(selectionWrapper);
272
dom.append(container, modeWrapper);
273
}
274
275
private createTriggerBreakpointInput(container: HTMLElement) {
276
this.availableBreakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint && !bp.logMessage);
277
const breakpointOptions = this.buildBreakpointOptions();
278
279
const index = this.availableBreakpoints.findIndex((bp) => this.breakpoint?.triggeredBy === bp.getId());
280
281
const selectBreakpointBox = this.selectBreakpointBox = this.store.add(new SelectBox(breakpointOptions, index + 1, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint'), useCustomDrawn: !hasNativeContextMenu(this._configurationService) }));
282
this.store.add(selectBreakpointBox.onDidSelect(e => {
283
if (e.index === 0) {
284
this.triggeredByBreakpointInput = undefined;
285
} else {
286
this.triggeredByBreakpointInput = this.availableBreakpoints[e.index - 1];
287
}
288
}));
289
this.selectBreakpointContainer = $('.select-breakpoint-container');
290
this.store.add(dom.addDisposableListener(this.selectBreakpointContainer, dom.EventType.KEY_DOWN, e => {
291
const event = new StandardKeyboardEvent(e);
292
if (event.equals(KeyCode.Escape)) {
293
this.close(false);
294
}
295
}));
296
297
const selectionWrapper = $('.select-box-container');
298
dom.append(this.selectBreakpointContainer, selectionWrapper);
299
selectBreakpointBox.render(selectionWrapper);
300
301
dom.append(container, this.selectBreakpointContainer);
302
303
const closeButton = new Button(this.selectBreakpointContainer, defaultButtonStyles);
304
closeButton.label = nls.localize('ok', "OK");
305
this.store.add(closeButton.onDidClick(() => this.close(true)));
306
this.store.add(closeButton);
307
}
308
309
private buildBreakpointOptions(): ISelectOptionItem[] {
310
const breakpointOptions: ISelectOptionItem[] = [
311
{ text: nls.localize('noTriggerByBreakpoint', 'None'), isDisabled: true },
312
...this.availableBreakpoints.map(bp => ({
313
text: `${this.labelService.getUriLabel(bp.uri, { relative: true })}: ${bp.lineNumber}`,
314
description: nls.localize('triggerByLoading', 'Loading...')
315
})),
316
];
317
318
// Load the source code for each breakpoint asynchronously
319
for (const [i, bp] of this.availableBreakpoints.entries()) {
320
this.textModelService.createModelReference(bp.uri).then(ref => {
321
try {
322
breakpointOptions[i + 1].description = ref.object.textEditorModel.getLineContent(bp.lineNumber).trim();
323
} finally {
324
ref.dispose();
325
}
326
}).catch(() => {
327
breakpointOptions[i + 1].description = nls.localize('noBpSource', 'Could not load source.');
328
});
329
}
330
331
return breakpointOptions;
332
}
333
334
private updateTriggerBreakpointList(): void {
335
this.availableBreakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint && !bp.logMessage);
336
337
// Try to preserve the current selection if the breakpoint still exists
338
let selectedIndex = 0; // Default to "None"
339
if (this.triggeredByBreakpointInput) {
340
const newIndex = this.availableBreakpoints.findIndex(bp => bp.getId() === this.triggeredByBreakpointInput?.getId());
341
if (newIndex !== -1) {
342
selectedIndex = newIndex + 1;
343
} else {
344
// The selected breakpoint was removed, clear the selection
345
this.triggeredByBreakpointInput = undefined;
346
}
347
}
348
349
const breakpointOptions = this.buildBreakpointOptions();
350
this.selectBreakpointBox.setOptions(breakpointOptions, selectedIndex);
351
}
352
353
private updateContextInput() {
354
if (this.context === Context.TRIGGER_POINT) {
355
this.inputContainer.hidden = true;
356
this.selectBreakpointContainer.hidden = false;
357
// Update the breakpoint list when switching to trigger point context
358
if (this.selectBreakpointBox) {
359
this.updateTriggerBreakpointList();
360
}
361
} else {
362
this.inputContainer.hidden = false;
363
this.selectBreakpointContainer.hidden = true;
364
this.setInputMode();
365
const value = this.getInputValue(this.breakpoint);
366
this.input.getModel().setValue(value);
367
this.focusInput();
368
}
369
}
370
371
protected override _doLayout(heightInPixel: number, widthInPixel: number): void {
372
this.heightInPx = heightInPixel;
373
this.input.layout({ height: heightInPixel, width: widthInPixel - 113 });
374
this.centerInputVertically();
375
}
376
377
protected override _onWidth(widthInPixel: number): void {
378
if (typeof this.heightInPx === 'number') {
379
this._doLayout(this.heightInPx, widthInPixel);
380
}
381
}
382
383
private createBreakpointInput(container: HTMLElement): void {
384
const scopedInstatiationService = this.instantiationService.createChild(new ServiceCollection(
385
[IPrivateBreakpointWidgetService, this]
386
));
387
this.store.add(scopedInstatiationService);
388
389
const options = this.createEditorOptions();
390
const codeEditorWidgetOptions = getSimpleCodeEditorWidgetOptions();
391
this.input = <IActiveCodeEditor>scopedInstatiationService.createInstance(CodeEditorWidget, container, options, codeEditorWidgetOptions);
392
393
CONTEXT_IN_BREAKPOINT_WIDGET.bindTo(this.input.contextKeyService).set(true);
394
const model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:${this.editor.getId()}:breakpointinput`), true);
395
if (this.editor.hasModel()) {
396
model.setLanguage(this.editor.getModel().getLanguageId());
397
}
398
this.input.setModel(model);
399
this.setInputMode();
400
this.store.add(model);
401
const setDecorations = () => {
402
const value = this.input.getModel().getValue();
403
const decorations = !!value ? [] : createDecorations(this.themeService.getColorTheme(), this.placeholder);
404
this.input.setDecorationsByType('breakpoint-widget', DECORATION_KEY, decorations);
405
};
406
this.store.add(this.input.getModel().onDidChangeContent(() => setDecorations()));
407
this.store.add(this.themeService.onDidColorThemeChange(() => setDecorations()));
408
409
this.store.add(this.languageFeaturesService.completionProvider.register({ scheme: DEBUG_SCHEME, hasAccessToAllModels: true }, {
410
_debugDisplayName: 'breakpointWidget',
411
provideCompletionItems: (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise<CompletionList> => {
412
let suggestionsPromise: Promise<CompletionList>;
413
const underlyingModel = this.editor.getModel();
414
if (underlyingModel && (this.context === Context.CONDITION || (this.context === Context.LOG_MESSAGE && isPositionInCurlyBracketBlock(this.input)))) {
415
suggestionsPromise = provideSuggestionItems(this.languageFeaturesService.completionProvider, underlyingModel, new Position(this.lineNumber, 1), new CompletionOptions(undefined, new Set<CompletionItemKind>().add(CompletionItemKind.Snippet)), _context, token).then(suggestions => {
416
417
let overwriteBefore = 0;
418
if (this.context === Context.CONDITION) {
419
overwriteBefore = position.column - 1;
420
} else {
421
// Inside the currly brackets, need to count how many useful characters are behind the position so they would all be taken into account
422
const value = this.input.getModel().getValue();
423
while ((position.column - 2 - overwriteBefore >= 0) && value[position.column - 2 - overwriteBefore] !== '{' && value[position.column - 2 - overwriteBefore] !== ' ') {
424
overwriteBefore++;
425
}
426
}
427
428
return {
429
suggestions: suggestions.items.map(s => {
430
s.completion.range = Range.fromPositions(position.delta(0, -overwriteBefore), position);
431
return s.completion;
432
})
433
};
434
});
435
} else {
436
suggestionsPromise = Promise.resolve({ suggestions: [] });
437
}
438
439
return suggestionsPromise;
440
}
441
}));
442
443
this.store.add(this._configurationService.onDidChangeConfiguration((e) => {
444
if (e.affectsConfiguration('editor.fontSize') || e.affectsConfiguration('editor.lineHeight')) {
445
this.input.updateOptions(this.createEditorOptions());
446
this.centerInputVertically();
447
}
448
}));
449
}
450
451
private createEditorOptions(): IEditorOptions {
452
const editorConfig = this._configurationService.getValue<IEditorOptions>('editor');
453
const options = getSimpleEditorOptions(this._configurationService);
454
options.fontSize = editorConfig.fontSize;
455
options.fontFamily = editorConfig.fontFamily;
456
options.lineHeight = editorConfig.lineHeight;
457
options.fontLigatures = editorConfig.fontLigatures;
458
options.ariaLabel = this.placeholder;
459
return options;
460
}
461
462
private centerInputVertically() {
463
if (this.container && typeof this.heightInPx === 'number') {
464
const lineHeight = this.input.getOption(EditorOption.lineHeight);
465
const lineNum = this.input.getModel().getLineCount();
466
const newTopMargin = (this.heightInPx - lineNum * lineHeight) / 2;
467
this.inputContainer.style.marginTop = newTopMargin + 'px';
468
}
469
}
470
471
close(success: boolean): void {
472
if (success) {
473
// if there is already a breakpoint on this location - remove it.
474
475
let condition: string | undefined = undefined;
476
let hitCondition: string | undefined = undefined;
477
let logMessage: string | undefined = undefined;
478
let triggeredBy: string | undefined = undefined;
479
let mode: string | undefined = undefined;
480
let modeLabel: string | undefined = undefined;
481
482
this.rememberInput();
483
484
if (this.conditionInput || this.context === Context.CONDITION) {
485
condition = this.conditionInput;
486
}
487
if (this.hitCountInput || this.context === Context.HIT_COUNT) {
488
hitCondition = this.hitCountInput;
489
}
490
if (this.logMessageInput || this.context === Context.LOG_MESSAGE) {
491
logMessage = this.logMessageInput;
492
}
493
if (this.selectModeBox) {
494
mode = this.modeInput?.mode;
495
modeLabel = this.modeInput?.label;
496
}
497
if (this.context === Context.TRIGGER_POINT) {
498
// currently, trigger points don't support additional conditions:
499
condition = undefined;
500
hitCondition = undefined;
501
logMessage = undefined;
502
triggeredBy = this.triggeredByBreakpointInput?.getId();
503
}
504
505
if (this.breakpoint) {
506
const data = new Map<string, IBreakpointUpdateData>();
507
data.set(this.breakpoint.getId(), {
508
condition,
509
hitCondition,
510
logMessage,
511
triggeredBy,
512
mode,
513
modeLabel,
514
});
515
this.debugService.updateBreakpoints(this.breakpoint.originalUri, data, false).then(undefined, onUnexpectedError);
516
} else {
517
const model = this.editor.getModel();
518
if (model) {
519
this.debugService.addBreakpoints(model.uri, [{
520
lineNumber: this.lineNumber,
521
column: this.column,
522
enabled: true,
523
condition,
524
hitCondition,
525
logMessage,
526
triggeredBy,
527
mode,
528
modeLabel,
529
}]);
530
}
531
}
532
}
533
534
this.dispose();
535
}
536
537
private focusInput() {
538
if (this.context === Context.TRIGGER_POINT) {
539
this.selectBreakpointBox.focus();
540
} else {
541
this.input.focus();
542
}
543
}
544
545
override dispose(): void {
546
super.dispose();
547
this.input.dispose();
548
lifecycle.dispose(this.store);
549
setTimeout(() => this.editor.focus(), 0);
550
}
551
}
552
553
class AcceptBreakpointWidgetInputAction extends EditorCommand {
554
static ID = 'breakpointWidget.action.acceptInput';
555
constructor() {
556
super({
557
id: AcceptBreakpointWidgetInputAction.ID,
558
precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE,
559
kbOpts: {
560
kbExpr: CONTEXT_IN_BREAKPOINT_WIDGET,
561
primary: KeyCode.Enter,
562
weight: KeybindingWeight.EditorContrib
563
}
564
});
565
}
566
567
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void {
568
accessor.get(IPrivateBreakpointWidgetService).close(true);
569
}
570
}
571
572
class CloseBreakpointWidgetCommand extends EditorCommand {
573
static ID = 'closeBreakpointWidget';
574
constructor() {
575
super({
576
id: CloseBreakpointWidgetCommand.ID,
577
precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE,
578
kbOpts: {
579
kbExpr: EditorContextKeys.textInputFocus,
580
primary: KeyCode.Escape,
581
secondary: [KeyMod.Shift | KeyCode.Escape],
582
weight: KeybindingWeight.EditorContrib
583
}
584
});
585
}
586
587
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void {
588
const debugContribution = editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID);
589
if (debugContribution) {
590
// if focus is in outer editor we need to use the debug contribution to close
591
return debugContribution.closeBreakpointWidget();
592
}
593
594
accessor.get(IPrivateBreakpointWidgetService).close(false);
595
}
596
}
597
598
registerEditorCommand(new AcceptBreakpointWidgetInputAction());
599
registerEditorCommand(new CloseBreakpointWidgetCommand());
600
601