debug: make ctrl+c copy value(s) in debug views (#236501)

Not sure how this ever worked, but it was reported as a bug, and this makes it work.

Fixes #232767
pull/236509/head
Connor Peet 2024-12-18 10:40:34 -08:00 committed by GitHub
parent 4fcae8834d
commit 4c3f5de789
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 6 deletions

View File

@ -63,6 +63,8 @@ import { ReplAccessibilityHelp } from './replAccessibilityHelp.js';
import { ReplAccessibilityAnnouncer } from '../common/replAccessibilityAnnouncer.js';
import { RunAndDebugAccessibilityHelp } from './runAndDebugAccessibilityHelp.js';
import { DebugWatchAccessibilityAnnouncer } from '../common/debugAccessibilityAnnouncer.js';
import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { FocusedViewContext } from '../../../common/contextkeys.js';
const debugCategory = nls.localize('debugCategory', "Debug");
registerColors();
@ -206,6 +208,16 @@ registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_WATCH_EXPRESSIONS_COM
registerDebugViewMenuItem(MenuId.NotebookVariablesContext, COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL, 20, CONTEXT_VARIABLE_VALUE);
KeybindingsRegistry.registerKeybindingRule({
id: COPY_VALUE_ID,
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.or(
FocusedViewContext.isEqualTo(WATCH_VIEW_ID),
FocusedViewContext.isEqualTo(VARIABLES_VIEW_ID),
),
primary: KeyMod.CtrlCmd | KeyCode.KeyC
});
// Touch Bar
if (isMacintosh) {

View File

@ -40,8 +40,9 @@ import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.j
import { IViewDescriptorService } from '../../../common/views.js';
import { IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { IViewsService } from '../../../services/views/common/viewsService.js';
import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js';
import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, DataBreakpointSetType, DebugVisualizationType, IDataBreakpointInfoResponse, IDebugService, IExpression, IScope, IStackFrame, IViewModel, VARIABLES_VIEW_ID } from '../common/debug.js';
import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, DataBreakpointSetType, DebugVisualizationType, IDataBreakpointInfoResponse, IDebugService, IDebugViewWithVariables, IExpression, IScope, IStackFrame, IViewModel, VARIABLES_VIEW_ID, WATCH_VIEW_ID } from '../common/debug.js';
import { getContextForVariable } from '../common/debugContext.js';
import { ErrorScope, Expression, Scope, StackFrame, Variable, VisualizedExpression, getUriForDebugMemory } from '../common/debugModel.js';
import { DebugVisualizer, IDebugVisualizerService } from '../common/debugVisualizers.js';
@ -61,7 +62,7 @@ interface IVariablesContext {
variable: DebugProtocol.Variable;
}
export class VariablesView extends ViewPane {
export class VariablesView extends ViewPane implements IDebugViewWithVariables {
private updateTreeScheduler: RunOnceScheduler;
private needsRefresh = false;
@ -69,6 +70,10 @@ export class VariablesView extends ViewPane {
private savedViewState = new Map<string, IAsyncDataTreeViewState>();
private autoExpandedScopes = new Set<string>();
public get treeSelection() {
return this.tree.getSelection();
}
constructor(
options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@ -653,12 +658,34 @@ CommandsRegistry.registerCommand({
description: COPY_VALUE_LABEL,
},
id: COPY_VALUE_ID,
handler: async (accessor: ServicesAccessor, arg: Variable | Expression | IVariablesContext, ctx?: (Variable | Expression)[]) => {
handler: async (accessor: ServicesAccessor, arg: Variable | Expression | IVariablesContext | undefined, ctx?: (Variable | Expression)[]) => {
if (!arg) {
const viewService = accessor.get(IViewsService);
const view = viewService.getActiveViewWithId(WATCH_VIEW_ID) || viewService.getActiveViewWithId(VARIABLES_VIEW_ID);
if (view) {
}
}
const debugService = accessor.get(IDebugService);
const clipboardService = accessor.get(IClipboardService);
let elementContext = '';
let elements: (Variable | Expression)[];
if (arg instanceof Variable || arg instanceof Expression) {
if (!arg) {
const viewService = accessor.get(IViewsService);
const focusedView = viewService.getFocusedView();
let view: IDebugViewWithVariables | null | undefined;
if (focusedView?.id === WATCH_VIEW_ID) {
view = viewService.getActiveViewWithId<IDebugViewWithVariables>(WATCH_VIEW_ID);
elementContext = 'watch';
} else if (focusedView?.id === VARIABLES_VIEW_ID) {
view = viewService.getActiveViewWithId<IDebugViewWithVariables>(VARIABLES_VIEW_ID);
elementContext = 'variables';
}
if (!view) {
return;
}
elements = view.treeSelection.filter(e => e instanceof Expression || e instanceof Variable);
} else if (arg instanceof Variable || arg instanceof Expression) {
elementContext = 'watch';
elements = ctx ? ctx : [];
} else {

View File

@ -29,7 +29,7 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js
import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js';
import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js';
import { IViewDescriptorService } from '../../../common/views.js';
import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IExpression, WATCH_VIEW_ID } from '../common/debug.js';
import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, WATCH_VIEW_ID } from '../common/debug.js';
import { Expression, Variable, VisualizedExpression } from '../common/debugModel.js';
import { AbstractExpressionDataSource, AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js';
import { DebugExpressionRenderer } from './debugExpressionRenderer.js';
@ -40,7 +40,7 @@ const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
let ignoreViewUpdates = false;
let useCachedEvaluation = false;
export class WatchExpressionsView extends ViewPane {
export class WatchExpressionsView extends ViewPane implements IDebugViewWithVariables {
private watchExpressionsUpdatedScheduler: RunOnceScheduler;
private needsRefresh = false;
@ -51,6 +51,10 @@ export class WatchExpressionsView extends ViewPane {
private menu: IMenu;
private expressionRenderer: DebugExpressionRenderer;
public get treeSelection() {
return this.tree.getSelection();
}
constructor(
options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,

View File

@ -29,6 +29,7 @@ import { Source } from './debugSource.js';
import { ITaskIdentifier } from '../../tasks/common/tasks.js';
import { LiveTestResult } from '../../testing/common/testResult.js';
import { IEditorService } from '../../../services/editor/common/editorService.js';
import { IView } from '../../../common/views.js';
export const VIEWLET_ID = 'workbench.view.debug';
@ -115,6 +116,10 @@ export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {
description: nls.localize('internalConsoleOptions', "Controls when the internal Debug Console should open.")
};
export interface IDebugViewWithVariables extends IView {
readonly treeSelection: IExpression[];
}
// raw
export interface IRawModelUpdate {