Skip to content

Commit a19eb0a

Browse files
authored
Merge pull request #133643 from microsoft/connor4312/debug-read-memory
debug: read/write memory support
2 parents fc76a87 + 200a679 commit a19eb0a

File tree

21 files changed

+1013
-195
lines changed

21 files changed

+1013
-195
lines changed

src/vs/base/common/buffer.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,104 @@ export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReada
284284
export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {
285285
return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));
286286
}
287+
288+
/** Decodes base64 to a uint8 array. URL-encoded and unpadded base64 is allowed. */
289+
export function decodeBase64(encoded: string) {
290+
let building = 0;
291+
let remainder = 0;
292+
let bufi = 0;
293+
294+
// The simpler way to do this is `Uint8Array.from(atob(str), c => c.charCodeAt(0))`,
295+
// but that's about 10-20x slower than this function in current Chromium versions.
296+
297+
const buffer = new Uint8Array(Math.floor(encoded.length / 4 * 3));
298+
const append = (value: number) => {
299+
switch (remainder) {
300+
case 3:
301+
buffer[bufi++] = building | value;
302+
remainder = 0;
303+
break;
304+
case 2:
305+
buffer[bufi++] = building | (value >>> 2);
306+
building = value << 6;
307+
remainder = 3;
308+
break;
309+
case 1:
310+
buffer[bufi++] = building | (value >>> 4);
311+
building = value << 4;
312+
remainder = 2;
313+
break;
314+
default:
315+
building = value << 2;
316+
remainder = 1;
317+
}
318+
};
319+
320+
for (let i = 0; i < encoded.length; i++) {
321+
const code = encoded.charCodeAt(i);
322+
// See https://datatracker.ietf.org/doc/html/rfc4648#section-4
323+
// This branchy code is about 3x faster than an indexOf on a base64 char string.
324+
if (code >= 65 && code <= 90) {
325+
append(code - 65); // A-Z starts ranges from char code 65 to 90
326+
} else if (code >= 97 && code <= 122) {
327+
append(code - 97 + 26); // a-z starts ranges from char code 97 to 122, starting at byte 26
328+
} else if (code >= 48 && code <= 57) {
329+
append(code - 48 + 52); // 0-9 starts ranges from char code 48 to 58, starting at byte 52
330+
} else if (code === 43 || code === 45) {
331+
append(62); // "+" or "-" for URLS
332+
} else if (code === 47 || code === 95) {
333+
append(63); // "/" or "_" for URLS
334+
} else if (code === 61) {
335+
break; // "="
336+
} else {
337+
throw new SyntaxError(`Unexpected base64 character ${encoded[i]}`);
338+
}
339+
}
340+
341+
const unpadded = bufi;
342+
while (remainder > 0) {
343+
append(0);
344+
}
345+
346+
// slice is needed to account for overestimation due to padding
347+
return VSBuffer.wrap(buffer).slice(0, unpadded);
348+
}
349+
350+
const base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
351+
const base64UrlSafeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
352+
353+
/** Encodes a buffer to a base64 string. */
354+
export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = false) {
355+
const dictionary = urlSafe ? base64UrlSafeAlphabet : base64Alphabet;
356+
let output = '';
357+
358+
const remainder = buffer.byteLength % 3;
359+
360+
let i = 0;
361+
for (; i < buffer.byteLength - remainder; i += 3) {
362+
const a = buffer[i + 0];
363+
const b = buffer[i + 1];
364+
const c = buffer[i + 2];
365+
366+
output += dictionary[a >>> 2];
367+
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
368+
output += dictionary[(b << 2 | c >>> 6) & 0b111111];
369+
output += dictionary[c & 0b111111];
370+
}
371+
372+
if (remainder === 1) {
373+
const a = buffer[i + 0];
374+
output += dictionary[a >>> 2];
375+
output += dictionary[(a << 4) & 0b111111];
376+
if (padded) { output += '=='; }
377+
} else if (remainder === 2) {
378+
const a = buffer[i + 0];
379+
const b = buffer[i + 1];
380+
output += dictionary[a >>> 2];
381+
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
382+
output += dictionary[(b << 2) & 0b111111];
383+
if (padded) { output += '='; }
384+
}
385+
386+
return output;
387+
}

