Merge pull request #127162 from microsoft/isidorn/setExpression

set value for variable and watch
This commit is contained in:
Isidor Nikolic 2021-08-05 15:34:48 +02:00 committed by GitHub
commit 664201582e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 108 additions and 41 deletions

View file

@ -160,8 +160,9 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
data.toDispose = Disposable.None;
const { element } = node;
this.renderExpression(element, data, createMatches(node.filterData));
if (element === this.debugService.getViewModel().getSelectedExpression() || (element instanceof Variable && element.errorMessage)) {
const options = this.getInputBoxOptions(element);
const selectedExpression = this.debugService.getViewModel().getSelectedExpression();
if (element === selectedExpression?.expression || (element instanceof Variable && element.errorMessage)) {
const options = this.getInputBoxOptions(element, !!selectedExpression?.settingWatch);
if (options) {
data.toDispose = this.renderInputBox(data.name, data.value, data.inputBoxContainer, options);
return;
@ -189,7 +190,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
dispose(toDispose);
if (finishEditing) {
this.debugService.getViewModel().setSelectedExpression(undefined);
this.debugService.getViewModel().setSelectedExpression(undefined, false);
options.onFinish(value, success);
}
});
@ -222,7 +223,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
}
protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void;
protected abstract getInputBoxOptions(expression: IExpression): IInputBoxOptions | undefined;
protected abstract getInputBoxOptions(expression: IExpression, settingValue: boolean): IInputBoxOptions | undefined;
disposeElement(node: ITreeNode<IExpression, FuzzyScore>, index: number, templateData: IExpressionTemplateData): void {
templateData.toDispose.dispose();

View file

@ -16,11 +16,11 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import {
IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID, CONTEXT_SET_EXPRESSION_SUPPORTED,
} from 'vs/workbench/contrib/debug/common/debug';
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider';
import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
@ -140,7 +140,7 @@ registerDebugViewMenuItem(MenuId.DebugCallStackContext, TERMINATE_THREAD_ID, nls
registerDebugViewMenuItem(MenuId.DebugCallStackContext, RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED), CONTEXT_STACK_FRAME_SUPPORTS_RESTART);
registerDebugViewMenuItem(MenuId.DebugCallStackContext, COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), undefined, '3_modification');
registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.localize('setValue', "Set Value"), 10, CONTEXT_SET_VARIABLE_SUPPORTED, undefined, '3_modification');
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)), undefined, '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');
@ -150,7 +150,8 @@ registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_ACCE
registerDebugViewMenuItem(MenuId.DebugWatchContext, ADD_WATCH_ID, ADD_WATCH_LABEL, 10, undefined, undefined, '3_modification');
registerDebugViewMenuItem(MenuId.DebugWatchContext, EDIT_EXPRESSION_COMMAND_ID, nls.localize('editWatchExpression', "Edit Expression"), 20, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, '3_modification');
registerDebugViewMenuItem(MenuId.DebugWatchContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 30, ContextKeyExpr.or(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), CONTEXT_WATCH_ITEM_TYPE.isEqualTo('variable')), CONTEXT_IN_DEBUG_MODE, '3_modification');
registerDebugViewMenuItem(MenuId.DebugWatchContext, SET_EXPRESSION_COMMAND_ID, nls.localize('setValue', "Set Value"), 30, ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), CONTEXT_SET_EXPRESSION_SUPPORTED), ContextKeyExpr.and(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('variable'), CONTEXT_SET_VARIABLE_SUPPORTED)), undefined, '3_modification');
registerDebugViewMenuItem(MenuId.DebugWatchContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 40, ContextKeyExpr.or(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), CONTEXT_WATCH_ITEM_TYPE.isEqualTo('variable')), CONTEXT_IN_DEBUG_MODE, '3_modification');
registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_EXPRESSION_COMMAND_ID, nls.localize('removeWatchExpression', "Remove Expression"), 10, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, 'z_commands');
registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, 20, undefined, undefined, 'z_commands');

View file

