Path: blob/main/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts
3296 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 { IDragAndDropData } from '../../../../base/browser/dnd.js';6import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';7import { IHighlight } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js';8import { IListVirtualDelegate, ListDragOverEffectPosition, ListDragOverEffectType } from '../../../../base/browser/ui/list/list.js';9import { ElementsDragAndDropData, ListViewTargetSector } from '../../../../base/browser/ui/list/listView.js';10import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';11import { ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeMouseEvent, ITreeNode } from '../../../../base/browser/ui/tree/tree.js';12import { RunOnceScheduler } from '../../../../base/common/async.js';13import { Codicon } from '../../../../base/common/codicons.js';14import { FuzzyScore } from '../../../../base/common/filters.js';15import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';16import { localize } from '../../../../nls.js';17import { getContextMenuActions, } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';18import { Action2, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';19import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';20import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';21import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';22import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js';23import { IHoverService } from '../../../../platform/hover/browser/hover.js';24import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';25import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';26import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';27import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js';28import { IOpenerService } from '../../../../platform/opener/common/opener.js';29import { IThemeService } from '../../../../platform/theme/common/themeService.js';30import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js';31import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js';32import { FocusedViewContext } from '../../../common/contextkeys.js';33import { IViewDescriptorService } from '../../../common/views.js';34import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_EXPRESSION_SELECTED, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_VARIABLE_TYPE, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, WATCH_VIEW_ID, CONTEXT_DEBUG_TYPE } from '../common/debug.js';35import { Expression, Variable, VisualizedExpression } from '../common/debugModel.js';36import { AbstractExpressionDataSource, AbstractExpressionsRenderer, expressionAndScopeLabelProvider, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js';37import { COPY_WATCH_EXPRESSION_COMMAND_ID, setDataBreakpointInfoResponse } from './debugCommands.js';38import { DebugExpressionRenderer } from './debugExpressionRenderer.js';39import { watchExpressionsAdd, watchExpressionsRemoveAll } from './debugIcons.js';40import { VariablesRenderer, VisualizedVariableRenderer } from './variablesView.js';4142const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;43let ignoreViewUpdates = false;44let useCachedEvaluation = false;4546export class WatchExpressionsView extends ViewPane implements IDebugViewWithVariables {4748private watchExpressionsUpdatedScheduler: RunOnceScheduler;49private needsRefresh = false;50private tree!: WorkbenchAsyncDataTree<IDebugService | IExpression, IExpression, FuzzyScore>;51private watchExpressionsExist: IContextKey<boolean>;52private expressionRenderer: DebugExpressionRenderer;5354public get treeSelection() {55return this.tree.getSelection();56}5758constructor(59options: IViewletViewOptions,60@IContextMenuService contextMenuService: IContextMenuService,61@IDebugService private readonly debugService: IDebugService,62@IKeybindingService keybindingService: IKeybindingService,63@IInstantiationService instantiationService: IInstantiationService,64@IViewDescriptorService viewDescriptorService: IViewDescriptorService,65@IConfigurationService configurationService: IConfigurationService,66@IContextKeyService contextKeyService: IContextKeyService,67@IOpenerService openerService: IOpenerService,68@IThemeService themeService: IThemeService,69@IHoverService hoverService: IHoverService,70@IMenuService private readonly menuService: IMenuService71) {72super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);7374this.watchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {75this.needsRefresh = false;76this.tree.updateChildren();77}, 50);78this.watchExpressionsExist = CONTEXT_WATCH_EXPRESSIONS_EXIST.bindTo(contextKeyService);79this.watchExpressionsExist.set(this.debugService.getModel().getWatchExpressions().length > 0);80this.expressionRenderer = instantiationService.createInstance(DebugExpressionRenderer);81}8283protected override renderBody(container: HTMLElement): void {84super.renderBody(container);8586this.element.classList.add('debug-pane');87container.classList.add('debug-watch');88const treeContainer = renderViewTree(container);8990const expressionsRenderer = this.instantiationService.createInstance(WatchExpressionsRenderer, this.expressionRenderer);91this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree<IDebugService | IExpression, IExpression, FuzzyScore>, 'WatchExpressions', treeContainer, new WatchExpressionsDelegate(),92[93expressionsRenderer,94this.instantiationService.createInstance(VariablesRenderer, this.expressionRenderer),95this.instantiationService.createInstance(VisualizedVariableRenderer, this.expressionRenderer),96],97this.instantiationService.createInstance(WatchExpressionsDataSource), {98accessibilityProvider: new WatchExpressionsAccessibilityProvider(),99identityProvider: { getId: (element: IExpression) => element.getId() },100keyboardNavigationLabelProvider: {101getKeyboardNavigationLabel: (e: IExpression) => {102if (e === this.debugService.getViewModel().getSelectedExpression()?.expression) {103// Don't filter input box104return undefined;105}106107return expressionAndScopeLabelProvider.getKeyboardNavigationLabel(e);108}109},110dnd: new WatchExpressionsDragAndDrop(this.debugService),111overrideStyles: this.getLocationBasedColors().listOverrideStyles112});113this._register(this.tree);114this.tree.setInput(this.debugService);115CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);116117this._register(VisualizedVariableRenderer.rendererOnVisualizationRange(this.debugService.getViewModel(), this.tree));118this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));119this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e)));120this._register(this.debugService.getModel().onDidChangeWatchExpressions(async we => {121this.watchExpressionsExist.set(this.debugService.getModel().getWatchExpressions().length > 0);122if (!this.isBodyVisible()) {123this.needsRefresh = true;124} else {125if (we && !we.name) {126// We are adding a new input box, no need to re-evaluate watch expressions127useCachedEvaluation = true;128}129await this.tree.updateChildren();130useCachedEvaluation = false;131if (we instanceof Expression) {132this.tree.reveal(we);133}134}135}));136this._register(this.debugService.getViewModel().onDidFocusStackFrame(() => {137if (!this.isBodyVisible()) {138this.needsRefresh = true;139return;140}141142if (!this.watchExpressionsUpdatedScheduler.isScheduled()) {143this.watchExpressionsUpdatedScheduler.schedule();144}145}));146this._register(this.debugService.getViewModel().onWillUpdateViews(() => {147if (!ignoreViewUpdates) {148this.tree.updateChildren();149}150}));151152this._register(this.onDidChangeBodyVisibility(visible => {153if (visible && this.needsRefresh) {154this.watchExpressionsUpdatedScheduler.schedule();155}156}));157let horizontalScrolling: boolean | undefined;158this._register(this.debugService.getViewModel().onDidSelectExpression(e => {159const expression = e?.expression;160if (expression && this.tree.hasNode(expression)) {161horizontalScrolling = this.tree.options.horizontalScrolling;162if (horizontalScrolling) {163this.tree.updateOptions({ horizontalScrolling: false });164}165166if (expression.name) {167// Only rerender if the input is already done since otherwise the tree is not yet aware of the new element168this.tree.rerender(expression);169}170} else if (!expression && horizontalScrolling !== undefined) {171this.tree.updateOptions({ horizontalScrolling: horizontalScrolling });172horizontalScrolling = undefined;173}174}));175176this._register(this.debugService.getViewModel().onDidEvaluateLazyExpression(async e => {177if (e instanceof Variable && this.tree.hasNode(e)) {178await this.tree.updateChildren(e, false, true);179await this.tree.expand(e);180}181}));182}183184protected override layoutBody(height: number, width: number): void {185super.layoutBody(height, width);186this.tree.layout(height, width);187}188189override focus(): void {190super.focus();191this.tree.domFocus();192}193194collapseAll(): void {195this.tree.collapseAll();196}197198private onMouseDblClick(e: ITreeMouseEvent<IExpression>): void {199if ((e.browserEvent.target as HTMLElement).className.indexOf('twistie') >= 0) {200// Ignore double click events on twistie201return;202}203204const element = e.element;205// double click on primitive value: open input box to be able to select and copy value.206const selectedExpression = this.debugService.getViewModel().getSelectedExpression();207if ((element instanceof Expression && element !== selectedExpression?.expression) || (element instanceof VisualizedExpression && element.treeItem.canEdit)) {208this.debugService.getViewModel().setSelectedExpression(element, false);209} else if (!element) {210// Double click in watch panel triggers to add a new watch expression211this.debugService.addWatchExpression();212}213}214215private async onContextMenu(e: ITreeContextMenuEvent<IExpression>): Promise<void> {216const element = e.element;217if (!element) {218return;219}220221const selection = this.tree.getSelection();222223const contextKeyService = element && await getContextForWatchExpressionMenuWithDataAccess(this.contextKeyService, element);224const menu = this.menuService.getMenuActions(MenuId.DebugWatchContext, contextKeyService, { arg: element, shouldForwardArgs: false });225const { secondary } = getContextMenuActions(menu, 'inline');226227// const actions = getFlatContextMenuActions(this.menu.getActions({ arg: element, shouldForwardArgs: true }));228this.contextMenuService.showContextMenu({229getAnchor: () => e.anchor,230getActions: () => secondary,231getActionsContext: () => element && selection.includes(element) ? selection : element ? [element] : [],232});233}234}235236class WatchExpressionsDelegate implements IListVirtualDelegate<IExpression> {237238getHeight(_element: IExpression): number {239return 22;240}241242getTemplateId(element: IExpression): string {243if (element instanceof Expression) {244return WatchExpressionsRenderer.ID;245}246247if (element instanceof VisualizedExpression) {248return VisualizedVariableRenderer.ID;249}250251// Variable252return VariablesRenderer.ID;253}254}255256function isDebugService(element: any): element is IDebugService {257return typeof element.getConfigurationManager === 'function';258}259260class WatchExpressionsDataSource extends AbstractExpressionDataSource<IDebugService, IExpression> {261262public override hasChildren(element: IExpression | IDebugService): boolean {263return isDebugService(element) || element.hasChildren;264}265266protected override doGetChildren(element: IDebugService | IExpression): Promise<Array<IExpression>> {267if (isDebugService(element)) {268const debugService = element as IDebugService;269const watchExpressions = debugService.getModel().getWatchExpressions();270const viewModel = debugService.getViewModel();271return Promise.all(watchExpressions.map(we => !!we.name && !useCachedEvaluation272? we.evaluate(viewModel.focusedSession!, viewModel.focusedStackFrame!, 'watch').then(() => we)273: Promise.resolve(we)));274}275276return element.getChildren();277}278}279280281export class WatchExpressionsRenderer extends AbstractExpressionsRenderer {282283static readonly ID = 'watchexpression';284285constructor(286private readonly expressionRenderer: DebugExpressionRenderer,287@IMenuService private readonly menuService: IMenuService,288@IContextKeyService private readonly contextKeyService: IContextKeyService,289@IDebugService debugService: IDebugService,290@IContextViewService contextViewService: IContextViewService,291@IHoverService hoverService: IHoverService,292@IConfigurationService private configurationService: IConfigurationService,293) {294super(debugService, contextViewService, hoverService);295}296297get templateId() {298return WatchExpressionsRenderer.ID;299}300301public override renderElement(node: ITreeNode<IExpression, FuzzyScore>, index: number, data: IExpressionTemplateData): void {302data.elementDisposable.clear();303data.elementDisposable.add(this.configurationService.onDidChangeConfiguration(e => {304if (e.affectsConfiguration('debug.showVariableTypes')) {305super.renderExpressionElement(node.element, node, data);306}307}));308super.renderExpressionElement(node.element, node, data);309}310311protected renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void {312let text: string;313data.type.textContent = '';314const showType = this.configurationService.getValue<IDebugConfiguration>('debug').showVariableTypes;315if (showType && expression.type) {316text = typeof expression.value === 'string' ? `${expression.name}: ` : expression.name;317//render type318data.type.textContent = expression.type + ' =';319} else {320text = typeof expression.value === 'string' ? `${expression.name} =` : expression.name;321}322323let title: string;324if (expression.type) {325if (showType) {326title = `${expression.name}`;327} else {328title = expression.type === expression.value ?329expression.type :330`${expression.type}`;331}332} else {333title = expression.value;334}335336data.label.set(text, highlights, title);337data.elementDisposable.add(this.expressionRenderer.renderValue(data.value, expression, {338showChanged: true,339maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,340colorize: true,341session: expression.getSession(),342}));343}344345protected getInputBoxOptions(expression: IExpression, settingValue: boolean): IInputBoxOptions {346if (settingValue) {347return {348initialValue: expression.value,349ariaLabel: localize('typeNewValue', "Type new value"),350onFinish: async (value: string, success: boolean) => {351if (success && value) {352const focusedFrame = this.debugService.getViewModel().focusedStackFrame;353if (focusedFrame && (expression instanceof Variable || expression instanceof Expression)) {354await expression.setExpression(value, focusedFrame);355this.debugService.getViewModel().updateViews();356}357}358}359};360}361362return {363initialValue: expression.name ? expression.name : '',364ariaLabel: localize('watchExpressionInputAriaLabel', "Type watch expression"),365placeholder: localize('watchExpressionPlaceholder', "Expression to watch"),366onFinish: (value: string, success: boolean) => {367if (success && value) {368this.debugService.renameWatchExpression(expression.getId(), value);369ignoreViewUpdates = true;370this.debugService.getViewModel().updateViews();371ignoreViewUpdates = false;372} else if (!expression.name) {373this.debugService.removeWatchExpressions(expression.getId());374}375}376};377}378379protected override renderActionBar(actionBar: ActionBar, expression: IExpression) {380const contextKeyService = getContextForWatchExpressionMenu(this.contextKeyService, expression);381const context = expression;382const menu = this.menuService.getMenuActions(MenuId.DebugWatchContext, contextKeyService, { arg: context, shouldForwardArgs: false });383384const { primary } = getContextMenuActions(menu, 'inline');385386actionBar.clear();387actionBar.context = context;388actionBar.push(primary, { icon: true, label: false });389}390}391392/**393* Gets a context key overlay that has context for the given expression.394*/395function getContextForWatchExpressionMenu(parentContext: IContextKeyService, expression: IExpression, additionalContext: [string, unknown][] = []) {396const session = expression.getSession();397return parentContext.createOverlay([398[CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT.key, 'evaluateName' in expression],399[CONTEXT_WATCH_ITEM_TYPE.key, expression instanceof Expression ? 'expression' : expression instanceof Variable ? 'variable' : undefined],400[CONTEXT_CAN_VIEW_MEMORY.key, !!session?.capabilities.supportsReadMemoryRequest && expression.memoryReference !== undefined],401[CONTEXT_VARIABLE_IS_READONLY.key, !!expression.presentationHint?.attributes?.includes('readOnly') || expression.presentationHint?.lazy],402[CONTEXT_VARIABLE_TYPE.key, expression.type],403[CONTEXT_DEBUG_TYPE.key, session?.configuration.type],404...additionalContext405]);406}407408/**409* Gets a context key overlay that has context for the given expression, including data access info.410*/411async function getContextForWatchExpressionMenuWithDataAccess(parentContext: IContextKeyService, expression: IExpression) {412const session = expression.getSession();413if (!session || !session.capabilities.supportsDataBreakpoints) {414return getContextForWatchExpressionMenu(parentContext, expression);415}416417const contextKeys: [string, unknown][] = [];418const dataBreakpointInfoResponse = await session.dataBreakpointInfo('evaluateName' in expression ? expression.evaluateName as string : expression.name);419const dataBreakpointId = dataBreakpointInfoResponse?.dataId;420const dataBreakpointAccessTypes = dataBreakpointInfoResponse?.accessTypes;421setDataBreakpointInfoResponse(dataBreakpointInfoResponse);422423if (!dataBreakpointAccessTypes) {424contextKeys.push([CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED.key, !!dataBreakpointId]);425} else {426for (const accessType of dataBreakpointAccessTypes) {427switch (accessType) {428case 'read':429contextKeys.push([CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED.key, !!dataBreakpointId]);430break;431case 'write':432contextKeys.push([CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED.key, !!dataBreakpointId]);433break;434case 'readWrite':435contextKeys.push([CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED.key, !!dataBreakpointId]);436break;437}438}439}440441return getContextForWatchExpressionMenu(parentContext, expression, contextKeys);442}443444445class WatchExpressionsAccessibilityProvider implements IListAccessibilityProvider<IExpression> {446447getWidgetAriaLabel(): string {448return localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions");449}450451getAriaLabel(element: IExpression): string {452if (element instanceof Expression) {453return localize('watchExpressionAriaLabel', "{0}, value {1}", (<Expression>element).name, (<Expression>element).value);454}455456// Variable457return localize('watchVariableAriaLabel', "{0}, value {1}", (<Variable>element).name, (<Variable>element).value);458}459}460461class WatchExpressionsDragAndDrop implements ITreeDragAndDrop<IExpression> {462463constructor(private debugService: IDebugService) { }464onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void {465if (data instanceof ElementsDragAndDropData) {466originalEvent.dataTransfer!.setData('text/plain', data.elements[0].name);467}468}469470onDragOver(data: IDragAndDropData, targetElement: IExpression | undefined, targetIndex: number | undefined, targetSector: ListViewTargetSector | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction {471if (!(data instanceof ElementsDragAndDropData)) {472return false;473}474475const expressions = (data as ElementsDragAndDropData<IExpression>).elements;476if (!(expressions.length > 0 && expressions[0] instanceof Expression)) {477return false;478}479480let dropEffectPosition: ListDragOverEffectPosition | undefined = undefined;481if (targetIndex === undefined) {482// Hovering over the list483dropEffectPosition = ListDragOverEffectPosition.After;484targetIndex = -1;485} else {486// Hovering over an element487switch (targetSector) {488case ListViewTargetSector.TOP:489case ListViewTargetSector.CENTER_TOP:490dropEffectPosition = ListDragOverEffectPosition.Before; break;491case ListViewTargetSector.CENTER_BOTTOM:492case ListViewTargetSector.BOTTOM:493dropEffectPosition = ListDragOverEffectPosition.After; break;494}495}496497return { accept: true, effect: { type: ListDragOverEffectType.Move, position: dropEffectPosition }, feedback: [targetIndex] } satisfies ITreeDragOverReaction;498}499500getDragURI(element: IExpression): string | null {501if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()?.expression) {502return null;503}504505return element.getId();506}507508getDragLabel(elements: IExpression[]): string | undefined {509if (elements.length === 1) {510return elements[0].name;511}512513return undefined;514}515516drop(data: IDragAndDropData, targetElement: IExpression, targetIndex: number | undefined, targetSector: ListViewTargetSector | undefined, originalEvent: DragEvent): void {517if (!(data instanceof ElementsDragAndDropData)) {518return;519}520521const draggedElement = (data as ElementsDragAndDropData<IExpression>).elements[0];522if (!(draggedElement instanceof Expression)) {523throw new Error('Invalid dragged element');524}525526const watches = this.debugService.getModel().getWatchExpressions();527const sourcePosition = watches.indexOf(draggedElement);528529let targetPosition;530if (targetElement instanceof Expression) {531targetPosition = watches.indexOf(targetElement);532533switch (targetSector) {534case ListViewTargetSector.BOTTOM:535case ListViewTargetSector.CENTER_BOTTOM:536targetPosition++; break;537}538539if (sourcePosition < targetPosition) {540targetPosition--;541}542} else {543targetPosition = watches.length - 1;544}545546this.debugService.moveWatchExpression(draggedElement.getId(), targetPosition);547}548549dispose(): void { }550}551552registerAction2(class Collapse extends ViewAction<WatchExpressionsView> {553constructor() {554super({555id: 'watch.collapse',556viewId: WATCH_VIEW_ID,557title: localize('collapse', "Collapse All"),558f1: false,559icon: Codicon.collapseAll,560precondition: CONTEXT_WATCH_EXPRESSIONS_EXIST,561menu: {562id: MenuId.ViewTitle,563order: 30,564group: 'navigation',565when: ContextKeyExpr.equals('view', WATCH_VIEW_ID)566}567});568}569570runInView(_accessor: ServicesAccessor, view: WatchExpressionsView) {571view.collapseAll();572}573});574575export const ADD_WATCH_ID = 'workbench.debug.viewlet.action.addWatchExpression'; // Use old and long id for backwards compatibility576export const ADD_WATCH_LABEL = localize('addWatchExpression', "Add Expression");577578registerAction2(class AddWatchExpressionAction extends Action2 {579constructor() {580super({581id: ADD_WATCH_ID,582title: ADD_WATCH_LABEL,583f1: false,584icon: watchExpressionsAdd,585menu: {586id: MenuId.ViewTitle,587group: 'navigation',588when: ContextKeyExpr.equals('view', WATCH_VIEW_ID)589}590});591}592593run(accessor: ServicesAccessor): void {594const debugService = accessor.get(IDebugService);595debugService.addWatchExpression();596}597});598599export const REMOVE_WATCH_EXPRESSIONS_COMMAND_ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions';600export const REMOVE_WATCH_EXPRESSIONS_LABEL = localize('removeAllWatchExpressions', "Remove All Expressions");601registerAction2(class RemoveAllWatchExpressionsAction extends Action2 {602constructor() {603super({604id: REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, // Use old and long id for backwards compatibility605title: REMOVE_WATCH_EXPRESSIONS_LABEL,606f1: false,607icon: watchExpressionsRemoveAll,608precondition: CONTEXT_WATCH_EXPRESSIONS_EXIST,609menu: {610id: MenuId.ViewTitle,611order: 20,612group: 'navigation',613when: ContextKeyExpr.equals('view', WATCH_VIEW_ID)614}615});616}617618run(accessor: ServicesAccessor): void {619const debugService = accessor.get(IDebugService);620debugService.removeWatchExpressions();621}622});623624registerAction2(class CopyExpression extends ViewAction<WatchExpressionsView> {625constructor() {626super({627id: COPY_WATCH_EXPRESSION_COMMAND_ID,628title: localize('copyWatchExpression', "Copy Expression"),629f1: false,630viewId: WATCH_VIEW_ID,631precondition: CONTEXT_WATCH_EXPRESSIONS_EXIST,632keybinding: {633primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyC,634weight: KeybindingWeight.WorkbenchContrib,635when: ContextKeyExpr.and(636FocusedViewContext.isEqualTo(WATCH_VIEW_ID),637CONTEXT_EXPRESSION_SELECTED.negate(),638),639},640menu: {641id: MenuId.DebugWatchContext,642order: 20,643group: '3_modification',644when: CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression')645}646});647}648649runInView(accessor: ServicesAccessor, view: WatchExpressionsView, value?: IExpression): void {650const clipboardService = accessor.get(IClipboardService);651if (!value) {652value = view.treeSelection.at(-1);653}654if (value) {655clipboardService.writeText(value.name);656}657}658});659660661