src/vs/base/test/common/buffer.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as assert from 'assert';
77
import { timeout } from 'vs/base/common/async';
8-
import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer } from 'vs/base/common/buffer';
8+
import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, decodeBase64, encodeBase64, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer } from 'vs/base/common/buffer';
99
import { peekStream } from 'vs/base/common/stream';
1010

1111
suite('Buffer', () => {
@@ -412,4 +412,53 @@ suite('Buffer', () => {
412412
assert.strictEqual(u2[0], 17);
413413
}
414414
});
415+
416+
suite('base64', () => {
417+
/*
418+
Generated with:
419+
420+
const crypto = require('crypto');
421+
422+
for (let i = 0; i < 16; i++) {
423+
const buf = crypto.randomBytes(i);
424+
console.log(`[new Uint8Array([${Array.from(buf).join(', ')}]), '${buf.toString('base64')}'],`)
425+
}
426+
427+
*/
428+
429+
const testCases: [Uint8Array, string][] = [
430+
[new Uint8Array([]), ''],
431+
[new Uint8Array([56]), 'OA=='],
432+
[new Uint8Array([209, 4]), '0QQ='],
433+
[new Uint8Array([19, 57, 119]), 'Ezl3'],
434+
[new Uint8Array([199, 237, 207, 112]), 'x+3PcA=='],
435+
[new Uint8Array([59, 193, 173, 26, 242]), 'O8GtGvI='],
436+
[new Uint8Array([81, 226, 95, 231, 116, 126]), 'UeJf53R+'],
437+
[new Uint8Array([11, 164, 253, 85, 8, 6, 56]), 'C6T9VQgGOA=='],
438+
[new Uint8Array([164, 16, 88, 88, 224, 173, 144, 114]), 'pBBYWOCtkHI='],
439+
[new Uint8Array([0, 196, 99, 12, 21, 229, 78, 101, 13]), 'AMRjDBXlTmUN'],
440+
[new Uint8Array([167, 114, 225, 116, 226, 83, 51, 48, 88, 114]), 'p3LhdOJTMzBYcg=='],
441+
[new Uint8Array([75, 33, 118, 10, 77, 5, 168, 194, 59, 47, 59]), 'SyF2Ck0FqMI7Lzs='],
442+
[new Uint8Array([203, 182, 165, 51, 208, 27, 123, 223, 112, 198, 127, 147]), 'y7alM9Abe99wxn+T'],
443+
[new Uint8Array([154, 93, 222, 41, 117, 234, 250, 85, 95, 144, 16, 94, 18]), 'ml3eKXXq+lVfkBBeEg=='],
444+
[new Uint8Array([246, 186, 88, 105, 192, 57, 25, 168, 183, 164, 103, 162, 243, 56]), '9rpYacA5Gai3pGei8zg='],
445+
[new Uint8Array([149, 240, 155, 96, 30, 55, 162, 172, 191, 187, 33, 124, 169, 183, 254]), 'lfCbYB43oqy/uyF8qbf+'],
446+
];
447+
448+
test('encodes', () => {
449+
for (const [bytes, expected] of testCases) {
450+
assert.strictEqual(encodeBase64(VSBuffer.wrap(bytes)), expected);
451+
}
452+
});
453+
454+
test('decodes', () => {
455+
for (const [expected, encoded] of testCases) {
456+
assert.deepStrictEqual(new Uint8Array(decodeBase64(encoded).buffer), expected);
457+
}
458+
});
459+
460+
test('throws error on invalid encoding', () => {
461+
assert.throws(() => decodeBase64('invalid!'));
462+
});
463+
});
415464
});

src/vs/base/test/common/mock.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ export function mock<T>(): Ctor<T> {
1313
return function () { } as any;
1414
}
1515

16-
export type MockObject<T, TP = {}> = { [K in keyof T]: K extends keyof TP ? TP[K] : SinonStub };
16+
export type MockObject<T, ExceptProps = never> = { [K in keyof T]: K extends ExceptProps ? T[K] : SinonStub };
1717

