Path: blob/main/src/vs/workbench/contrib/debug/browser/breakpointsView.ts
5237 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 { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';7import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';8import { AriaRole } from '../../../../base/browser/ui/aria/aria.js';9import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';10import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js';11import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js';12import { Checkbox, TriStateCheckbox } from '../../../../base/browser/ui/toggle/toggle.js';13import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';14import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';15import { Orientation } from '../../../../base/browser/ui/splitview/splitview.js';16import { ICompressedTreeElement, ICompressedTreeNode } from '../../../../base/browser/ui/tree/compressedObjectTreeModel.js';17import { ICompressibleTreeRenderer } from '../../../../base/browser/ui/tree/objectTree.js';18import { ITreeContextMenuEvent, ITreeNode } from '../../../../base/browser/ui/tree/tree.js';19import { Action } from '../../../../base/common/actions.js';20import { RunOnceScheduler } from '../../../../base/common/async.js';21import { Codicon } from '../../../../base/common/codicons.js';22import { MarkdownString } from '../../../../base/common/htmlContent.js';23import { KeyCode } from '../../../../base/common/keyCodes.js';24import { DisposableStore, dispose, toDisposable } from '../../../../base/common/lifecycle.js';25import * as resources from '../../../../base/common/resources.js';26import { ThemeIcon } from '../../../../base/common/themables.js';27import { URI } from '../../../../base/common/uri.js';28import { Constants } from '../../../../base/common/uint.js';29import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';30import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';31import { ILanguageService } from '../../../../editor/common/languages/language.js';32import { ITextModelService } from '../../../../editor/common/services/resolverService.js';33import { localize, localize2 } from '../../../../nls.js';34import { getActionBarActions, getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';35import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';36import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';37import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';38import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js';39import { TextEditorSelectionRevealType } from '../../../../platform/editor/common/editor.js';40import { IHoverService } from '../../../../platform/hover/browser/hover.js';41import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';42import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';43import { ILabelService } from '../../../../platform/label/common/label.js';44import { WorkbenchCompressibleObjectTree } from '../../../../platform/list/browser/listService.js';45import { INotificationService } from '../../../../platform/notification/common/notification.js';46import { IOpenerService } from '../../../../platform/opener/common/opener.js';47import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';48import { defaultCheckboxStyles, defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js';49import { IThemeService } from '../../../../platform/theme/common/themeService.js';50import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js';51import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js';52import { IEditorPane } from '../../../common/editor.js';53import { IViewDescriptorService } from '../../../common/views.js';54import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js';55import { IViewsService } from '../../../services/views/common/viewsService.js';56import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_HAS_MODES, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, DEBUG_SCHEME, DataBreakpointSetType, DataBreakpointSource, DebuggerString, IBaseBreakpoint, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDataBreakpoint, IDataBreakpointInfoResponse, IDebugModel, IDebugService, IEnablement, IExceptionBreakpoint, IFunctionBreakpoint, IInstructionBreakpoint, State } from '../common/debug.js';57import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, FunctionBreakpoint, InstructionBreakpoint } from '../common/debugModel.js';58import { DisassemblyViewInput } from '../common/disassemblyViewInput.js';59import * as icons from './debugIcons.js';60import { DisassemblyView } from './disassemblyView.js';61import { equals } from '../../../../base/common/arrays.js';62import { hasKey } from '../../../../base/common/types.js';6364const $ = dom.$;6566function createCheckbox(disposables: DisposableStore): Checkbox {67const checkbox = new Checkbox('', false, defaultCheckboxStyles);68checkbox.domNode.tabIndex = -1;69disposables.add(checkbox);7071return checkbox;72}7374const MAX_VISIBLE_BREAKPOINTS = 9;75export function getExpandedBodySize(model: IDebugModel, sessionId: string | undefined, countLimit: number): number {76const length = model.getBreakpoints().length + model.getExceptionBreakpointsForSession(sessionId).length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length + model.getInstructionBreakpoints().length;77return Math.min(countLimit, length) * 22;78}79type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint | IInstructionBreakpoint;8081/**82* Represents a file node in the breakpoints tree that groups breakpoints by file.83*/84export class BreakpointsFolderItem {85constructor(86readonly uri: URI,87readonly breakpoints: IBreakpoint[]88) { }8990getId(): string {91return this.uri.toString();92}9394get enabled(): boolean {95return this.breakpoints.every(bp => bp.enabled);96}9798get indeterminate(): boolean {99const enabledCount = this.breakpoints.filter(bp => bp.enabled).length;100return enabledCount > 0 && enabledCount < this.breakpoints.length;101}102}103104type BreakpointTreeElement = BreakpointsFolderItem | BreakpointItem;105106interface InputBoxData {107breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | IDataBreakpoint;108type: 'condition' | 'hitCount' | 'name';109}110111function getModeKindForBreakpoint(breakpoint: IBreakpoint) {112const kind = breakpoint instanceof Breakpoint ? 'source' : breakpoint instanceof InstructionBreakpoint ? 'instruction' : 'exception';113return kind;114}115116export class BreakpointsView extends ViewPane {117118private tree!: WorkbenchCompressibleObjectTree<BreakpointTreeElement, void>;119private needsRefresh = false;120private needsStateChange = false;121private ignoreLayout = false;122private menu: IMenu;123private breakpointItemType: IContextKey<string | undefined>;124private breakpointIsDataBytes: IContextKey<boolean | undefined>;125private breakpointHasMultipleModes: IContextKey<boolean>;126private breakpointSupportsCondition: IContextKey<boolean>;127private _inputBoxData: InputBoxData | undefined;128breakpointInputFocused: IContextKey<boolean>;129private autoFocusedElement: BreakpointItem | undefined;130private collapsedState = new Set<string>();131132private hintContainer: IconLabel | undefined;133private hintDelayer: RunOnceScheduler;134135private getPresentation(): 'tree' | 'list' {136return this.configurationService.getValue<'tree' | 'list'>('debug.breakpointsView.presentation');137}138139constructor(140options: IViewletViewOptions,141@IContextMenuService contextMenuService: IContextMenuService,142@IDebugService private readonly debugService: IDebugService,143@IKeybindingService keybindingService: IKeybindingService,144@IInstantiationService instantiationService: IInstantiationService,145@IThemeService themeService: IThemeService,146@IEditorService private readonly editorService: IEditorService,147@IContextViewService private readonly contextViewService: IContextViewService,148@IConfigurationService configurationService: IConfigurationService,149@IViewDescriptorService viewDescriptorService: IViewDescriptorService,150@IContextKeyService contextKeyService: IContextKeyService,151@IOpenerService openerService: IOpenerService,152@ILabelService private readonly labelService: ILabelService,153@IMenuService menuService: IMenuService,154@IHoverService hoverService: IHoverService,155@ILanguageService private readonly languageService: ILanguageService,156) {157super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);158159this.menu = menuService.createMenu(MenuId.DebugBreakpointsContext, contextKeyService);160this._register(this.menu);161this.breakpointItemType = CONTEXT_BREAKPOINT_ITEM_TYPE.bindTo(contextKeyService);162this.breakpointIsDataBytes = CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES.bindTo(contextKeyService);163this.breakpointHasMultipleModes = CONTEXT_BREAKPOINT_HAS_MODES.bindTo(contextKeyService);164this.breakpointSupportsCondition = CONTEXT_BREAKPOINT_SUPPORTS_CONDITION.bindTo(contextKeyService);165this.breakpointInputFocused = CONTEXT_BREAKPOINT_INPUT_FOCUSED.bindTo(contextKeyService);166this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));167this._register(this.debugService.getViewModel().onDidFocusSession(() => this.onBreakpointsChange()));168this._register(this.debugService.onDidChangeState(() => this.onStateChange()));169this.hintDelayer = this._register(new RunOnceScheduler(() => this.updateBreakpointsHint(true), 4000));170}171172protected override renderBody(container: HTMLElement): void {173super.renderBody(container);174175this.element.classList.add('debug-pane');176container.classList.add('debug-breakpoints');177178this.tree = this.instantiationService.createInstance(179WorkbenchCompressibleObjectTree<BreakpointTreeElement, void>,180'BreakpointsView',181container,182new BreakpointsDelegate(this),183[184this.instantiationService.createInstance(BreakpointsFolderRenderer),185this.instantiationService.createInstance(BreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType),186new ExceptionBreakpointsRenderer(this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType, this.debugService, this.hoverService),187new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService),188this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType),189new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.hoverService, this.labelService),190this.instantiationService.createInstance(DataBreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType, this.breakpointIsDataBytes),191new DataBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.hoverService, this.labelService),192this.instantiationService.createInstance(InstructionBreakpointsRenderer),193],194{195compressionEnabled: this.getPresentation() === 'tree',196hideTwistiesOfChildlessElements: true,197identityProvider: {198getId: (element: BreakpointTreeElement) => element.getId()199},200keyboardNavigationLabelProvider: {201getKeyboardNavigationLabel: (element: BreakpointTreeElement) => {202if (element instanceof BreakpointsFolderItem) {203return resources.basenameOrAuthority(element.uri);204}205if (element instanceof Breakpoint) {206return `${resources.basenameOrAuthority(element.uri)}:${element.lineNumber}`;207}208if (element instanceof FunctionBreakpoint) {209return element.name;210}211if (element instanceof DataBreakpoint) {212return element.description;213}214if (element instanceof ExceptionBreakpoint) {215return element.label || element.filter;216}217if (element instanceof InstructionBreakpoint) {218return `0x${element.address.toString(16)}`;219}220return '';221},222getCompressedNodeKeyboardNavigationLabel: (elements: BreakpointTreeElement[]) => {223return elements.map(e => {224if (e instanceof BreakpointsFolderItem) {225return resources.basenameOrAuthority(e.uri);226}227return '';228}).join('/');229}230},231accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService, this.labelService),232multipleSelectionSupport: false,233overrideStyles: this.getLocationBasedColors().listOverrideStyles234}235);236this._register(this.tree);237238CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.tree.contextKeyService);239240this._register(this.tree.onContextMenu(this.onTreeContextMenu, this));241242this._register(this.tree.onMouseMiddleClick(async ({ element }) => {243if (element instanceof Breakpoint) {244await this.debugService.removeBreakpoints(element.getId());245} else if (element instanceof FunctionBreakpoint) {246await this.debugService.removeFunctionBreakpoints(element.getId());247} else if (element instanceof DataBreakpoint) {248await this.debugService.removeDataBreakpoints(element.getId());249} else if (element instanceof InstructionBreakpoint) {250await this.debugService.removeInstructionBreakpoints(element.instructionReference, element.offset);251} else if (element instanceof BreakpointsFolderItem) {252await this.debugService.removeBreakpoints(element.breakpoints.map(bp => bp.getId()));253}254}));255256this._register(this.tree.onDidOpen(async e => {257const element = e.element;258if (!element) {259return;260}261262if (dom.isMouseEvent(e.browserEvent) && e.browserEvent.button === 1) { // middle click263return;264}265266if (element instanceof Breakpoint) {267openBreakpointSource(element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService);268}269if (element instanceof InstructionBreakpoint) {270const disassemblyView = await this.editorService.openEditor(DisassemblyViewInput.instance);271// Focus on double click272(disassemblyView as DisassemblyView).goToInstructionAndOffset(element.instructionReference, element.offset, dom.isMouseEvent(e.browserEvent) && e.browserEvent.detail === 2);273}274if (dom.isMouseEvent(e.browserEvent) && e.browserEvent.detail === 2 && element instanceof FunctionBreakpoint && element !== this.inputBoxData?.breakpoint) {275// double click276this.renderInputBox({ breakpoint: element, type: 'name' });277}278}));279280// Track collapsed state and update size (items are expanded by default)281this._register(this.tree.onDidChangeCollapseState(e => {282const element = e.node.element;283if (element instanceof BreakpointsFolderItem) {284if (e.node.collapsed) {285this.collapsedState.add(element.getId());286} else {287this.collapsedState.delete(element.getId());288}289this.updateSize();290}291}));292293// React to configuration changes294this._register(this.configurationService.onDidChangeConfiguration(e => {295if (e.affectsConfiguration('debug.breakpointsView.presentation')) {296const presentation = this.getPresentation();297this.tree.updateOptions({ compressionEnabled: presentation === 'tree' });298this.onBreakpointsChange();299}300}));301302this.setTreeInput();303304this._register(this.onDidChangeBodyVisibility(visible => {305if (visible) {306if (this.needsRefresh) {307this.onBreakpointsChange();308}309310if (this.needsStateChange) {311this.onStateChange();312}313}314}));315316const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!);317this._register(containerModel.onDidChangeAllViewDescriptors(() => {318this.updateSize();319}));320}321322protected override renderHeaderTitle(container: HTMLElement, title: string): void {323super.renderHeaderTitle(container, title);324325const iconLabelContainer = dom.append(container, $('span.breakpoint-warning'));326this.hintContainer = this._register(new IconLabel(iconLabelContainer, {327supportIcons: true, hoverDelegate: {328showHover: (options, focus?) => this.hoverService.showInstantHover({ content: options.content, target: this.hintContainer!.element }, focus),329delay: this.configurationService.getValue<number>('workbench.hover.delay')330}331}));332dom.hide(this.hintContainer.element);333}334335override focus(): void {336super.focus();337this.tree?.domFocus();338}339340renderInputBox(data: InputBoxData | undefined): void {341this._inputBoxData = data;342this.onBreakpointsChange();343this._inputBoxData = undefined;344}345346get inputBoxData(): InputBoxData | undefined {347return this._inputBoxData;348}349350protected override layoutBody(height: number, width: number): void {351if (this.ignoreLayout) {352return;353}354355super.layoutBody(height, width);356this.tree?.layout(height, width);357try {358this.ignoreLayout = true;359this.updateSize();360} finally {361this.ignoreLayout = false;362}363}364365private onTreeContextMenu(e: ITreeContextMenuEvent<BreakpointTreeElement | null>): void {366const element = e.element;367if (element instanceof BreakpointsFolderItem) {368// For folder items, show file-level context menu369this.breakpointItemType.set('breakpointFolder');370const { secondary } = getContextMenuActions(this.menu.getActions({ arg: element, shouldForwardArgs: false }), 'inline');371this.contextMenuService.showContextMenu({372getAnchor: () => e.anchor,373getActions: () => secondary,374getActionsContext: () => element375});376return;377}378379const type = element instanceof Breakpoint ? 'breakpoint' : element instanceof ExceptionBreakpoint ? 'exceptionBreakpoint' :380element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' :381element instanceof InstructionBreakpoint ? 'instructionBreakpoint' : undefined;382this.breakpointItemType.set(type);383const session = this.debugService.getViewModel().focusedSession;384const conditionSupported = element instanceof ExceptionBreakpoint ? element.supportsCondition : (!session || !!session.capabilities.supportsConditionalBreakpoints);385this.breakpointSupportsCondition.set(conditionSupported);386this.breakpointIsDataBytes.set(element instanceof DataBreakpoint && element.src.type === DataBreakpointSetType.Address);387this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes(getModeKindForBreakpoint(element as IBreakpoint)).length > 1);388389const { secondary } = getContextMenuActions(this.menu.getActions({ arg: e.element, shouldForwardArgs: false }), 'inline');390391this.contextMenuService.showContextMenu({392getAnchor: () => e.anchor,393getActions: () => secondary,394getActionsContext: () => element395});396}397398private updateSize(): void {399const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!);400401// Calculate visible row count from tree's content height402// Each row is 22px high403const rowHeight = 22;404405this.minimumBodySize = this.orientation === Orientation.VERTICAL ? Math.min(MAX_VISIBLE_BREAKPOINTS * rowHeight, this.tree.contentHeight) : 170;406this.maximumBodySize = this.orientation === Orientation.VERTICAL && containerModel.visibleViewDescriptors.length > 1 ? this.tree.contentHeight : Number.POSITIVE_INFINITY;407}408409private updateBreakpointsHint(delayed = false): void {410if (!this.hintContainer) {411return;412}413414const currentType = this.debugService.getViewModel().focusedSession?.configuration.type;415const dbg = currentType ? this.debugService.getAdapterManager().getDebugger(currentType) : undefined;416const message = dbg?.strings?.[DebuggerString.UnverifiedBreakpoints];417const debuggerHasUnverifiedBps = message && this.debugService.getModel().getBreakpoints().filter(bp => {418if (bp.verified || !bp.enabled) {419return false;420}421422const langId = this.languageService.guessLanguageIdByFilepathOrFirstLine(bp.uri);423return langId && dbg.interestedInLanguage(langId);424});425426if (message && debuggerHasUnverifiedBps?.length && this.debugService.getModel().areBreakpointsActivated()) {427if (delayed) {428const mdown = new MarkdownString(undefined, { isTrusted: true }).appendMarkdown(message);429this.hintContainer.setLabel('$(warning)', undefined, { title: { markdown: mdown, markdownNotSupportedFallback: message } });430dom.show(this.hintContainer.element);431} else {432this.hintDelayer.schedule();433}434} else {435dom.hide(this.hintContainer.element);436}437}438439private onBreakpointsChange(): void {440if (this.isBodyVisible()) {441if (this.tree) {442this.setTreeInput();443this.needsRefresh = false;444}445this.updateBreakpointsHint();446this.updateSize();447} else {448this.needsRefresh = true;449}450}451452private onStateChange(): void {453if (this.isBodyVisible()) {454this.needsStateChange = false;455const thread = this.debugService.getViewModel().focusedThread;456let found = false;457if (thread && thread.stoppedDetails && thread.stoppedDetails.hitBreakpointIds && thread.stoppedDetails.hitBreakpointIds.length > 0) {458const hitBreakpointIds = thread.stoppedDetails.hitBreakpointIds;459const elements = this.flatElements;460const hitElement = elements.find(e => {461const id = e.getIdFromAdapter(thread.session.getId());462return typeof id === 'number' && hitBreakpointIds.indexOf(id) !== -1;463});464if (hitElement) {465this.tree.setFocus([hitElement]);466this.tree.setSelection([hitElement]);467found = true;468this.autoFocusedElement = hitElement;469}470}471if (!found) {472// Deselect breakpoint in breakpoint view when no longer stopped on it #125528473const focus = this.tree.getFocus();474const selection = this.tree.getSelection();475if (this.autoFocusedElement && equals(focus, selection) && selection.includes(this.autoFocusedElement)) {476this.tree.setFocus([]);477this.tree.setSelection([]);478}479this.autoFocusedElement = undefined;480}481this.updateBreakpointsHint();482} else {483this.needsStateChange = true;484}485}486487private setTreeInput(): void {488const treeInput = this.getTreeElements();489this.tree.setChildren(null, treeInput);490}491492private getTreeElements(): ICompressedTreeElement<BreakpointTreeElement>[] {493const model = this.debugService.getModel();494const sessionId = this.debugService.getViewModel().focusedSession?.getId();495const showAsTree = this.getPresentation() === 'tree';496497const result: ICompressedTreeElement<BreakpointTreeElement>[] = [];498499// Exception breakpoints at the top (root level)500for (const exBp of model.getExceptionBreakpointsForSession(sessionId)) {501result.push({ element: exBp, incompressible: true });502}503504// Function breakpoints (root level)505for (const funcBp of model.getFunctionBreakpoints()) {506result.push({ element: funcBp, incompressible: true });507}508509// Data breakpoints (root level)510for (const dataBp of model.getDataBreakpoints()) {511result.push({ element: dataBp, incompressible: true });512}513514// Source breakpoints - group by file if showAsTree is enabled515const sourceBreakpoints = model.getBreakpoints();516if (showAsTree && sourceBreakpoints.length > 0) {517// Group breakpoints by URI518const breakpointsByUri = new Map<string, IBreakpoint[]>();519for (const bp of sourceBreakpoints) {520const key = bp.uri.toString();521if (!breakpointsByUri.has(key)) {522breakpointsByUri.set(key, []);523}524breakpointsByUri.get(key)!.push(bp);525}526527// Create folder items for each file528for (const [uriStr, breakpoints] of breakpointsByUri) {529const uri = URI.parse(uriStr);530const folderItem = new BreakpointsFolderItem(uri, breakpoints);531532// Sort breakpoints by line number533breakpoints.sort((a, b) => a.lineNumber - b.lineNumber);534535const children: ICompressedTreeElement<BreakpointTreeElement>[] = breakpoints.map(bp => ({536element: bp,537incompressible: false538}));539540result.push({541element: folderItem,542incompressible: false,543collapsed: this.collapsedState.has(folderItem.getId()),544children545});546}547} else {548// Flat mode - just add all source breakpoints549for (const bp of sourceBreakpoints) {550result.push({ element: bp, incompressible: true });551}552}553554// Instruction breakpoints (root level)555for (const instrBp of model.getInstructionBreakpoints()) {556result.push({ element: instrBp, incompressible: true });557}558559return result;560}561562private get flatElements(): BreakpointItem[] {563const model = this.debugService.getModel();564const sessionId = this.debugService.getViewModel().focusedSession?.getId();565const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpointsForSession(sessionId)).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()).concat(model.getInstructionBreakpoints());566567return elements as BreakpointItem[];568}569}570571class BreakpointsDelegate implements IListVirtualDelegate<BreakpointTreeElement> {572573constructor(private view: BreakpointsView) {574// noop575}576577getHeight(_element: BreakpointTreeElement): number {578return 22;579}580581getTemplateId(element: BreakpointTreeElement): string {582if (element instanceof BreakpointsFolderItem) {583return BreakpointsFolderRenderer.ID;584}585if (element instanceof Breakpoint) {586return BreakpointsRenderer.ID;587}588if (element instanceof FunctionBreakpoint) {589const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;590if (!element.name || (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId())) {591return FunctionBreakpointInputRenderer.ID;592}593594return FunctionBreakpointsRenderer.ID;595}596if (element instanceof ExceptionBreakpoint) {597const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;598if (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId()) {599return ExceptionBreakpointInputRenderer.ID;600}601return ExceptionBreakpointsRenderer.ID;602}603if (element instanceof DataBreakpoint) {604const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;605if (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId()) {606return DataBreakpointInputRenderer.ID;607}608609return DataBreakpointsRenderer.ID;610}611if (element instanceof InstructionBreakpoint) {612return InstructionBreakpointsRenderer.ID;613}614615return '';616}617}618619interface IBaseBreakpointTemplateData {620breakpoint: HTMLElement;621name: HTMLElement;622checkbox: Checkbox;623context: BreakpointItem;624actionBar: ActionBar;625templateDisposables: DisposableStore;626elementDisposables: DisposableStore;627badge: HTMLElement;628}629630interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {631icon: HTMLElement;632}633634interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {635filePath: HTMLElement;636}637638interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData {639condition: HTMLElement;640}641642interface IFunctionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {643condition: HTMLElement;644}645646interface IDataBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {647accessType: HTMLElement;648condition: HTMLElement;649}650651interface IInstructionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {652address: HTMLElement;653}654655interface IFunctionBreakpointInputTemplateData {656inputBox: InputBox;657checkbox: Checkbox;658icon: HTMLElement;659breakpoint: IFunctionBreakpoint;660templateDisposables: DisposableStore;661elementDisposables: DisposableStore;662type: 'hitCount' | 'condition' | 'name';663updating?: boolean;664}665666interface IDataBreakpointInputTemplateData {667inputBox: InputBox;668checkbox: Checkbox;669icon: HTMLElement;670breakpoint: IDataBreakpoint;671elementDisposables: DisposableStore;672templateDisposables: DisposableStore;673type: 'hitCount' | 'condition' | 'name';674updating?: boolean;675}676677interface IExceptionBreakpointInputTemplateData {678inputBox: InputBox;679checkbox: Checkbox;680currentBreakpoint?: IExceptionBreakpoint;681templateDisposables: DisposableStore;682elementDisposables: DisposableStore;683}684685interface IBreakpointsFolderTemplateData {686container: HTMLElement;687checkbox: TriStateCheckbox;688name: HTMLElement;689actionBar: ActionBar;690context: BreakpointsFolderItem;691templateDisposables: DisposableStore;692elementDisposables: DisposableStore;693}694695const breakpointIdToActionBarDomeNode = new Map<string, HTMLElement>();696697class BreakpointsFolderRenderer implements ICompressibleTreeRenderer<BreakpointsFolderItem, void, IBreakpointsFolderTemplateData> {698699static readonly ID = 'breakpointFolder';700701constructor(702@IDebugService private readonly debugService: IDebugService,703@ILabelService private readonly labelService: ILabelService,704@IHoverService private readonly hoverService: IHoverService,705) { }706707get templateId() {708return BreakpointsFolderRenderer.ID;709}710711renderTemplate(container: HTMLElement): IBreakpointsFolderTemplateData {712const data: IBreakpointsFolderTemplateData = Object.create(null);713data.elementDisposables = new DisposableStore();714data.templateDisposables = new DisposableStore();715data.templateDisposables.add(data.elementDisposables);716717data.container = container;718container.classList.add('breakpoint', 'breakpoint-folder');719720data.templateDisposables.add(toDisposable(() => {721container.classList.remove('breakpoint', 'breakpoint-folder');722}));723724data.checkbox = new TriStateCheckbox('', false, defaultCheckboxStyles);725data.checkbox.domNode.tabIndex = -1;726data.templateDisposables.add(data.checkbox);727data.templateDisposables.add(data.checkbox.onChange(() => {728const checked = data.checkbox.checked;729const enabled = checked === 'mixed' ? true : checked;730for (const bp of data.context.breakpoints) {731this.debugService.enableOrDisableBreakpoints(enabled, bp);732}733}));734735dom.append(data.container, data.checkbox.domNode);736data.name = dom.append(data.container, $('span.name'));737dom.append(data.container, $('span.file-path'));738739data.actionBar = new ActionBar(data.container);740data.templateDisposables.add(data.actionBar);741742return data;743}744745renderElement(node: ITreeNode<BreakpointsFolderItem, void>, _index: number, data: IBreakpointsFolderTemplateData): void {746const folderItem = node.element;747data.context = folderItem;748749data.name.textContent = this.labelService.getUriBasenameLabel(folderItem.uri);750data.container.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());751752const fullPath = this.labelService.getUriLabel(folderItem.uri, { relative: true });753data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.container, fullPath));754755// Set checkbox state756if (folderItem.indeterminate) {757data.checkbox.checked = 'mixed';758} else {759data.checkbox.checked = folderItem.enabled;760}761762// Add remove action763data.actionBar.clear();764const removeAction = data.elementDisposables.add(new Action(765'debug.removeBreakpointsInFile',766localize('removeBreakpointsInFile', "Remove Breakpoints in File"),767ThemeIcon.asClassName(Codicon.close),768true,769async () => {770for (const bp of folderItem.breakpoints) {771await this.debugService.removeBreakpoints(bp.getId());772}773}774));775data.actionBar.push(removeAction, { icon: true, label: false });776}777778renderCompressedElements(node: ITreeNode<ICompressedTreeNode<BreakpointsFolderItem>, void>, _index: number, data: IBreakpointsFolderTemplateData): void {779const elements = node.element.elements;780const folderItem = elements[elements.length - 1];781data.context = folderItem;782783// For compressed nodes, show the combined path784const names = elements.map(e => resources.basenameOrAuthority(e.uri));785data.name.textContent = names.join('/');786787const fullPath = this.labelService.getUriLabel(folderItem.uri, { relative: true });788data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.container, fullPath));789790// Set checkbox state791if (folderItem.indeterminate) {792data.checkbox.checked = 'mixed';793} else {794data.checkbox.checked = folderItem.enabled;795}796797// Add remove action798data.actionBar.clear();799const removeAction = data.elementDisposables.add(new Action(800'debug.removeBreakpointsInFile',801localize('removeBreakpointsInFile', "Remove Breakpoints in File"),802ThemeIcon.asClassName(Codicon.close),803true,804async () => {805for (const bp of folderItem.breakpoints) {806await this.debugService.removeBreakpoints(bp.getId());807}808}809));810data.actionBar.push(removeAction, { icon: true, label: false });811}812813disposeElement(element: ITreeNode<BreakpointsFolderItem, void>, index: number, templateData: IBreakpointsFolderTemplateData): void {814templateData.elementDisposables.clear();815}816817disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<BreakpointsFolderItem>, void>, index: number, templateData: IBreakpointsFolderTemplateData): void {818templateData.elementDisposables.clear();819}820821disposeTemplate(templateData: IBreakpointsFolderTemplateData): void {822templateData.templateDisposables.dispose();823}824}825826class BreakpointsRenderer implements ICompressibleTreeRenderer<IBreakpoint, void, IBreakpointTemplateData> {827828constructor(829private menu: IMenu,830private breakpointHasMultipleModes: IContextKey<boolean>,831private breakpointSupportsCondition: IContextKey<boolean>,832private breakpointItemType: IContextKey<string | undefined>,833@IDebugService private readonly debugService: IDebugService,834@IHoverService private readonly hoverService: IHoverService,835@ILabelService private readonly labelService: ILabelService,836@ITextModelService private readonly textModelService: ITextModelService837) {838// noop839}840841static readonly ID = 'breakpoints';842843get templateId() {844return BreakpointsRenderer.ID;845}846847renderTemplate(container: HTMLElement): IBreakpointTemplateData {848const data: IBreakpointTemplateData = Object.create(null);849data.elementDisposables = new DisposableStore();850data.templateDisposables = new DisposableStore();851data.templateDisposables.add(data.elementDisposables);852853data.breakpoint = container;854container.classList.add('breakpoint');855856data.templateDisposables.add(toDisposable(() => {857container.classList.remove('breakpoint');858}));859860data.icon = $('.icon');861data.checkbox = createCheckbox(data.templateDisposables);862863data.templateDisposables.add(data.checkbox.onChange(() => {864this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);865}));866867dom.append(data.breakpoint, data.icon);868dom.append(data.breakpoint, data.checkbox.domNode);869870data.name = dom.append(data.breakpoint, $('span.name'));871872data.filePath = dom.append(data.breakpoint, $('span.file-path'));873data.actionBar = new ActionBar(data.breakpoint);874data.templateDisposables.add(data.actionBar);875const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));876data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));877878return data;879}880881renderElement(node: ITreeNode<IBreakpoint, void>, index: number, data: IBreakpointTemplateData): void {882const breakpoint = node.element;883data.context = breakpoint;884885if (node.depth > 1) {886this.renderBreakpointLineLabel(breakpoint, data);887} else {888this.renderBreakpointFileLabel(breakpoint, data);889}890891this.renderBreakpointCommon(breakpoint, data);892}893894renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IBreakpoint>, void>, index: number, data: IBreakpointTemplateData): void {895const breakpoint = node.element.elements[node.element.elements.length - 1];896data.context = breakpoint;897this.renderBreakpointFileLabel(breakpoint, data);898this.renderBreakpointCommon(breakpoint, data);899}900901private renderBreakpointCommon(breakpoint: IBreakpoint, data: IBreakpointTemplateData): void {902data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());903let badgeContent = breakpoint.lineNumber.toString();904if (breakpoint.column) {905badgeContent += `:${breakpoint.column}`;906}907if (breakpoint.modeLabel) {908badgeContent = `${breakpoint.modeLabel}: ${badgeContent}`;909}910data.badge.textContent = badgeContent;911data.checkbox.checked = breakpoint.enabled;912913const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService, this.debugService.getModel());914data.icon.className = ThemeIcon.asClassName(icon);915data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, breakpoint.message || message || ''));916917const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;918if (debugActive && !breakpoint.verified) {919data.breakpoint.classList.add('disabled');920}921922const session = this.debugService.getViewModel().focusedSession;923this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);924this.breakpointItemType.set('breakpoint');925this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('source').length > 1);926const { primary } = getActionBarActions(this.menu.getActions({ arg: breakpoint, shouldForwardArgs: true }), 'inline');927data.actionBar.clear();928data.actionBar.push(primary, { icon: true, label: false });929breakpointIdToActionBarDomeNode.set(breakpoint.getId(), data.actionBar.domNode);930}931932private renderBreakpointFileLabel(breakpoint: IBreakpoint, data: IBreakpointTemplateData): void {933data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);934data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });935}936937private renderBreakpointLineLabel(breakpoint: IBreakpoint, data: IBreakpointTemplateData): void {938data.name.textContent = localize('loading', "Loading...");939data.filePath.textContent = '';940941this.textModelService.createModelReference(breakpoint.uri).then(reference => {942if (data.context !== breakpoint) {943reference.dispose();944return;945}946data.elementDisposables.add(reference);947const model = reference.object.textEditorModel;948if (model && breakpoint.lineNumber <= model.getLineCount()) {949const lineContent = model.getLineContent(breakpoint.lineNumber).trim();950data.name.textContent = lineContent || localize('emptyLine', "(empty line)");951} else {952data.name.textContent = localize('lineNotFound', "(line not found)");953}954}).catch(() => {955if (data.context === breakpoint) {956data.name.textContent = localize('cannotLoadLine', "(cannot load line)");957}958});959}960961disposeElement(node: ITreeNode<IBreakpoint, void>, index: number, template: IBreakpointTemplateData): void {962template.elementDisposables.clear();963}964965disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IBreakpoint>, void>, index: number, template: IBreakpointTemplateData): void {966template.elementDisposables.clear();967}968969disposeTemplate(templateData: IBreakpointTemplateData): void {970templateData.templateDisposables.dispose();971}972}973974class ExceptionBreakpointsRenderer implements ICompressibleTreeRenderer<IExceptionBreakpoint, void, IExceptionBreakpointTemplateData> {975976constructor(977private menu: IMenu,978private breakpointHasMultipleModes: IContextKey<boolean>,979private breakpointSupportsCondition: IContextKey<boolean>,980private breakpointItemType: IContextKey<string | undefined>,981private debugService: IDebugService,982private readonly hoverService: IHoverService,983) {984// noop985}986987static readonly ID = 'exceptionbreakpoints';988989get templateId() {990return ExceptionBreakpointsRenderer.ID;991}992993renderTemplate(container: HTMLElement): IExceptionBreakpointTemplateData {994const data: IExceptionBreakpointTemplateData = Object.create(null);995data.elementDisposables = new DisposableStore();996data.templateDisposables = new DisposableStore();997data.templateDisposables.add(data.elementDisposables);998data.breakpoint = dom.append(container, $('.breakpoint'));9991000data.checkbox = createCheckbox(data.templateDisposables);1001data.templateDisposables.add(data.checkbox.onChange(() => {1002this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);1003}));10041005dom.append(data.breakpoint, data.checkbox.domNode);10061007data.name = dom.append(data.breakpoint, $('span.name'));1008data.condition = dom.append(data.breakpoint, $('span.condition'));1009data.breakpoint.classList.add('exception');10101011data.actionBar = new ActionBar(data.breakpoint);1012data.templateDisposables.add(data.actionBar);1013const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));1014data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));10151016return data;1017}10181019renderElement(node: ITreeNode<IExceptionBreakpoint, void>, index: number, data: IExceptionBreakpointTemplateData): void {1020const exceptionBreakpoint = node.element;1021this.renderExceptionBreakpoint(exceptionBreakpoint, data);1022}10231024renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IExceptionBreakpoint>, void>, index: number, data: IExceptionBreakpointTemplateData): void {1025const exceptionBreakpoint = node.element.elements[node.element.elements.length - 1];1026this.renderExceptionBreakpoint(exceptionBreakpoint, data);1027}10281029private renderExceptionBreakpoint(exceptionBreakpoint: IExceptionBreakpoint, data: IExceptionBreakpointTemplateData): void {1030data.context = exceptionBreakpoint;1031data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;1032const exceptionBreakpointtitle = exceptionBreakpoint.verified ? (exceptionBreakpoint.description || data.name.textContent) : exceptionBreakpoint.message || localize('unverifiedExceptionBreakpoint', "Unverified Exception Breakpoint");1033data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, exceptionBreakpointtitle));1034data.breakpoint.classList.toggle('disabled', !exceptionBreakpoint.verified);1035data.checkbox.checked = exceptionBreakpoint.enabled;1036data.condition.textContent = exceptionBreakpoint.condition || '';1037data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.condition, localize('expressionCondition', "Expression condition: {0}", exceptionBreakpoint.condition)));10381039if (exceptionBreakpoint.modeLabel) {1040data.badge.textContent = exceptionBreakpoint.modeLabel;1041data.badge.style.display = 'block';1042} else {1043data.badge.style.display = 'none';1044}10451046this.breakpointSupportsCondition.set((exceptionBreakpoint as ExceptionBreakpoint).supportsCondition);1047this.breakpointItemType.set('exceptionBreakpoint');1048this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('exception').length > 1);1049const { primary } = getActionBarActions(this.menu.getActions({ arg: exceptionBreakpoint, shouldForwardArgs: true }), 'inline');1050data.actionBar.clear();1051data.actionBar.push(primary, { icon: true, label: false });1052breakpointIdToActionBarDomeNode.set(exceptionBreakpoint.getId(), data.actionBar.domNode);1053}10541055disposeElement(node: ITreeNode<IExceptionBreakpoint, void>, index: number, templateData: IExceptionBreakpointTemplateData): void {1056templateData.elementDisposables.clear();1057}10581059disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IExceptionBreakpoint>, void>, index: number, templateData: IExceptionBreakpointTemplateData): void {1060templateData.elementDisposables.clear();1061}10621063disposeTemplate(templateData: IExceptionBreakpointTemplateData): void {1064templateData.templateDisposables.dispose();1065}1066}10671068class FunctionBreakpointsRenderer implements ICompressibleTreeRenderer<FunctionBreakpoint, void, IFunctionBreakpointTemplateData> {10691070constructor(1071private menu: IMenu,1072private breakpointSupportsCondition: IContextKey<boolean>,1073private breakpointItemType: IContextKey<string | undefined>,1074@IDebugService private readonly debugService: IDebugService,1075@IHoverService private readonly hoverService: IHoverService,1076@ILabelService private readonly labelService: ILabelService1077) {1078// noop1079}10801081static readonly ID = 'functionbreakpoints';10821083get templateId() {1084return FunctionBreakpointsRenderer.ID;1085}10861087renderTemplate(container: HTMLElement): IFunctionBreakpointTemplateData {1088const data: IFunctionBreakpointTemplateData = Object.create(null);1089data.elementDisposables = new DisposableStore();1090data.templateDisposables = new DisposableStore();1091data.templateDisposables.add(data.elementDisposables);1092data.breakpoint = dom.append(container, $('.breakpoint'));10931094data.icon = $('.icon');1095data.checkbox = createCheckbox(data.templateDisposables);1096data.templateDisposables.add(data.checkbox.onChange(() => {1097this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);1098}));10991100dom.append(data.breakpoint, data.icon);1101dom.append(data.breakpoint, data.checkbox.domNode);11021103data.name = dom.append(data.breakpoint, $('span.name'));1104data.condition = dom.append(data.breakpoint, $('span.condition'));11051106data.actionBar = new ActionBar(data.breakpoint);1107data.templateDisposables.add(data.actionBar);1108const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));1109data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));11101111return data;1112}11131114renderElement(node: ITreeNode<FunctionBreakpoint, void>, _index: number, data: IFunctionBreakpointTemplateData): void {1115this.renderFunctionBreakpoint(node.element, data);1116}11171118renderCompressedElements(node: ITreeNode<ICompressedTreeNode<FunctionBreakpoint>, void>, _index: number, data: IFunctionBreakpointTemplateData): void {1119this.renderFunctionBreakpoint(node.element.elements[node.element.elements.length - 1], data);1120}11211122private renderFunctionBreakpoint(functionBreakpoint: FunctionBreakpoint, data: IFunctionBreakpointTemplateData): void {1123data.context = functionBreakpoint;1124data.name.textContent = functionBreakpoint.name;1125const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService, this.debugService.getModel());1126data.icon.className = ThemeIcon.asClassName(icon);1127data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));1128data.checkbox.checked = functionBreakpoint.enabled;1129data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, message ? message : ''));1130if (functionBreakpoint.condition && functionBreakpoint.hitCondition) {1131data.condition.textContent = localize('expressionAndHitCount', "Condition: {0} | Hit Count: {1}", functionBreakpoint.condition, functionBreakpoint.hitCondition);1132} else {1133data.condition.textContent = functionBreakpoint.condition || functionBreakpoint.hitCondition || '';1134}11351136if (functionBreakpoint.modeLabel) {1137data.badge.textContent = functionBreakpoint.modeLabel;1138data.badge.style.display = 'block';1139} else {1140data.badge.style.display = 'none';1141}11421143// Mark function breakpoints as disabled if deactivated or if debug type does not support them #90991144const session = this.debugService.getViewModel().focusedSession;1145data.breakpoint.classList.toggle('disabled', (session && !session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());1146if (session && !session.capabilities.supportsFunctionBreakpoints) {1147data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type")));1148}11491150this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);1151this.breakpointItemType.set('functionBreakpoint');1152const { primary } = getActionBarActions(this.menu.getActions({ arg: functionBreakpoint, shouldForwardArgs: true }), 'inline');1153data.actionBar.clear();1154data.actionBar.push(primary, { icon: true, label: false });1155breakpointIdToActionBarDomeNode.set(functionBreakpoint.getId(), data.actionBar.domNode);1156}11571158disposeElement(node: ITreeNode<FunctionBreakpoint, void>, index: number, templateData: IFunctionBreakpointTemplateData): void {1159templateData.elementDisposables.clear();1160}11611162disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<FunctionBreakpoint>, void>, index: number, templateData: IFunctionBreakpointTemplateData): void {1163templateData.elementDisposables.clear();1164}11651166disposeTemplate(templateData: IFunctionBreakpointTemplateData): void {1167templateData.templateDisposables.dispose();1168}1169}11701171class DataBreakpointsRenderer implements ICompressibleTreeRenderer<DataBreakpoint, void, IDataBreakpointTemplateData> {11721173constructor(1174private menu: IMenu,1175private breakpointHasMultipleModes: IContextKey<boolean>,1176private breakpointSupportsCondition: IContextKey<boolean>,1177private breakpointItemType: IContextKey<string | undefined>,1178private breakpointIsDataBytes: IContextKey<boolean | undefined>,1179@IDebugService private readonly debugService: IDebugService,1180@IHoverService private readonly hoverService: IHoverService,1181@ILabelService private readonly labelService: ILabelService1182) {1183// noop1184}11851186static readonly ID = 'databreakpoints';11871188get templateId() {1189return DataBreakpointsRenderer.ID;1190}11911192renderTemplate(container: HTMLElement): IDataBreakpointTemplateData {1193const data: IDataBreakpointTemplateData = Object.create(null);1194data.breakpoint = dom.append(container, $('.breakpoint'));1195data.elementDisposables = new DisposableStore();1196data.templateDisposables = new DisposableStore();1197data.templateDisposables.add(data.elementDisposables);11981199data.icon = $('.icon');1200data.checkbox = createCheckbox(data.templateDisposables);1201data.templateDisposables.add(data.checkbox.onChange(() => {1202this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);1203}));12041205dom.append(data.breakpoint, data.icon);1206dom.append(data.breakpoint, data.checkbox.domNode);12071208data.name = dom.append(data.breakpoint, $('span.name'));1209data.accessType = dom.append(data.breakpoint, $('span.access-type'));1210data.condition = dom.append(data.breakpoint, $('span.condition'));12111212data.actionBar = new ActionBar(data.breakpoint);1213data.templateDisposables.add(data.actionBar);1214const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));1215data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));12161217return data;1218}12191220renderElement(node: ITreeNode<DataBreakpoint, void>, _index: number, data: IDataBreakpointTemplateData): void {1221this.renderDataBreakpoint(node.element, data);1222}12231224renderCompressedElements(node: ITreeNode<ICompressedTreeNode<DataBreakpoint>, void>, _index: number, data: IDataBreakpointTemplateData): void {1225this.renderDataBreakpoint(node.element.elements[node.element.elements.length - 1], data);1226}12271228private renderDataBreakpoint(dataBreakpoint: DataBreakpoint, data: IDataBreakpointTemplateData): void {1229data.context = dataBreakpoint;1230data.name.textContent = dataBreakpoint.description;1231const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService, this.debugService.getModel());1232data.icon.className = ThemeIcon.asClassName(icon);1233data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));1234data.checkbox.checked = dataBreakpoint.enabled;1235data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, message ? message : ''));12361237if (dataBreakpoint.modeLabel) {1238data.badge.textContent = dataBreakpoint.modeLabel;1239data.badge.style.display = 'block';1240} else {1241data.badge.style.display = 'none';1242}12431244// Mark data breakpoints as disabled if deactivated or if debug type does not support them1245const session = this.debugService.getViewModel().focusedSession;1246data.breakpoint.classList.toggle('disabled', (session && !session.capabilities.supportsDataBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());1247if (session && !session.capabilities.supportsDataBreakpoints) {1248data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, localize('dataBreakpointsNotSupported', "Data breakpoints are not supported by this debug type")));1249}1250if (dataBreakpoint.accessType) {1251const accessType = dataBreakpoint.accessType === 'read' ? localize('read', "Read") : dataBreakpoint.accessType === 'write' ? localize('write', "Write") : localize('access', "Access");1252data.accessType.textContent = accessType;1253} else {1254data.accessType.textContent = '';1255}1256if (dataBreakpoint.condition && dataBreakpoint.hitCondition) {1257data.condition.textContent = localize('expressionAndHitCount', "Condition: {0} | Hit Count: {1}", dataBreakpoint.condition, dataBreakpoint.hitCondition);1258} else {1259data.condition.textContent = dataBreakpoint.condition || dataBreakpoint.hitCondition || '';1260}12611262this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);1263this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('data').length > 1);1264this.breakpointItemType.set('dataBreakpoint');1265this.breakpointIsDataBytes.set(dataBreakpoint.src.type === DataBreakpointSetType.Address);1266const { primary } = getActionBarActions(this.menu.getActions({ arg: dataBreakpoint, shouldForwardArgs: true }), 'inline');1267data.actionBar.clear();1268data.actionBar.push(primary, { icon: true, label: false });1269breakpointIdToActionBarDomeNode.set(dataBreakpoint.getId(), data.actionBar.domNode);1270this.breakpointIsDataBytes.reset();1271}12721273disposeElement(node: ITreeNode<DataBreakpoint, void>, index: number, templateData: IDataBreakpointTemplateData): void {1274templateData.elementDisposables.clear();1275}12761277disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<DataBreakpoint>, void>, index: number, templateData: IDataBreakpointTemplateData): void {1278templateData.elementDisposables.clear();1279}12801281disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {1282templateData.templateDisposables.dispose();1283}1284}12851286class InstructionBreakpointsRenderer implements ICompressibleTreeRenderer<IInstructionBreakpoint, void, IInstructionBreakpointTemplateData> {12871288constructor(1289@IDebugService private readonly debugService: IDebugService,1290@IHoverService private readonly hoverService: IHoverService,1291@ILabelService private readonly labelService: ILabelService1292) {1293// noop1294}12951296static readonly ID = 'instructionBreakpoints';12971298get templateId() {1299return InstructionBreakpointsRenderer.ID;1300}13011302renderTemplate(container: HTMLElement): IInstructionBreakpointTemplateData {1303const data: IInstructionBreakpointTemplateData = Object.create(null);1304data.elementDisposables = new DisposableStore();1305data.templateDisposables = new DisposableStore();1306data.templateDisposables.add(data.elementDisposables);1307data.breakpoint = dom.append(container, $('.breakpoint'));13081309data.icon = $('.icon');1310data.checkbox = createCheckbox(data.templateDisposables);1311data.templateDisposables.add(data.checkbox.onChange(() => {1312this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);1313}));13141315dom.append(data.breakpoint, data.icon);1316dom.append(data.breakpoint, data.checkbox.domNode);13171318data.name = dom.append(data.breakpoint, $('span.name'));13191320data.address = dom.append(data.breakpoint, $('span.file-path'));1321data.actionBar = new ActionBar(data.breakpoint);1322data.templateDisposables.add(data.actionBar);1323const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));1324data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));13251326return data;1327}13281329renderElement(node: ITreeNode<IInstructionBreakpoint, void>, index: number, data: IInstructionBreakpointTemplateData): void {1330this.renderInstructionBreakpoint(node.element, data);1331}13321333renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IInstructionBreakpoint>, void>, index: number, data: IInstructionBreakpointTemplateData): void {1334this.renderInstructionBreakpoint(node.element.elements[node.element.elements.length - 1], data);1335}13361337private renderInstructionBreakpoint(breakpoint: IInstructionBreakpoint, data: IInstructionBreakpointTemplateData): void {1338data.context = breakpoint;1339data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());13401341data.name.textContent = '0x' + breakpoint.address.toString(16);1342data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.name, localize('debug.decimal.address', "Decimal Address: {0}", breakpoint.address.toString())));1343data.checkbox.checked = breakpoint.enabled;13441345const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService, this.debugService.getModel());1346data.icon.className = ThemeIcon.asClassName(icon);1347data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, breakpoint.message || message || ''));13481349const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;1350if (debugActive && !breakpoint.verified) {1351data.breakpoint.classList.add('disabled');1352}13531354if (breakpoint.modeLabel) {1355data.badge.textContent = breakpoint.modeLabel;1356data.badge.style.display = 'block';1357} else {1358data.badge.style.display = 'none';1359}1360}13611362disposeElement(node: ITreeNode<IInstructionBreakpoint, void>, index: number, templateData: IInstructionBreakpointTemplateData): void {1363templateData.elementDisposables.clear();1364}13651366disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IInstructionBreakpoint>, void>, index: number, templateData: IInstructionBreakpointTemplateData): void {1367templateData.elementDisposables.clear();1368}13691370disposeTemplate(templateData: IInstructionBreakpointTemplateData): void {1371templateData.templateDisposables.dispose();1372}1373}13741375class FunctionBreakpointInputRenderer implements ICompressibleTreeRenderer<IFunctionBreakpoint, void, IFunctionBreakpointInputTemplateData> {13761377constructor(1378private view: BreakpointsView,1379private debugService: IDebugService,1380private contextViewService: IContextViewService,1381private readonly hoverService: IHoverService,1382private labelService: ILabelService1383) { }13841385static readonly ID = 'functionbreakpointinput';13861387get templateId() {1388return FunctionBreakpointInputRenderer.ID;1389}13901391renderTemplate(container: HTMLElement): IFunctionBreakpointInputTemplateData {1392const template: IFunctionBreakpointInputTemplateData = Object.create(null);1393const toDispose = new DisposableStore();13941395const breakpoint = dom.append(container, $('.breakpoint'));1396template.icon = $('.icon');1397template.checkbox = createCheckbox(toDispose);13981399dom.append(breakpoint, template.icon);1400dom.append(breakpoint, template.checkbox.domNode);1401this.view.breakpointInputFocused.set(true);1402const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));140314041405const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { inputBoxStyles: defaultInputBoxStyles });14061407toDispose.add(inputBox);14081409const wrapUp = (success: boolean) => {1410template.updating = true;1411try {1412this.view.breakpointInputFocused.set(false);1413const id = template.breakpoint.getId();14141415if (success) {1416if (template.type === 'name') {1417this.debugService.updateFunctionBreakpoint(id, { name: inputBox.value });1418}1419if (template.type === 'condition') {1420this.debugService.updateFunctionBreakpoint(id, { condition: inputBox.value });1421}1422if (template.type === 'hitCount') {1423this.debugService.updateFunctionBreakpoint(id, { hitCondition: inputBox.value });1424}1425} else {1426if (template.type === 'name' && !template.breakpoint.name) {1427this.debugService.removeFunctionBreakpoints(id);1428} else {1429this.view.renderInputBox(undefined);1430}1431}1432} finally {1433template.updating = false;1434}1435};14361437toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {1438const isEscape = e.equals(KeyCode.Escape);1439const isEnter = e.equals(KeyCode.Enter);1440if (isEscape || isEnter) {1441e.preventDefault();1442e.stopPropagation();1443wrapUp(isEnter);1444}1445}));1446toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {1447if (!template.updating) {1448wrapUp(!!inputBox.value);1449}1450}));14511452template.inputBox = inputBox;1453template.elementDisposables = new DisposableStore();1454template.templateDisposables = toDispose;1455template.templateDisposables.add(template.elementDisposables);1456return template;1457}14581459renderElement(node: ITreeNode<FunctionBreakpoint, void>, _index: number, data: IFunctionBreakpointInputTemplateData): void {1460const functionBreakpoint = node.element;1461data.breakpoint = functionBreakpoint;1462data.type = this.view.inputBoxData?.type || 'name'; // If there is no type set take the 'name' as the default1463const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService, this.debugService.getModel());14641465data.icon.className = ThemeIcon.asClassName(icon);1466data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));1467data.checkbox.checked = functionBreakpoint.enabled;1468data.checkbox.disable();1469data.inputBox.value = functionBreakpoint.name || '';14701471let placeholder = localize('functionBreakpointPlaceholder', "Function to break on");1472let ariaLabel = localize('functionBreakPointInputAriaLabel', "Type function breakpoint.");1473if (data.type === 'condition') {1474data.inputBox.value = functionBreakpoint.condition || '';1475placeholder = localize('functionBreakpointExpressionPlaceholder', "Break when expression evaluates to true");1476ariaLabel = localize('functionBreakPointExpresionAriaLabel', "Type expression. Function breakpoint will break when expression evaluates to true");1477} else if (data.type === 'hitCount') {1478data.inputBox.value = functionBreakpoint.hitCondition || '';1479placeholder = localize('functionBreakpointHitCountPlaceholder', "Break when hit count is met");1480ariaLabel = localize('functionBreakPointHitCountAriaLabel', "Type hit count. Function breakpoint will break when hit count is met.");1481}1482data.inputBox.setAriaLabel(ariaLabel);1483data.inputBox.setPlaceHolder(placeholder);14841485setTimeout(() => {1486data.inputBox.focus();1487data.inputBox.select();1488}, 0);1489}14901491renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IFunctionBreakpoint>, void>, _index: number, data: IFunctionBreakpointInputTemplateData): void {1492// Function breakpoints are not compressible1493}14941495disposeElement(node: ITreeNode<IFunctionBreakpoint, void>, index: number, templateData: IFunctionBreakpointInputTemplateData): void {1496templateData.elementDisposables.clear();1497}14981499disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IFunctionBreakpoint>, void>, index: number, templateData: IFunctionBreakpointInputTemplateData): void {1500templateData.elementDisposables.clear();1501}15021503disposeTemplate(templateData: IFunctionBreakpointInputTemplateData): void {1504templateData.templateDisposables.dispose();1505}1506}15071508class DataBreakpointInputRenderer implements ICompressibleTreeRenderer<IDataBreakpoint, void, IDataBreakpointInputTemplateData> {15091510constructor(1511private view: BreakpointsView,1512private debugService: IDebugService,1513private contextViewService: IContextViewService,1514private readonly hoverService: IHoverService,1515private labelService: ILabelService1516) { }15171518static readonly ID = 'databreakpointinput';15191520get templateId() {1521return DataBreakpointInputRenderer.ID;1522}15231524renderTemplate(container: HTMLElement): IDataBreakpointInputTemplateData {1525const template: IDataBreakpointInputTemplateData = Object.create(null);1526const toDispose = new DisposableStore();15271528const breakpoint = dom.append(container, $('.breakpoint'));1529template.icon = $('.icon');1530template.checkbox = createCheckbox(toDispose);15311532dom.append(breakpoint, template.icon);1533dom.append(breakpoint, template.checkbox.domNode);1534this.view.breakpointInputFocused.set(true);1535const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));153615371538const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { inputBoxStyles: defaultInputBoxStyles });1539toDispose.add(inputBox);15401541const wrapUp = (success: boolean) => {1542template.updating = true;1543try {1544this.view.breakpointInputFocused.set(false);1545const id = template.breakpoint.getId();15461547if (success) {1548if (template.type === 'condition') {1549this.debugService.updateDataBreakpoint(id, { condition: inputBox.value });1550}1551if (template.type === 'hitCount') {1552this.debugService.updateDataBreakpoint(id, { hitCondition: inputBox.value });1553}1554} else {1555this.view.renderInputBox(undefined);1556}1557} finally {1558template.updating = false;1559}1560};15611562toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {1563const isEscape = e.equals(KeyCode.Escape);1564const isEnter = e.equals(KeyCode.Enter);1565if (isEscape || isEnter) {1566e.preventDefault();1567e.stopPropagation();1568wrapUp(isEnter);1569}1570}));1571toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {1572if (!template.updating) {1573wrapUp(!!inputBox.value);1574}1575}));15761577template.inputBox = inputBox;1578template.elementDisposables = new DisposableStore();1579template.templateDisposables = toDispose;1580template.templateDisposables.add(template.elementDisposables);1581return template;1582}15831584renderElement(node: ITreeNode<DataBreakpoint, void>, _index: number, data: IDataBreakpointInputTemplateData): void {1585const dataBreakpoint = node.element;1586data.breakpoint = dataBreakpoint;1587data.type = this.view.inputBoxData?.type || 'condition'; // If there is no type set take the 'condition' as the default1588const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService, this.debugService.getModel());15891590data.icon.className = ThemeIcon.asClassName(icon);1591data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ?? ''));1592data.checkbox.checked = dataBreakpoint.enabled;1593data.checkbox.disable();1594data.inputBox.value = '';1595let placeholder = '';1596let ariaLabel = '';1597if (data.type === 'condition') {1598data.inputBox.value = dataBreakpoint.condition || '';1599placeholder = localize('dataBreakpointExpressionPlaceholder', "Break when expression evaluates to true");1600ariaLabel = localize('dataBreakPointExpresionAriaLabel', "Type expression. Data breakpoint will break when expression evaluates to true");1601} else if (data.type === 'hitCount') {1602data.inputBox.value = dataBreakpoint.hitCondition || '';1603placeholder = localize('dataBreakpointHitCountPlaceholder', "Break when hit count is met");1604ariaLabel = localize('dataBreakPointHitCountAriaLabel', "Type hit count. Data breakpoint will break when hit count is met.");1605}1606data.inputBox.setAriaLabel(ariaLabel);1607data.inputBox.setPlaceHolder(placeholder);16081609setTimeout(() => {1610data.inputBox.focus();1611data.inputBox.select();1612}, 0);1613}16141615renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IDataBreakpoint>, void>, _index: number, data: IDataBreakpointInputTemplateData): void {1616// Data breakpoints are not compressible1617}16181619disposeElement(node: ITreeNode<IDataBreakpoint, void>, index: number, templateData: IDataBreakpointInputTemplateData): void {1620templateData.elementDisposables.clear();1621}16221623disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IDataBreakpoint>, void>, index: number, templateData: IDataBreakpointInputTemplateData): void {1624templateData.elementDisposables.clear();1625}16261627disposeTemplate(templateData: IDataBreakpointInputTemplateData): void {1628templateData.templateDisposables.dispose();1629}1630}16311632class ExceptionBreakpointInputRenderer implements ICompressibleTreeRenderer<IExceptionBreakpoint, void, IExceptionBreakpointInputTemplateData> {16331634constructor(1635private view: BreakpointsView,1636private debugService: IDebugService,1637private contextViewService: IContextViewService,1638) {1639// noop1640}16411642static readonly ID = 'exceptionbreakpointinput';16431644get templateId() {1645return ExceptionBreakpointInputRenderer.ID;1646}16471648renderTemplate(container: HTMLElement): IExceptionBreakpointInputTemplateData {1649const toDispose = new DisposableStore();16501651const breakpoint = dom.append(container, $('.breakpoint'));1652breakpoint.classList.add('exception');1653const checkbox = createCheckbox(toDispose);16541655dom.append(breakpoint, checkbox.domNode);1656this.view.breakpointInputFocused.set(true);1657const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));1658const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {1659ariaLabel: localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition"),1660inputBoxStyles: defaultInputBoxStyles1661});166216631664toDispose.add(inputBox);1665const wrapUp = (success: boolean) => {1666if (!templateData.currentBreakpoint) {1667return;1668}16691670this.view.breakpointInputFocused.set(false);1671let newCondition = templateData.currentBreakpoint.condition;1672if (success) {1673newCondition = inputBox.value !== '' ? inputBox.value : undefined;1674}1675this.debugService.setExceptionBreakpointCondition(templateData.currentBreakpoint, newCondition);1676};16771678toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {1679const isEscape = e.equals(KeyCode.Escape);1680const isEnter = e.equals(KeyCode.Enter);1681if (isEscape || isEnter) {1682e.preventDefault();1683e.stopPropagation();1684wrapUp(isEnter);1685}1686}));1687toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {1688// Need to react with a timeout on the blur event due to possible concurent splices #564431689setTimeout(() => {1690wrapUp(true);1691});1692}));16931694const elementDisposables = new DisposableStore();1695toDispose.add(elementDisposables);16961697const templateData: IExceptionBreakpointInputTemplateData = {1698inputBox,1699checkbox,1700templateDisposables: toDispose,1701elementDisposables: new DisposableStore(),1702};17031704return templateData;1705}17061707renderElement(node: ITreeNode<ExceptionBreakpoint, void>, _index: number, data: IExceptionBreakpointInputTemplateData): void {1708const exceptionBreakpoint = node.element;1709const placeHolder = exceptionBreakpoint.conditionDescription || localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true");1710data.inputBox.setPlaceHolder(placeHolder);1711data.currentBreakpoint = exceptionBreakpoint;1712data.checkbox.checked = exceptionBreakpoint.enabled;1713data.checkbox.disable();1714data.inputBox.value = exceptionBreakpoint.condition || '';1715setTimeout(() => {1716data.inputBox.focus();1717data.inputBox.select();1718}, 0);1719}17201721renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IExceptionBreakpoint>, void>, _index: number, data: IExceptionBreakpointInputTemplateData): void {1722// Exception breakpoints are not compressible1723}17241725disposeElement(node: ITreeNode<IExceptionBreakpoint, void>, index: number, templateData: IExceptionBreakpointInputTemplateData): void {1726templateData.elementDisposables.clear();1727}17281729disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IExceptionBreakpoint>, void>, index: number, templateData: IExceptionBreakpointInputTemplateData): void {1730templateData.elementDisposables.clear();1731}17321733disposeTemplate(templateData: IExceptionBreakpointInputTemplateData): void {1734templateData.templateDisposables.dispose();1735}1736}17371738class BreakpointsAccessibilityProvider implements IListAccessibilityProvider<BreakpointTreeElement> {17391740constructor(1741private readonly debugService: IDebugService,1742private readonly labelService: ILabelService1743) { }17441745getWidgetAriaLabel(): string {1746return localize('breakpoints', "Breakpoints");1747}17481749getRole(): AriaRole {1750return 'checkbox';1751}17521753isChecked(element: BreakpointTreeElement) {1754if (element instanceof BreakpointsFolderItem) {1755return element.enabled;1756}1757return element.enabled;1758}17591760getAriaLabel(element: BreakpointTreeElement): string | null {1761if (element instanceof BreakpointsFolderItem) {1762return localize('breakpointFolder', "Breakpoints in {0}, {1} breakpoints", resources.basenameOrAuthority(element.uri), element.breakpoints.length);1763}17641765if (element instanceof ExceptionBreakpoint) {1766return element.toString();1767}17681769const { message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint, this.labelService, this.debugService.getModel());1770const toString = element.toString();17711772return message ? `${toString}, ${message}` : toString;1773}1774}17751776export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, pinned: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {1777if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {1778return Promise.resolve(undefined);1779}17801781const selection = breakpoint.endLineNumber ? {1782startLineNumber: breakpoint.lineNumber,1783endLineNumber: breakpoint.endLineNumber,1784startColumn: breakpoint.column || 1,1785endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER1786} : {1787startLineNumber: breakpoint.lineNumber,1788startColumn: breakpoint.column || 1,1789endLineNumber: breakpoint.lineNumber,1790endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER1791};17921793return editorService.openEditor({1794resource: breakpoint.uri,1795options: {1796preserveFocus,1797selection,1798revealIfOpened: true,1799selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,1800pinned1801}1802}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);1803}18041805export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: BreakpointItem, labelService: ILabelService, debugModel: IDebugModel): { message?: string; icon: ThemeIcon; showAdapterUnverifiedMessage?: boolean } {1806const debugActive = state === State.Running || state === State.Stopped;18071808const breakpointIcon = breakpoint instanceof DataBreakpoint ? icons.dataBreakpoint : breakpoint instanceof FunctionBreakpoint ? icons.functionBreakpoint : breakpoint.logMessage ? icons.logBreakpoint : icons.breakpoint;18091810if (!breakpoint.enabled || !breakpointsActivated) {1811return {1812icon: breakpointIcon.disabled,1813message: breakpoint.logMessage ? localize('disabledLogpoint', "Disabled Logpoint") : localize('disabledBreakpoint', "Disabled Breakpoint"),1814};1815}18161817const appendMessage = (text: string): string => {1818return breakpoint.message ? text.concat(', ' + breakpoint.message) : text;1819};18201821if (debugActive && breakpoint instanceof Breakpoint && breakpoint.pending) {1822return {1823icon: icons.breakpoint.pending1824};1825}18261827if (debugActive && !breakpoint.verified) {1828return {1829icon: breakpointIcon.unverified,1830message: breakpoint.message ? breakpoint.message : (breakpoint.logMessage ? localize('unverifiedLogpoint', "Unverified Logpoint") : localize('unverifiedBreakpoint', "Unverified Breakpoint")),1831showAdapterUnverifiedMessage: true1832};1833}18341835if (breakpoint instanceof DataBreakpoint) {1836if (!breakpoint.supported) {1837return {1838icon: breakpointIcon.unverified,1839message: localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),1840};1841}18421843return {1844icon: breakpointIcon.regular,1845message: breakpoint.message || localize('dataBreakpoint', "Data Breakpoint")1846};1847}18481849if (breakpoint instanceof FunctionBreakpoint) {1850if (!breakpoint.supported) {1851return {1852icon: breakpointIcon.unverified,1853message: localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),1854};1855}1856const messages: string[] = [];1857messages.push(breakpoint.message || localize('functionBreakpoint', "Function Breakpoint"));1858if (breakpoint.condition) {1859messages.push(localize('expression', "Condition: {0}", breakpoint.condition));1860}1861if (breakpoint.hitCondition) {1862messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));1863}18641865return {1866icon: breakpointIcon.regular,1867message: appendMessage(messages.join('\n'))1868};1869}18701871if (breakpoint instanceof InstructionBreakpoint) {1872if (!breakpoint.supported) {1873return {1874icon: breakpointIcon.unverified,1875message: localize('instructionBreakpointUnsupported', "Instruction breakpoints not supported by this debug type"),1876};1877}1878const messages: string[] = [];1879if (breakpoint.message) {1880messages.push(breakpoint.message);1881} else if (breakpoint.instructionReference) {1882messages.push(localize('instructionBreakpointAtAddress', "Instruction breakpoint at address {0}", breakpoint.instructionReference));1883} else {1884messages.push(localize('instructionBreakpoint', "Instruction breakpoint"));1885}18861887if (breakpoint.hitCondition) {1888messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));1889}18901891return {1892icon: breakpointIcon.regular,1893message: appendMessage(messages.join('\n'))1894};1895}18961897// can change this when all breakpoint supports dependent breakpoint condition1898let triggeringBreakpoint: IBreakpoint | undefined;1899if (breakpoint instanceof Breakpoint && breakpoint.triggeredBy) {1900triggeringBreakpoint = debugModel.getBreakpoints().find(bp => bp.getId() === breakpoint.triggeredBy);1901}19021903if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition || triggeringBreakpoint) {1904const messages: string[] = [];1905let icon = breakpoint.logMessage ? icons.logBreakpoint.regular : icons.conditionalBreakpoint.regular;1906if (!breakpoint.supported) {1907icon = icons.debugBreakpointUnsupported;1908messages.push(localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"));1909}19101911if (breakpoint.logMessage) {1912messages.push(localize('logMessage', "Log Message: {0}", breakpoint.logMessage));1913}1914if (breakpoint.condition) {1915messages.push(localize('expression', "Condition: {0}", breakpoint.condition));1916}1917if (breakpoint.hitCondition) {1918messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));1919}1920if (triggeringBreakpoint) {1921messages.push(localize('triggeredBy', "Hit after breakpoint: {0}", `${labelService.getUriLabel(triggeringBreakpoint.uri, { relative: true })}: ${triggeringBreakpoint.lineNumber}`));1922}19231924return {1925icon,1926message: appendMessage(messages.join('\n'))1927};1928}19291930const message = breakpoint.message ? breakpoint.message : breakpoint instanceof Breakpoint && labelService ? labelService.getUriLabel(breakpoint.uri) : localize('breakpoint', "Breakpoint");1931return {1932icon: breakpointIcon.regular,1933message1934};1935}19361937registerAction2(class extends Action2 {1938constructor() {1939super({1940id: 'workbench.debug.viewlet.action.addFunctionBreakpointAction',1941title: {1942...localize2('addFunctionBreakpoint', "Add Function Breakpoint"),1943mnemonicTitle: localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."),1944},1945f1: true,1946icon: icons.watchExpressionsAddFuncBreakpoint,1947menu: [{1948id: MenuId.ViewTitle,1949group: 'navigation',1950order: 10,1951when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)1952}, {1953id: MenuId.MenubarNewBreakpointMenu,1954group: '1_breakpoints',1955order: 3,1956when: CONTEXT_DEBUGGERS_AVAILABLE1957}]1958});1959}19601961async run(accessor: ServicesAccessor): Promise<void> {1962const debugService = accessor.get(IDebugService);1963const viewService = accessor.get(IViewsService);1964await viewService.openView(BREAKPOINTS_VIEW_ID);1965debugService.addFunctionBreakpoint();1966}1967});19681969abstract class MemoryBreakpointAction extends Action2 {1970async run(accessor: ServicesAccessor, existingBreakpoint?: IDataBreakpoint): Promise<void> {1971const debugService = accessor.get(IDebugService);1972const session = debugService.getViewModel().focusedSession;1973if (!session) {1974return;1975}19761977let defaultValue = undefined;1978if (existingBreakpoint && existingBreakpoint.src.type === DataBreakpointSetType.Address) {1979defaultValue = `${existingBreakpoint.src.address} + ${existingBreakpoint.src.bytes}`;1980}19811982const quickInput = accessor.get(IQuickInputService);1983const notifications = accessor.get(INotificationService);1984const range = await this.getRange(quickInput, defaultValue);1985if (!range) {1986return;1987}19881989let info: IDataBreakpointInfoResponse | undefined;1990try {1991info = await session.dataBytesBreakpointInfo(range.address, range.bytes);1992} catch (e) {1993notifications.error(localize('dataBreakpointError', "Failed to set data breakpoint at {0}: {1}", range.address, e.message));1994}19951996if (!info?.dataId) {1997return;1998}19992000let accessType: DebugProtocol.DataBreakpointAccessType = 'write';2001if (info.accessTypes && info.accessTypes?.length > 1) {2002const accessTypes = info.accessTypes.map(type => ({ label: type }));2003const selectedAccessType = await quickInput.pick(accessTypes, { placeHolder: localize('dataBreakpointAccessType', "Select the access type to monitor") });2004if (!selectedAccessType) {2005return;2006}20072008accessType = selectedAccessType.label;2009}20102011const src: DataBreakpointSource = { type: DataBreakpointSetType.Address, ...range };2012if (existingBreakpoint) {2013await debugService.removeDataBreakpoints(existingBreakpoint.getId());2014}20152016await debugService.addDataBreakpoint({2017description: info.description,2018src,2019canPersist: true,2020accessTypes: info.accessTypes,2021accessType: accessType,2022initialSessionData: { session, dataId: info.dataId }2023});2024}20252026private getRange(quickInput: IQuickInputService, defaultValue?: string) {2027return new Promise<{ address: string; bytes: number } | undefined>(resolve => {2028const disposables = new DisposableStore();2029const input = disposables.add(quickInput.createInputBox());2030input.prompt = localize('dataBreakpointMemoryRangePrompt', "Enter a memory range in which to break");2031input.placeholder = localize('dataBreakpointMemoryRangePlaceholder', 'Absolute range (0x1234 - 0x1300) or range of bytes after an address (0x1234 + 0xff)');2032if (defaultValue) {2033input.value = defaultValue;2034input.valueSelection = [0, defaultValue.length];2035}2036disposables.add(input.onDidChangeValue(e => {2037const err = this.parseAddress(e, false);2038input.validationMessage = err?.error;2039}));2040disposables.add(input.onDidAccept(() => {2041const r = this.parseAddress(input.value, true);2042if (hasKey(r, { error: true })) {2043input.validationMessage = r.error;2044} else {2045resolve(r);2046}2047input.dispose();2048}));2049disposables.add(input.onDidHide(() => {2050resolve(undefined);2051disposables.dispose();2052}));2053input.ignoreFocusOut = true;2054input.show();2055});2056}20572058private parseAddress(range: string, isFinal: false): { error: string } | undefined;2059private parseAddress(range: string, isFinal: true): { error: string } | { address: string; bytes: number };2060private parseAddress(range: string, isFinal: boolean): { error: string } | { address: string; bytes: number } | undefined {2061const parts = /^(\S+)\s*(?:([+-])\s*(\S+))?/.exec(range);2062if (!parts) {2063return { error: localize('dataBreakpointAddrFormat', 'Address should be a range of numbers the form "[Start] - [End]" or "[Start] + [Bytes]"') };2064}20652066const isNum = (e: string) => isFinal ? /^0x[0-9a-f]*|[0-9]*$/i.test(e) : /^0x[0-9a-f]+|[0-9]+$/i.test(e);2067const [, startStr, sign = '+', endStr = '1'] = parts;20682069for (const n of [startStr, endStr]) {2070if (!isNum(n)) {2071return { error: localize('dataBreakpointAddrStartEnd', 'Number must be a decimal integer or hex value starting with \"0x\", got {0}', n) };2072}2073}20742075if (!isFinal) {2076return;2077}20782079const start = BigInt(startStr);2080const end = BigInt(endStr);2081const address = `0x${start.toString(16)}`;2082if (sign === '-') {2083if (start > end) {2084return { error: localize('dataBreakpointAddrOrder', 'End ({1}) should be greater than Start ({0})', startStr, endStr) };2085}2086return { address, bytes: Number(end - start) };2087}20882089return { address, bytes: Number(end) };2090}2091}20922093registerAction2(class extends MemoryBreakpointAction {2094constructor() {2095super({2096id: 'workbench.debug.viewlet.action.addDataBreakpointOnAddress',2097title: {2098...localize2('addDataBreakpointOnAddress', "Add Data Breakpoint at Address"),2099mnemonicTitle: localize({ key: 'miDataBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Data Breakpoint..."),2100},2101f1: true,2102icon: icons.watchExpressionsAddDataBreakpoint,2103menu: [{2104id: MenuId.ViewTitle,2105group: 'navigation',2106order: 11,2107when: ContextKeyExpr.and(CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID))2108}, {2109id: MenuId.MenubarNewBreakpointMenu,2110group: '1_breakpoints',2111order: 4,2112when: CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED2113}]2114});2115}2116});21172118registerAction2(class extends MemoryBreakpointAction {2119constructor() {2120super({2121id: 'workbench.debug.viewlet.action.editDataBreakpointOnAddress',2122title: localize2('editDataBreakpointOnAddress', "Edit Address..."),2123menu: [{2124id: MenuId.DebugBreakpointsContext,2125when: ContextKeyExpr.and(CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES),2126group: 'navigation',2127order: 15,2128}]2129});2130}2131});21322133registerAction2(class extends Action2 {2134constructor() {2135super({2136id: 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction',2137title: localize2('activateBreakpoints', 'Toggle Activate Breakpoints'),2138f1: true,2139icon: icons.breakpointsActivate,2140menu: {2141id: MenuId.ViewTitle,2142group: 'navigation',2143order: 20,2144when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)2145}2146});2147}21482149run(accessor: ServicesAccessor): void {2150const debugService = accessor.get(IDebugService);2151debugService.setBreakpointsActivated(!debugService.getModel().areBreakpointsActivated());2152}2153});21542155registerAction2(class extends Action2 {2156constructor() {2157super({2158id: 'workbench.debug.viewlet.action.removeBreakpoint',2159title: localize('removeBreakpoint', "Remove Breakpoint"),2160icon: Codicon.removeClose,2161menu: [{2162id: MenuId.DebugBreakpointsContext,2163group: '3_modification',2164order: 10,2165when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint')2166}, {2167id: MenuId.DebugBreakpointsContext,2168group: 'inline',2169order: 20,2170when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint')2171}]2172});2173}21742175async run(accessor: ServicesAccessor, breakpoint: IBaseBreakpoint): Promise<void> {2176const debugService = accessor.get(IDebugService);2177if (breakpoint instanceof Breakpoint) {2178await debugService.removeBreakpoints(breakpoint.getId());2179} else if (breakpoint instanceof FunctionBreakpoint) {2180await debugService.removeFunctionBreakpoints(breakpoint.getId());2181} else if (breakpoint instanceof DataBreakpoint) {2182await debugService.removeDataBreakpoints(breakpoint.getId());2183} else if (breakpoint instanceof InstructionBreakpoint) {2184await debugService.removeInstructionBreakpoints(breakpoint.instructionReference, breakpoint.offset);2185}2186}2187});21882189registerAction2(class extends Action2 {2190constructor() {2191super({2192id: 'workbench.debug.viewlet.action.removeAllBreakpoints',2193title: {2194...localize2('removeAllBreakpoints', "Remove All Breakpoints"),2195mnemonicTitle: localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"),2196},2197f1: true,2198icon: icons.breakpointsRemoveAll,2199menu: [{2200id: MenuId.ViewTitle,2201group: 'navigation',2202order: 30,2203when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)2204}, {2205id: MenuId.DebugBreakpointsContext,2206group: '3_modification',2207order: 20,2208when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))2209}, {2210id: MenuId.MenubarDebugMenu,2211group: '5_breakpoints',2212order: 3,2213when: CONTEXT_DEBUGGERS_AVAILABLE2214}]2215});2216}22172218run(accessor: ServicesAccessor): void {2219const debugService = accessor.get(IDebugService);2220debugService.removeBreakpoints();2221debugService.removeFunctionBreakpoints();2222debugService.removeDataBreakpoints();2223debugService.removeInstructionBreakpoints();2224}2225});22262227registerAction2(class extends Action2 {2228constructor() {2229super({2230id: 'workbench.debug.viewlet.action.enableAllBreakpoints',2231title: {2232...localize2('enableAllBreakpoints', "Enable All Breakpoints"),2233mnemonicTitle: localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "&&Enable All Breakpoints"),2234},2235f1: true,2236precondition: CONTEXT_DEBUGGERS_AVAILABLE,2237menu: [{2238id: MenuId.DebugBreakpointsContext,2239group: 'z_commands',2240order: 10,2241when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))2242}, {2243id: MenuId.MenubarDebugMenu,2244group: '5_breakpoints',2245order: 1,2246when: CONTEXT_DEBUGGERS_AVAILABLE2247}]2248});2249}22502251async run(accessor: ServicesAccessor): Promise<void> {2252const debugService = accessor.get(IDebugService);2253await debugService.enableOrDisableBreakpoints(true);2254}2255});22562257registerAction2(class extends Action2 {2258constructor() {2259super({2260id: 'workbench.debug.viewlet.action.disableAllBreakpoints',2261title: {2262...localize2('disableAllBreakpoints', "Disable All Breakpoints"),2263mnemonicTitle: localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"),2264},2265f1: true,2266precondition: CONTEXT_DEBUGGERS_AVAILABLE,2267menu: [{2268id: MenuId.DebugBreakpointsContext,2269group: 'z_commands',2270order: 20,2271when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))2272}, {2273id: MenuId.MenubarDebugMenu,2274group: '5_breakpoints',2275order: 2,2276when: CONTEXT_DEBUGGERS_AVAILABLE2277}]2278});2279}22802281async run(accessor: ServicesAccessor): Promise<void> {2282const debugService = accessor.get(IDebugService);2283await debugService.enableOrDisableBreakpoints(false);2284}2285});22862287registerAction2(class extends Action2 {2288constructor() {2289super({2290id: 'workbench.debug.viewlet.action.reapplyBreakpointsAction',2291title: localize2('reapplyAllBreakpoints', 'Reapply All Breakpoints'),2292f1: true,2293precondition: CONTEXT_IN_DEBUG_MODE,2294menu: [{2295id: MenuId.DebugBreakpointsContext,2296group: 'z_commands',2297order: 30,2298when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))2299}]2300});2301}23022303async run(accessor: ServicesAccessor): Promise<void> {2304const debugService = accessor.get(IDebugService);2305await debugService.setBreakpointsActivated(true);2306}2307});23082309registerAction2(class extends Action2 {2310constructor() {2311super({2312id: 'workbench.debug.viewlet.action.toggleBreakpointsPresentation',2313title: localize2('toggleBreakpointsPresentation', "Toggle Breakpoints View Presentation"),2314f1: true,2315icon: icons.breakpointsViewIcon,2316menu: {2317id: MenuId.ViewTitle,2318group: 'navigation',2319order: 10,2320when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)2321}2322});2323}23242325async run(accessor: ServicesAccessor): Promise<void> {2326const configurationService = accessor.get(IConfigurationService);2327const currentPresentation = configurationService.getValue<'list' | 'tree'>('debug.breakpointsView.presentation');2328const newPresentation = currentPresentation === 'tree' ? 'list' : 'tree';2329await configurationService.updateValue('debug.breakpointsView.presentation', newPresentation);2330}2331});23322333registerAction2(class extends ViewAction<BreakpointsView> {2334constructor() {2335super({2336id: 'debug.editBreakpoint',2337viewId: BREAKPOINTS_VIEW_ID,2338title: localize('editCondition', "Edit Condition..."),2339icon: Codicon.edit,2340precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION,2341menu: [{2342id: MenuId.DebugBreakpointsContext,2343when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('functionBreakpoint'),2344group: 'navigation',2345order: 102346}, {2347id: MenuId.DebugBreakpointsContext,2348group: 'inline',2349order: 102350}]2351});2352}23532354async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: ExceptionBreakpoint | Breakpoint | FunctionBreakpoint | DataBreakpoint): Promise<void> {2355const debugService = accessor.get(IDebugService);2356const editorService = accessor.get(IEditorService);2357if (breakpoint instanceof Breakpoint) {2358const editor = await openBreakpointSource(breakpoint, false, false, true, debugService, editorService);2359if (editor) {2360const codeEditor = editor.getControl();2361if (isCodeEditor(codeEditor)) {2362codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(breakpoint.lineNumber, breakpoint.column);2363}2364}2365} else if (breakpoint instanceof FunctionBreakpoint) {2366const contextMenuService = accessor.get(IContextMenuService);2367const actions: Action[] = [new Action('breakpoint.editCondition', localize('editCondition', "Edit Condition..."), undefined, true, async () => view.renderInputBox({ breakpoint, type: 'condition' })),2368new Action('breakpoint.editCondition', localize('editHitCount', "Edit Hit Count..."), undefined, true, async () => view.renderInputBox({ breakpoint, type: 'hitCount' }))];2369const domNode = breakpointIdToActionBarDomeNode.get(breakpoint.getId());23702371if (domNode) {2372contextMenuService.showContextMenu({2373getActions: () => actions,2374getAnchor: () => domNode,2375onHide: () => dispose(actions)2376});2377}2378} else {2379view.renderInputBox({ breakpoint, type: 'condition' });2380}2381}2382});238323842385registerAction2(class extends ViewAction<BreakpointsView> {2386constructor() {2387super({2388id: 'debug.editFunctionBreakpoint',2389viewId: BREAKPOINTS_VIEW_ID,2390title: localize('editBreakpoint', "Edit Function Condition..."),2391menu: [{2392id: MenuId.DebugBreakpointsContext,2393group: 'navigation',2394order: 10,2395when: CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint')2396}]2397});2398}23992400runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) {2401view.renderInputBox({ breakpoint, type: 'name' });2402}2403});24042405registerAction2(class extends ViewAction<BreakpointsView> {2406constructor() {2407super({2408id: 'debug.editFunctionBreakpointHitCount',2409viewId: BREAKPOINTS_VIEW_ID,2410title: localize('editHitCount', "Edit Hit Count..."),2411precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION,2412menu: [{2413id: MenuId.DebugBreakpointsContext,2414group: 'navigation',2415order: 20,2416when: ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('dataBreakpoint'))2417}]2418});2419}24202421runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) {2422view.renderInputBox({ breakpoint, type: 'hitCount' });2423}2424});24252426registerAction2(class extends ViewAction<BreakpointsView> {2427constructor() {2428super({2429id: 'debug.editBreakpointMode',2430viewId: BREAKPOINTS_VIEW_ID,2431title: localize('editMode', "Edit Mode..."),2432menu: [{2433id: MenuId.DebugBreakpointsContext,2434group: 'navigation',2435order: 20,2436when: ContextKeyExpr.and(2437CONTEXT_BREAKPOINT_HAS_MODES,2438ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('breakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('exceptionBreakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('instructionBreakpoint'))2439)2440}]2441});2442}24432444async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IBreakpoint) {2445const debugService = accessor.get(IDebugService);2446const kind = getModeKindForBreakpoint(breakpoint);2447const modes = debugService.getModel().getBreakpointModes(kind);2448const picked = await accessor.get(IQuickInputService).pick(2449modes.map(mode => ({ label: mode.label, description: mode.description, mode: mode.mode })),2450{ placeHolder: localize('selectBreakpointMode', "Select Breakpoint Mode") }2451);24522453if (!picked) {2454return;2455}24562457if (kind === 'source') {2458const data = new Map<string, IBreakpointUpdateData>();2459data.set(breakpoint.getId(), { mode: picked.mode, modeLabel: picked.label });2460debugService.updateBreakpoints(breakpoint.originalUri, data, false);2461} else if (breakpoint instanceof InstructionBreakpoint) {2462debugService.removeInstructionBreakpoints(breakpoint.instructionReference, breakpoint.offset);2463debugService.addInstructionBreakpoint({ ...breakpoint.toJSON(), mode: picked.mode, modeLabel: picked.label });2464} else if (breakpoint instanceof ExceptionBreakpoint) {2465breakpoint.mode = picked.mode;2466breakpoint.modeLabel = picked.label;2467debugService.setExceptionBreakpointCondition(breakpoint, breakpoint.condition); // no-op to trigger a re-send2468}2469}2470});247124722473