@ -54,6 +54,7 @@ export const DEBUG_CONFIGURE_COMMAND_ID = 'workbench.action.debug.configure';
export const DEBUG_START_COMMAND_ID = 'workbench.action.debug.start';
export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run';
export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression';
export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression';
export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression';
export const RESTART_LABEL = nls.localize('restartDebug', "Restart");
@ -501,7 +502,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
if (expression instanceof Expression) {
debugService.getViewModel().setSelectedExpression(expression);
debugService.getViewModel().setSelectedExpression(expression, false);
}
}
});
CommandsRegistry.registerCommand({
id: SET_EXPRESSION_COMMAND_ID,
handler: async (accessor: ServicesAccessor, expression: Expression | unknown) => {
const debugService = accessor.get(IDebugService);
if (expression instanceof Expression || expression instanceof Variable) {
debugService.getViewModel().setSelectedExpression(expression, true);
}
}
});
@ -520,7 +531,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
if (focused) {
const elements = focused.getFocus();
if (Array.isArray(elements) && elements[0] instanceof Variable) {
debugService.getViewModel().setSelectedExpression(elements[0]);
debugService.getViewModel().setSelectedExpression(elements[0], false);
}
}
}

View file

@ -877,7 +877,7 @@ export class DebugService implements IDebugService {
addWatchExpression(name?: string): void {
const we = this.model.addWatchExpression(name);
if (!name) {
this.viewModel.setSelectedExpression(we);
this.viewModel.setSelectedExpression(we, false);
}
this.debugStorage.storeWatchExpressions(this.model.getWatchExpressions());
}

View file

@ -650,6 +650,14 @@ export class DebugSession implements IDebugSession {
return this.raw.setVariable({ variablesReference, name, value });
}
setExpression(frameId: number, expression: string, value: string): Promise<DebugProtocol.SetExpressionResponse | undefined> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'setExpression'));
}
return this.raw.setExpression({ expression, value, frameId });
}
gotoTargets(source: DebugProtocol.Source, line: number, column?: number): Promise<DebugProtocol.GotoTargetsResponse | undefined> {
if (!this.raw) {
throw new Error(localize('noDebugAdapter', "No debugger available, can not send '{0}'", 'gotoTargets'));

View file

@ -358,6 +358,13 @@ export class RawDebugSession implements IDisposable {
return Promise.reject(new Error('setVariable not supported'));
}
setExpression(args: DebugProtocol.SetExpressionArguments): Promise<DebugProtocol.SetExpressionResponse | undefined> {
if (this.capabilities.supportsSetExpression) {
return this.send<DebugProtocol.SetExpressionResponse>('setExpression', args);
}
return Promise.reject(new Error('setExpression not supported'));
}
async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse | undefined> {
if (this.capabilities.supportsRestartFrame) {
const response = await this.send('restartFrame', args);

View file

@ -164,13 +164,14 @@ export class VariablesView extends ViewPane {
}));
let horizontalScrolling: boolean | undefined;
this._register(this.debugService.getViewModel().onDidSelectExpression(e => {
if (e instanceof Variable) {
const variable = e?.expression;
if (variable instanceof Variable && !e?.settingWatch) {
horizontalScrolling = this.tree.options.horizontalScrolling;
if (horizontalScrolling) {
this.tree.updateOptions({ horizontalScrolling: false });
}
this.tree.rerender(e);
this.tree.rerender(variable);
} else if (!e && horizontalScrolling !== undefined) {
this.tree.updateOptions({ horizontalScrolling: horizontalScrolling });
horizontalScrolling = undefined;
@ -198,7 +199,7 @@ export class VariablesView extends ViewPane {
private onMouseDblClick(e: ITreeMouseEvent<IExpression | IScope>): void {
const session = this.debugService.getViewModel().focusedSession;
if (session && e.element instanceof Variable && session.capabilities.supportsSetVariable) {
this.debugService.getViewModel().setSelectedExpression(e.element);
this.debugService.getViewModel().setSelectedExpression(e.element, false);
}
}
@ -374,8 +375,9 @@ export class VariablesRenderer extends AbstractExpressionsRenderer {
},
onFinish: (value: string, success: boolean) => {
variable.errorMessage = undefined;
if (success && variable.value !== value) {
variable.setVariable(value)
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
if (success && variable.value !== value && focusedStackFrame) {
variable.setVariable(value, focusedStackFrame)
// Need to force watch expressions and variables to update since a variable change can have an effect on both
.then(() => {
// Do not refresh scopes due to a node limitation #15520
@ -411,7 +413,7 @@ CommandsRegistry.registerCommand({
id: SET_VARIABLE_ID,
handler: (accessor: ServicesAccessor) => {
const debugService = accessor.get(IDebugService);
debugService.getViewModel().setSelectedExpression(variableInternalContext);
debugService.getViewModel().setSelectedExpression(variableInternalContext, false);
}
});

View file

@ -89,7 +89,7 @@ export class WatchExpressionsView extends ViewPane {
identityProvider: { getId: (element: IExpression) => element.getId() },
keyboardNavigationLabelProvider: {
getKeyboardNavigationLabel: (e: IExpression) => {
if (e === this.debugService.getViewModel().getSelectedExpression()) {
if (e === this.debugService.getViewModel().getSelectedExpression()?.expression) {
// Don't filter input box
return undefined;
}
@ -146,17 +146,18 @@ export class WatchExpressionsView extends ViewPane {
}));
let horizontalScrolling: boolean | undefined;
this._register(this.debugService.getViewModel().onDidSelectExpression(e => {
if (e instanceof Expression) {
const expression = e?.expression;
if (expression instanceof Expression || (expression instanceof Variable && e?.settingWatch)) {
horizontalScrolling = this.tree.options.horizontalScrolling;
if (horizontalScrolling) {
this.tree.updateOptions({ horizontalScrolling: false });
}
if (e.name) {
if (expression.name) {
// Only rerender if the input is already done since otherwise the tree is not yet aware of the new element
this.tree.rerender(e);
this.tree.rerender(expression);
}
} else if (!e && horizontalScrolling !== undefined) {
} else if (!expression && horizontalScrolling !== undefined) {
this.tree.updateOptions({ horizontalScrolling: horizontalScrolling });
horizontalScrolling = undefined;
}
@ -184,8 +185,9 @@ export class WatchExpressionsView extends ViewPane {
const element = e.element;
// double click on primitive value: open input box to be able to select and copy value.
if (element instanceof Expression && element !== this.debugService.getViewModel().getSelectedExpression()) {
this.debugService.getViewModel().setSelectedExpression(element);
const selectedExpression = this.debugService.getViewModel().getSelectedExpression();
if (element instanceof Expression && element !== selectedExpression?.expression) {
this.debugService.getViewModel().setSelectedExpression(element, false);
} else if (!element) {
// Double click in watch panel triggers to add a new watch expression
this.debugService.addWatchExpression();
@ -269,7 +271,27 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer {
});
}
protected getInputBoxOptions(expression: IExpression): IInputBoxOptions {
protected getInputBoxOptions(expression: IExpression, settingValue: boolean): IInputBoxOptions {
if (settingValue) {
return {
initialValue: expression.value,
ariaLabel: localize('typeNewValue', "Type new value"),
onFinish: async (value: string, success: boolean) => {
if (success && value) {
const focusedSession = this.debugService.getViewModel().focusedSession;
const focusedFrame = this.debugService.getViewModel().focusedStackFrame;
if (focusedSession && focusedFrame) {
const path = expression instanceof Variable && expression.evaluateName ? expression.evaluateName : expression.name;
await focusedSession.setExpression(focusedFrame.frameId, path, value);
ignoreViewUpdates = true;
this.debugService.getViewModel().updateViews();
ignoreViewUpdates = false;
}
}
}
};
}
return {
initialValue: expression.name ? expression.name : '',
ariaLabel: localize('watchExpressionInputAriaLabel', "Type watch expression"),
@ -318,7 +340,7 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop<IExpression> {
}
getDragURI(element: IExpression): string | null {
if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()) {
if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()?.expression) {
return null;
}

View file

@ -73,6 +73,7 @@ export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey<boolean>('breakpoints
export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey<boolean>('debuggersAvailable', false, { type: 'boolean', description: nls.localize('debuggersAvailable', "True when there is at least one debug extensions active.") });
export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey<string>('debugProtocolVariableMenuContext', undefined, { type: 'string', description: nls.localize('debugProtocolVariableMenuContext', "Represents the context the debug adapter sets on the focused variable in the VARIABLES view.") });
export const CONTEXT_SET_VARIABLE_SUPPORTED = new RawContextKey<boolean>('debugSetVariableSupported', false, { type: 'boolean', description: nls.localize('debugSetVariableSupported', "True when the focused session supports 'setVariable' request.") });
export const CONTEXT_SET_EXPRESSION_SUPPORTED = new RawContextKey<boolean>('debugSetExpressionSupported', false, { type: 'boolean', description: nls.localize('debugSetExpressionSupported', "True when the focused session supports 'setExpression' request.") });
export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey<boolean>('breakWhenValueChangesSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueChangesSupported', "True when the focused session supports to break when value changes.") });
export const CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED = new RawContextKey<boolean>('breakWhenValueIsAccessedSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsAccessedSupported', "True when the focused breakpoint supports to break when value is accessed.") });
export const CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED = new RawContextKey<boolean>('breakWhenValueIsReadSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsReadSupported', "True when the focused breakpoint supports to break when value is read.") });
@ -290,6 +291,7 @@ export interface IDebugSession extends ITreeElement {
completions(frameId: number | undefined, threadId: number, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<DebugProtocol.CompletionsResponse | undefined>;
setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse | undefined>;
setExpression(frameId: number, expression: string, value: string): Promise<DebugProtocol.SetExpressionResponse | undefined>;
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse | undefined>;
getLoadedSources(): Promise<Source[]>;
@ -475,15 +477,15 @@ export interface IViewModel extends ITreeElement {
*/
readonly focusedStackFrame: IStackFrame | undefined;
getSelectedExpression(): IExpression | undefined;
setSelectedExpression(expression: IExpression | undefined): void;
getSelectedExpression(): { expression: IExpression; settingWatch: boolean } | undefined;
setSelectedExpression(expression: IExpression | undefined, settingWatch: boolean): void;
updateViews(): void;
isMultiSessionView(): boolean;
onDidFocusSession: Event<IDebugSession | undefined>;
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }>;
onDidSelectExpression: Event<IExpression | undefined>;
onDidSelectExpression: Event<{ expression: IExpression; settingWatch: boolean } | undefined>;
onWillUpdateViews: Event<void>;
}

View file

@ -241,13 +241,20 @@ export class Variable extends ExpressionContainer implements IExpression {
this.type = type;
}
async setVariable(value: string): Promise<any> {
async setVariable(value: string, stackFrame: IStackFrame): Promise<any> {
if (!this.session) {
return;
}
try {
const response = await this.session.setVariable((<ExpressionContainer>this.parent).reference, this.name, value);
let response: DebugProtocol.SetExpressionResponse | DebugProtocol.SetVariableResponse | undefined;
// Send out a setExpression for debug extensions that do not support set variables https://github.com/microsoft/vscode/issues/124679#issuecomment-869844437
if (this.session.capabilities.supportsSetExpression && !this.session.capabilities.supportsSetVariable && this.evaluateName) {
response = await this.session.setExpression(stackFrame.frameId, this.evaluateName, value);
} else {
response = await this.session.setVariable((<ExpressionContainer>this.parent).reference, this.name, value);
}
if (response && response.body) {
this.value = response.body.value || '';
this.type = response.body.type || this.type;

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE } from 'vs/workbench/contrib/debug/common/debug';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE, CONTEXT_SET_EXPRESSION_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils';
@ -15,10 +15,10 @@ export class ViewModel implements IViewModel {
private _focusedStackFrame: IStackFrame | undefined;
private _focusedSession: IDebugSession | undefined;
private _focusedThread: IThread | undefined;
private selectedExpression: IExpression | undefined;
private selectedExpression: { expression: IExpression; settingWatch: boolean } | undefined;
private readonly _onDidFocusSession = new Emitter<IDebugSession | undefined>();
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>();
private readonly _onDidSelectExpression = new Emitter<IExpression | undefined>();
private readonly _onDidSelectExpression = new Emitter<{ expression: IExpression; settingWatch: boolean } | undefined>();
private readonly _onWillUpdateViews = new Emitter<void>();
private expressionSelectedContextKey!: IContextKey<boolean>;
private loadedScriptsSupportedContextKey!: IContextKey<boolean>;
@ -28,6 +28,7 @@ export class ViewModel implements IViewModel {
private stepIntoTargetsSupported!: IContextKey<boolean>;
private jumpToCursorSupported!: IContextKey<boolean>;
private setVariableSupported!: IContextKey<boolean>;
private setExpressionSupported!: IContextKey<boolean>;
private multiSessionDebug!: IContextKey<boolean>;
private terminateDebuggeeSuported!: IContextKey<boolean>;
private disassembleRequestSupported!: IContextKey<boolean>;
@ -43,6 +44,7 @@ export class ViewModel implements IViewModel {
this.stepIntoTargetsSupported = CONTEXT_STEP_INTO_TARGETS_SUPPORTED.bindTo(contextKeyService);
this.jumpToCursorSupported = CONTEXT_JUMP_TO_CURSOR_SUPPORTED.bindTo(contextKeyService);
this.setVariableSupported = CONTEXT_SET_VARIABLE_SUPPORTED.bindTo(contextKeyService);
this.setExpressionSupported = CONTEXT_SET_EXPRESSION_SUPPORTED.bindTo(contextKeyService);
this.multiSessionDebug = CONTEXT_MULTI_SESSION_DEBUG.bindTo(contextKeyService);
this.terminateDebuggeeSuported = CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED.bindTo(contextKeyService);
this.disassembleRequestSupported = CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED.bindTo(contextKeyService);
@ -81,6 +83,7 @@ export class ViewModel implements IViewModel {
this.stepIntoTargetsSupported.set(session ? !!session.capabilities.supportsStepInTargetsRequest : false);
this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false);
this.setVariableSupported.set(session ? !!session.capabilities.supportsSetVariable : false);
this.setExpressionSupported.set(session ? !!session.capabilities.supportsSetExpression : false);
this.terminateDebuggeeSuported.set(session ? !!session.capabilities.supportTerminateDebuggee : false);
this.disassembleRequestSupported.set(!!session?.capabilities.supportsDisassembleRequest);
this.focusedStackFrameHasInstructionPointerReference.set(!!stackFrame?.instructionPointerReference);
@ -104,17 +107,17 @@ export class ViewModel implements IViewModel {
return this._onDidFocusStackFrame.event;
}
getSelectedExpression(): IExpression | undefined {
getSelectedExpression(): { expression: IExpression; settingWatch: boolean } | undefined {
return this.selectedExpression;
}
setSelectedExpression(expression: IExpression | undefined) {
this.selectedExpression = expression;
setSelectedExpression(expression: IExpression | undefined, settingWatch: boolean) {
this.selectedExpression = expression ? { expression, settingWatch: settingWatch } : undefined;
this.expressionSelectedContextKey.set(!!expression);
this._onDidSelectExpression.fire(expression);
this._onDidSelectExpression.fire(this.selectedExpression);
}
get onDidSelectExpression(): Event<IExpression | undefined> {
get onDidSelectExpression(): Event<{ expression: IExpression; settingWatch: boolean } | undefined> {
return this._onDidSelectExpression.event;
}

View file

@ -38,9 +38,9 @@ suite('Debug - View Model', () => {
test('selected expression', () => {
assert.strictEqual(model.getSelectedExpression(), undefined);
const expression = new Expression('my expression');
model.setSelectedExpression(expression);
model.setSelectedExpression(expression, false);
assert.strictEqual(model.getSelectedExpression(), expression);
assert.strictEqual(model.getSelectedExpression()?.expression, expression);
});
test('multi session view and changed workbench state', () => {

View file

@ -392,6 +392,9 @@ export class MockSession implements IDebugSession {
setVariable(variablesReference: number, name: string, value: string): Promise<DebugProtocol.SetVariableResponse> {
throw new Error('Method not implemented.');
}
setExpression(frameId: number, expression: string, value: string): Promise<DebugProtocol.SetExpressionResponse | undefined> {
throw new Error('Method not implemented.');
}
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse> {
throw new Error('Method not implemented.');
}