debug: fix variables view does not show custom hover (#209058)

* debug: fix variables view does not show custom hover

Fixes #208429

Also adds easy access to copy expression/evaluateName since a user is
likely to do that after looking at the value (which is often incomplete
or summarized in the variables view)

![](https://memes.peet.io/img/24-03-26f2738f-6b21-4223-90ea-4e6938720582.png)

cc @amunger if you want to adopt this for notebook variables: extra
commands can be added in the hover by passing an object into `hover`.

Note: the hover is too persistent right now due to #209057

* fix formatting

* use action bar instead
This commit is contained in:
Connor Peet 2024-03-28 13:46:29 -07:00 committed by GitHub
parent fdca786f3d
commit 92bf068b48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 95 additions and 35 deletions

View file

@ -18,8 +18,10 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { DisposableStore, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { ThemeIcon } from 'vs/base/common/themables';
import { localize } from 'vs/nls';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles';
import { COPY_EVALUATE_PATH_ID, COPY_VALUE_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { IDebugService, IExpression, IExpressionValue } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, ExpressionContainer, Variable } from 'vs/workbench/contrib/debug/common/debugModel';
@ -34,7 +36,12 @@ const $ = dom.$;
export interface IRenderValueOptions {
showChanged?: boolean;
maxValueLength?: number;
showHover?: boolean;
/** If set, a hover will be shown on the element. Requires a disposable store for usage. */
hover?: DisposableStore | {
store: DisposableStore;
commands: { id: string; args: unknown[] }[];
commandService: ICommandService;
};
colorize?: boolean;
linkDetector?: LinkDetector;
}
@ -99,12 +106,31 @@ export function renderExpressionValue(expressionOrValue: IExpressionValue | stri
} else {
container.textContent = value;
}
if (options.showHover) {
container.title = value || '';
if (options.hover) {
const { store, commands, commandService } = options.hover instanceof DisposableStore ? { store: options.hover, commands: [], commandService: undefined } : options.hover;
store.add(setupCustomHover(getDefaultHoverDelegate('mouse'), container, () => {
const container = dom.$('div');
const markdownHoverElement = dom.$('div.hover-row');
const hoverContentsElement = dom.append(markdownHoverElement, dom.$('div.hover-contents'));
const hoverContentsPre = dom.append(hoverContentsElement, dom.$('pre.debug-var-hover-pre'));
hoverContentsPre.textContent = value;
container.appendChild(markdownHoverElement);
return container;
}, {
actions: commands.map(({ id, args }) => {
const description = CommandsRegistry.getCommand(id)?.metadata?.description;
return {
label: typeof description === 'string' ? description : description ? description.value : id,
commandId: id,
run: () => commandService!.executeCommand(id, ...args),
};
})
}));
}
}
export function renderVariable(variable: Variable, data: IVariableTemplateData, showChanged: boolean, highlights: IHighlight[], linkDetector?: LinkDetector): void {
export function renderVariable(store: DisposableStore, commandService: ICommandService, variable: Variable, data: IVariableTemplateData, showChanged: boolean, highlights: IHighlight[], linkDetector?: LinkDetector): void {
if (variable.available) {
let text = variable.name;
if (variable.value && typeof variable.name === 'string') {
@ -118,10 +144,17 @@ export function renderVariable(variable: Variable, data: IVariableTemplateData,
}
data.expression.classList.toggle('lazy', !!variable.presentationHint?.lazy);
const commands = [
{ id: COPY_VALUE_ID, args: [variable, [variable]] as unknown[] }
];
if (variable.evaluateName) {
commands.push({ id: COPY_EVALUATE_PATH_ID, args: [{ variable }] });
}
renderExpressionValue(variable, data.value, {
showChanged,
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
showHover: true,
hover: { store, commands, commandService },
colorize: true,
linkDetector
});

View file

@ -29,7 +29,7 @@ import { BreakpointsView } from 'vs/workbench/contrib/debug/browser/breakpointsV
import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView';
import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors';
import { ADD_CONFIGURATION_ID, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_STACK_TRACE_ID, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { ADD_CONFIGURATION_ID, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_STACK_TRACE_ID, COPY_VALUE_ID, COPY_VALUE_LABEL, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { DebugConsoleQuickAccess } from 'vs/workbench/contrib/debug/browser/debugConsoleQuickAccess';
import { RunToCursorAction, SelectionToReplAction, SelectionToWatchExpressionsAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions';
import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution';
@ -45,7 +45,7 @@ import { DisassemblyView, DisassemblyViewContribution } from 'vs/workbench/contr
import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView';
import { Repl } from 'vs/workbench/contrib/debug/browser/repl';
import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider';
import { ADD_TO_WATCH_ID, BREAK_WHEN_VALUE_CHANGES_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, COPY_EVALUATE_PATH_ID, COPY_VALUE_ID, SET_VARIABLE_ID, VIEW_MEMORY_ID, VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView';
import { BREAK_WHEN_VALUE_CHANGES_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, SET_VARIABLE_ID, VIEW_MEMORY_ID, VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView';
import { ADD_WATCH_ID, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView';
import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView';
import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_VARIABLE_VALUE, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID, getStateLabel } from 'vs/workbench/contrib/debug/common/debug';
@ -174,17 +174,17 @@ registerDebugViewMenuItem(MenuId.DebugCallStackContext, COPY_STACK_TRACE_ID, nls
registerDebugViewMenuItem(MenuId.DebugVariablesContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Binary Data"), 15, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, 'inline', icons.debugInspectMemory);
registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.localize('setValue', "Set Value"), 10, ContextKeyExpr.or(CONTEXT_SET_VARIABLE_SUPPORTED, ContextKeyExpr.and(CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_SET_EXPRESSION_SUPPORTED)), CONTEXT_VARIABLE_IS_READONLY.toNegated(), '3_modification');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, COPY_VALUE_LABEL, 10, undefined, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_READ_ID, nls.localize('breakWhenValueIsRead', "Break on Value Read"), 200, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break on Value Change"), 210, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_ACCESSED_ID, nls.localize('breakWhenValueIsAccessed', "Break on Value Access"), 220, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugHoverContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Binary Data"), 15, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, 'inline', icons.debugInspectMemory);
registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugHoverContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_VALUE_ID, COPY_VALUE_LABEL, 10, undefined, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste');
registerDebugViewMenuItem(MenuId.DebugHoverContext, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_IS_READ_ID, nls.localize('breakWhenValueIsRead', "Break on Value Read"), 200, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break on Value Change"), 210, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_IS_ACCESSED_ID, nls.localize('breakWhenValueIsAccessed', "Break on Value Access"), 220, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, undefined, 'z_commands');

View file

@ -73,6 +73,9 @@ export const CALLSTACK_TOP_ID = 'workbench.action.debug.callStackTop';
export const CALLSTACK_BOTTOM_ID = 'workbench.action.debug.callStackBottom';
export const CALLSTACK_UP_ID = 'workbench.action.debug.callStackUp';
export const CALLSTACK_DOWN_ID = 'workbench.action.debug.callStackDown';
export const ADD_TO_WATCH_ID = 'debug.addToWatchExpressions';
export const COPY_EVALUATE_PATH_ID = 'debug.copyEvaluatePath';
export const COPY_VALUE_ID = 'workbench.debug.viewlet.action.copyValue';
export const DEBUG_COMMAND_CATEGORY: ILocalizedString = nls.localize2('debug', 'Debug');
export const RESTART_LABEL = nls.localize2('restartDebug', "Restart");
@ -97,6 +100,9 @@ export const CALLSTACK_TOP_LABEL = nls.localize2('callStackTop', "Navigate to To
export const CALLSTACK_BOTTOM_LABEL = nls.localize2('callStackBottom', "Navigate to Bottom of Call Stack");
export const CALLSTACK_UP_LABEL = nls.localize2('callStackUp', "Navigate Up Call Stack");
export const CALLSTACK_DOWN_LABEL = nls.localize2('callStackDown', "Navigate Down Call Stack");
export const COPY_EVALUATE_PATH_LABEL = nls.localize2('copyAsExpression', "Copy as Expression");
export const COPY_VALUE_LABEL = nls.localize2('copyValue', "Copy Value");
export const ADD_TO_WATCH_LABEL = nls.localize2('addToWatchExpressions', "Add to Watch");
export const SELECT_DEBUG_CONSOLE_LABEL = nls.localize2('selectDebugConsole', "Select Debug Console");
export const SELECT_DEBUG_SESSION_LABEL = nls.localize2('selectDebugSession', "Select Debug Session");

View file

@ -54,6 +54,10 @@
vertical-align: middle;
}
.debug-var-hover-pre {
margin: 0;
}
/* Do not push text with inline decoration when decoration on start of line */
.monaco-editor .debug-top-stack-frame-column.start-of-line {
position: absolute;

View file

@ -230,6 +230,7 @@
}
.debug-pane .monaco-list-row .expression .value {
height: 22px;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;

View file

@ -30,6 +30,7 @@ import { RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult, ReplGr
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { ICommandService } from 'vs/platform/commands/common/commands';
const $ = dom.$;
@ -136,7 +137,6 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluati
renderElement(element: ITreeNode<ReplEvaluationResult | Variable, FuzzyScore>, index: number, templateData: IReplEvaluationResultTemplateData): void {
const expression = element.element;
renderExpressionValue(expression, templateData.value, {
showHover: false,
colorize: true,
linkDetector: this.linkDetector
});
@ -235,6 +235,7 @@ export class ReplVariablesRenderer extends AbstractExpressionsRenderer<IExpressi
private readonly linkDetector: LinkDetector,
@IDebugService debugService: IDebugService,
@IContextViewService contextViewService: IContextViewService,
@ICommandService private readonly commandService: ICommandService,
) {
super(debugService, contextViewService);
}
@ -248,10 +249,10 @@ export class ReplVariablesRenderer extends AbstractExpressionsRenderer<IExpressi
const isReplVariable = expression instanceof ReplVariableElement;
if (isReplVariable || !expression.name) {
data.label.set('');
renderExpressionValue(isReplVariable ? expression.expression : expression, data.value, { showHover: false, colorize: true, linkDetector: this.linkDetector });
renderExpressionValue(isReplVariable ? expression.expression : expression, data.value, { colorize: true, linkDetector: this.linkDetector });
data.expression.classList.remove('nested-variable');
} else {
renderVariable(expression as Variable, data, true, highlights, this.linkDetector);
renderVariable(data.elementDisposable, this.commandService, expression as Variable, data, true, highlights, this.linkDetector);
data.expression.classList.toggle('nested-variable', isNestedVariable(expression));
}
}
@ -293,7 +294,6 @@ export class ReplRawObjectsRenderer implements ITreeRenderer<RawObjectReplElemen
// value
renderExpressionValue(element.value, templateData.value, {
showHover: false,
linkDetector: this.linkDetector
});
}

View file

@ -22,7 +22,7 @@ import { localize } from 'vs/nls';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
@ -38,6 +38,7 @@ import { ViewAction, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { AbstractExpressionDataSource, AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderExpressionValue, renderVariable, renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
import { ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_VALUE_ID, COPY_VALUE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
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 'vs/workbench/contrib/debug/common/debug';
import { getContextForVariable } from 'vs/workbench/contrib/debug/common/debugContext';
@ -467,7 +468,7 @@ export class VisualizedVariableRenderer extends AbstractExpressionsRenderer {
renderExpressionValue(viz, data.value, {
showChanged: false,
maxValueLength: 1024,
showHover: true,
hover: data.elementDisposable,
colorize: true,
linkDetector: this.linkDetector
});
@ -525,6 +526,7 @@ export class VariablesRenderer extends AbstractExpressionsRenderer {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IDebugVisualizerService private readonly visualization: IDebugVisualizerService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@ICommandService private readonly commandService: ICommandService,
@IDebugService debugService: IDebugService,
@IContextViewService contextViewService: IContextViewService,
) {
@ -536,7 +538,7 @@ export class VariablesRenderer extends AbstractExpressionsRenderer {
}
protected renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void {
renderVariable(expression as Variable, data, true, highlights, this.linkDetector);
renderVariable(data.elementDisposable, this.commandService, expression as Variable, data, true, highlights, this.linkDetector);
}
public override renderElement(node: ITreeNode<IExpression, FuzzyScore>, index: number, data: IExpressionTemplateData): void {
@ -650,8 +652,10 @@ CommandsRegistry.registerCommand({
}
});
export const COPY_VALUE_ID = 'workbench.debug.viewlet.action.copyValue';
CommandsRegistry.registerCommand({
metadata: {
description: COPY_VALUE_LABEL,
},
id: COPY_VALUE_ID,
handler: async (accessor: ServicesAccessor, arg: Variable | Expression | IVariablesContext, ctx?: (Variable | Expression)[]) => {
const debugService = accessor.get(IDebugService);
@ -793,8 +797,10 @@ CommandsRegistry.registerCommand({
}
});
export const COPY_EVALUATE_PATH_ID = 'debug.copyEvaluatePath';
CommandsRegistry.registerCommand({
metadata: {
description: COPY_EVALUATE_PATH_LABEL,
},
id: COPY_EVALUATE_PATH_ID,
handler: async (accessor: ServicesAccessor, context: IVariablesContext) => {
const clipboardService = accessor.get(IClipboardService);
@ -802,8 +808,10 @@ CommandsRegistry.registerCommand({
}
});
export const ADD_TO_WATCH_ID = 'debug.addToWatchExpressions';
CommandsRegistry.registerCommand({
metadata: {
description: ADD_TO_WATCH_LABEL,
},
id: ADD_TO_WATCH_ID,
handler: async (accessor: ServicesAccessor, context: IVariablesContext) => {
const debugService = accessor.get(IDebugService);

View file

@ -310,7 +310,7 @@ class WatchExpressionsRenderer extends AbstractExpressionsRenderer {
renderExpressionValue(expression, data.value, {
showChanged: true,
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
showHover: true,
hover: data.elementDisposable,
colorize: true
});
}

View file

@ -6,8 +6,10 @@
import * as assert from 'assert';
import * as dom from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
import { NullCommandService } from 'vs/platform/commands/test/common/nullCommandService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { renderExpressionValue, renderVariable, renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
@ -44,9 +46,8 @@ suite('Debug - Base Debug View', () => {
test('render expression value', () => {
let container = $('.container');
renderExpressionValue('render \n me', container, { showHover: true });
renderExpressionValue('render \n me', container, {});
assert.strictEqual(container.className, 'value');
assert.strictEqual(container.title, 'render \n me');
assert.strictEqual(container.textContent, 'render \n me');
const expression = new Expression('console');
@ -92,17 +93,17 @@ suite('Debug - Base Debug View', () => {
let value = $('.');
const label = new HighlightedLabel(name);
const lazyButton = $('.');
renderVariable(variable, { expression, name, value, label, lazyButton }, false, []);
const store = disposables.add(new DisposableStore());
renderVariable(store, NullCommandService, variable, { expression, name, value, label, lazyButton }, false, []);
assert.strictEqual(label.element.textContent, 'foo');
assert.strictEqual(value.textContent, '');
assert.strictEqual(value.title, '');
variable.value = 'hey';
expression = $('.');
name = $('.');
value = $('.');
renderVariable(variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
renderVariable(store, NullCommandService, variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
assert.strictEqual(value.textContent, 'hey');
assert.strictEqual(label.element.textContent, 'foo:');
@ -110,7 +111,7 @@ suite('Debug - Base Debug View', () => {
expression = $('.');
name = $('.');
value = $('.');
renderVariable(variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
renderVariable(store, NullCommandService, variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
assert.ok(value.querySelector('a'));
assert.strictEqual(value.querySelector('a')!.textContent, variable.value);
@ -118,7 +119,7 @@ suite('Debug - Base Debug View', () => {
expression = $('.');
name = $('.');
value = $('.');
renderVariable(variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
renderVariable(store, NullCommandService, variable, { expression, name, value, label, lazyButton }, false, [], linkDetector);
assert.strictEqual(name.className, 'virtual');
assert.strictEqual(label.element.textContent, 'console:');
assert.strictEqual(value.className, 'value number');

View file

@ -8,6 +8,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { FuzzyScore } from 'vs/base/common/filters';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView';
@ -33,6 +34,7 @@ export interface IVariableTemplateData {
expression: HTMLElement;
name: HTMLSpanElement;
value: HTMLSpanElement;
elementDisposables: DisposableStore;
}
export class NotebookVariableRenderer implements ITreeRenderer<INotebookVariableElement, FuzzyScore, IVariableTemplateData> {
@ -48,7 +50,7 @@ export class NotebookVariableRenderer implements ITreeRenderer<INotebookVariable
const name = dom.append(expression, $('span.name'));
const value = dom.append(expression, $('span.value'));
const template: IVariableTemplateData = { expression, name, value };
const template: IVariableTemplateData = { expression, name, value, elementDisposables: new DisposableStore() };
return template;
}
@ -60,13 +62,18 @@ export class NotebookVariableRenderer implements ITreeRenderer<INotebookVariable
renderExpressionValue(element.element, data.value, {
colorize: true,
showHover: true,
hover: data.elementDisposables,
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET
});
}
disposeTemplate(): void {
// noop
disposeElement(element: ITreeNode<INotebookVariableElement, FuzzyScore>, index: number, templateData: IVariableTemplateData, height: number | undefined): void {
templateData.elementDisposables.clear();
}
disposeTemplate(templateData: IVariableTemplateData): void {
templateData.elementDisposables.dispose();
}
}