1818
// Creates an object object that returns sinon mocks for every property. Optionally
1919
// takes base properties.
20-
export function mockObject<T extends object, TP extends Partial<T>>(properties?: TP): MockObject<T, TP> {
20+
export const mockObject = <T extends object>() => <TP extends Partial<T> = {}>(properties?: TP): MockObject<T, keyof TP> => {
2121
return new Proxy({ ...properties } as any, {
2222
get(target, key) {
2323
if (!target.hasOwnProperty(key)) {
@@ -31,4 +31,4 @@ export function mockObject<T extends object, TP extends Partial<T>>(properties?:
3131
return true;
3232
},
3333
});
34-
}
34+
};

src/vs/workbench/contrib/debug/browser/baseDebugView.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Expression, Variable, ExpressionContainer } from 'vs/workbench/contrib/
99
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
1010
import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
1111
import { ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
12-
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
12+
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
1313
import { IThemeService } from 'vs/platform/theme/common/themeService';
1414
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
1515
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -19,6 +19,7 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters';
1919
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
2020
import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
2121
import { once } from 'vs/base/common/functional';
22+
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
2223

2324
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
2425
export const twistiePixels = 20;
@@ -131,7 +132,8 @@ export interface IExpressionTemplateData {
131132
name: HTMLSpanElement;
132133
value: HTMLSpanElement;
133134
inputBoxContainer: HTMLElement;
134-
toDispose: IDisposable;
135+
actionBar?: ActionBar;
136+
elementDisposable: IDisposable[];
135137
label: HighlightedLabel;
136138
}
137139

@@ -153,20 +155,26 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
153155

154156
const inputBoxContainer = dom.append(expression, $('.inputBoxContainer'));
155157

156-
return { expression, name, value, label, inputBoxContainer, toDispose: Disposable.None };
158+
let actionBar: ActionBar | undefined;
159+
if (this.renderActionBar) {
160+
dom.append(expression, $('.span.actionbar-spacer'));
161+
actionBar = new ActionBar(expression);
162+
}
163+
164+
return { expression, name, value, label, inputBoxContainer, actionBar, elementDisposable: [] };
157165
}
158166

159167
renderElement(node: ITreeNode<IExpression, FuzzyScore>, index: number, data: IExpressionTemplateData): void {
160-
data.toDispose.dispose();
161-
data.toDispose = Disposable.None;
162168
const { element } = node;
163169
this.renderExpression(element, data, createMatches(node.filterData));
170+
if (data.actionBar) {
171+
this.renderActionBar!(data.actionBar, element, data);
172+
}
164173
const selectedExpression = this.debugService.getViewModel().getSelectedExpression();
165174
if (element === selectedExpression?.expression || (element instanceof Variable && element.errorMessage)) {
166175
const options = this.getInputBoxOptions(element, !!selectedExpression?.settingWatch);
167176
if (options) {
168-
data.toDispose = this.renderInputBox(data.name, data.value, data.inputBoxContainer, options);
169-
return;
177+
data.elementDisposable.push(this.renderInputBox(data.name, data.value, data.inputBoxContainer, options));
170178
}
171179
}
172180
}
@@ -226,11 +234,15 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
226234
protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void;
227235
protected abstract getInputBoxOptions(expression: IExpression, settingValue: boolean): IInputBoxOptions | undefined;
228236

237+
protected renderActionBar?(actionBar: ActionBar, expression: IExpression, data: IExpressionTemplateData): void;
238+
229239
disposeElement(node: ITreeNode<IExpression, FuzzyScore>, index: number, templateData: IExpressionTemplateData): void {
230-
templateData.toDispose.dispose();
240+
dispose(templateData.elementDisposable);
241+
templateData.elementDisposable = [];
231242
}
232243

233244
disposeTemplate(templateData: IExpressionTemplateData): void {
234-
templateData.toDispose.dispose();
245+
dispose(templateData.elementDisposable);
246+
templateData.actionBar?.dispose();
235247
}
236248
}

