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