src/vs/workbench/contrib/debug/browser/debug.contribution.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'vs/css!./media/debug.contribution';
77
import 'vs/css!./media/debugHover';
88
import * as nls from 'vs/nls';
99
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
10-
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
10+
import { MenuRegistry, MenuId, Icon } from 'vs/platform/actions/common/actions';
1111
import { Registry } from 'vs/platform/registry/common/platform';
1212
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1313
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
@@ -16,7 +16,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
1616
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
1717
import {
1818
IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
19-
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, CONTEXT_VARIABLE_IS_READONLY,
19+
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, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_CAN_VIEW_MEMORY,
2020
} from 'vs/workbench/contrib/debug/common/debug';
2121
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
2222
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
@@ -32,7 +32,7 @@ import { launchSchemaId } from 'vs/workbench/services/configuration/common/confi
3232
import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView';
3333
import { RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions';
3434
import { WatchExpressionsView, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, ADD_WATCH_ID } from 'vs/workbench/contrib/debug/browser/watchExpressionsView';
35-
import { VariablesView, SET_VARIABLE_ID, COPY_VALUE_ID, BREAK_WHEN_VALUE_CHANGES_ID, COPY_EVALUATE_PATH_ID, ADD_TO_WATCH_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID } from 'vs/workbench/contrib/debug/browser/variablesView';
35+
import { VariablesView, SET_VARIABLE_ID, COPY_VALUE_ID, BREAK_WHEN_VALUE_CHANGES_ID, COPY_EVALUATE_PATH_ID, ADD_TO_WATCH_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, VIEW_MEMORY_ID } from 'vs/workbench/contrib/debug/browser/variablesView';
3636
import { Repl } from 'vs/workbench/contrib/debug/browser/repl';
3737
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
3838
import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView';
@@ -118,14 +118,16 @@ registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, Con
118118

119119

120120
// Debug callstack context menu
121-
const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => {
121+
const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation', icon?: Icon) => {
122122
MenuRegistry.appendMenuItem(menuId, {
123123
group,
124124
when,
125125
order,
126+
icon,
126127
command: {
127128
id,
128129
title,
130+
icon,
129131
precondition
130132
}
131133
});
@@ -142,6 +144,8 @@ registerDebugViewMenuItem(MenuId.DebugCallStackContext, TERMINATE_THREAD_ID, nls
142144
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);
143145
registerDebugViewMenuItem(MenuId.DebugCallStackContext, COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), undefined, '3_modification');
144146

147+
registerDebugViewMenuItem(MenuId.DebugVariablesContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Memory"), 15, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, 'inline', icons.debugInspectMemory);
148+
145149
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');
146150
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste');
147151
registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste');
@@ -154,6 +158,7 @@ registerDebugViewMenuItem(MenuId.DebugWatchContext, ADD_WATCH_ID, ADD_WATCH_LABE
154158
registerDebugViewMenuItem(MenuId.DebugWatchContext, EDIT_EXPRESSION_COMMAND_ID, nls.localize('editWatchExpression', "Edit Expression"), 20, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, '3_modification');
155159
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)), CONTEXT_VARIABLE_IS_READONLY.toNegated(), '3_modification');
156160
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');
161+
registerDebugViewMenuItem(MenuId.DebugWatchContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Memory"), 50, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, '3_modification');
157162
registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_EXPRESSION_COMMAND_ID, nls.localize('removeWatchExpression', "Remove Expression"), 10, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, 'z_commands');
158163
registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, 20, undefined, undefined, 'z_commands');
159164

src/vs/workbench/contrib/debug/browser/debugIcons.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,5 @@ export const breakpointsActivate = registerIcon('breakpoints-activate', Codicon.
8383

8484
export const debugConsoleEvaluationInput = registerIcon('debug-console-evaluation-input', Codicon.arrowSmallRight, localize('debugConsoleEvaluationInput', 'Icon for the debug evaluation input marker.'));
8585
export const debugConsoleEvaluationPrompt = registerIcon('debug-console-evaluation-prompt', Codicon.chevronRight, localize('debugConsoleEvaluationPrompt', 'Icon for the debug evaluation prompt.'));
86+
87+
export const debugInspectMemory = registerIcon('debug-inspect-memory', Codicon.fileBinary, localize('debugInspectMemory', 'Icon for the inspect memory action.'));

0 commit comments

Comments
 (0)