From fa67c60bdc23bdea169f9d4b6a4f69e59d7378a1 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Wed, 19 Jun 2024 08:54:36 +0200 Subject: [PATCH] Enable extenders to overwrite default settings (#129) * Harmonizes naming of webview config * Extracts column ids into constants * Switches to ... menu for multiple reset options in options menu * Improve Options Overlay with an `Accordion` Fixes #77 --- media/options-widget.css | 61 ++- package.json | 41 +- src/common/manifest.ts | 8 +- src/common/messaging.ts | 1 - src/common/webview-configuration.ts | 31 +- src/common/webview-context.ts | 6 +- .../adapter-registry/adapter-capabilities.ts | 4 + src/plugin/memory-provider.ts | 9 +- src/plugin/memory-webview-main.ts | 88 ++-- src/webview/columns/ascii-column.ts | 3 +- src/webview/components/memory-table.tsx | 18 +- src/webview/components/memory-widget.tsx | 23 +- src/webview/components/multi-select.tsx | 12 +- src/webview/components/options-widget.tsx | 384 ++++++++++-------- src/webview/hovers/hover-service.tsx | 4 +- src/webview/hovers/variable-hover.tsx | 3 +- src/webview/memory-webview-view.tsx | 30 +- src/webview/utils/view-types.ts | 8 +- src/webview/utils/vscode-contexts.ts | 4 + src/webview/variables/variable-decorations.ts | 3 +- 20 files changed, 466 insertions(+), 275 deletions(-) diff --git a/media/options-widget.css b/media/options-widget.css index b2e8c1f..d77c40d 100644 --- a/media/options-widget.css +++ b/media/options-widget.css @@ -98,13 +98,51 @@ color: var(--vscode-descriptionForeground); } -.advanced-options-content { - color: var(--vscode-settings-headerForeground); +.advanced-options-panel .p-overlaypanel-content { + padding: 0.8rem; +} + +.advanced-options-panel .advanced-options-header { + font-size: 11px; + text-transform: uppercase; + line-height: 22px; + margin:0; + font-weight: normal; +} + +.advanced-options-accordion a:focus { + outline-color: var(--vscode-focusBorder); +} + +.advanced-options-accordion .p-accordion-tab { + width: 180px; +} + +.advanced-options-accordion .p-accordion-header { + border-top: 1px solid var(--vscode-widget-border); + color: var(--vscode-sideBarSectionHeader-foreground); } -.advanced-options-content h2 { - font-size: 110%; - margin: 1.2rem 0 0 0; +.advanced-options-accordion .p-accordion-header-link { + overflow: hidden; + text-overflow: ellipsis; + color: var(--vscode-sideBarSectionHeader-foreground); +} + +.advanced-options-accordion .p-accordion-header-text { + font-size: 11px; + text-transform: uppercase; + font-weight: bold; + line-height: 22px; +} + +.advanced-options-accordion .p-accordion-toggle-icon { + margin-right: 3px; +} + +.advanced-options-content { + color: var(--vscode-settings-headerForeground); + padding-bottom: 0.5rem; } .advanced-options-toggle { @@ -113,6 +151,10 @@ margin-top: 1.1rem } +.p-accordion-content .advanced-options-content { + padding-left: 0.5rem; +} + .advanced-options-content { width: 180px; text-align: left; @@ -130,7 +172,7 @@ margin: 0.5rem 0 0.2rem 0; } -.reset-advanced-options-icon { +.more-actions-overlay-icon { position: absolute; top: 12px; right: 0; @@ -143,6 +185,7 @@ overflow-y: scroll; max-height: 100%; margin-top: 0px; + width: 210px; } .advanced-options-panel::-webkit-scrollbar-track { background-color: var(--vscode-dropdown-listBackground); @@ -161,3 +204,9 @@ color: var(--vscode-button-background); margin-left: 0.2em; } + +.settings-contribution-message { + margin-top: 0; + margin-bottom: 0.5rem; + color: var(--vscode-descriptionForeground); +} \ No newline at end of file diff --git a/package.json b/package.json index 8e4c893..f7e3609 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,18 @@ "title": "Apply Memory from File...", "enablement": "memory-inspector.canWrite", "category": "Memory" + }, + { + "command": "memory-inspector.reset-display-options-to-debugger-defaults", + "title": "Reset to Debugger Defaults", + "enablement": "optionsMenu && hasDebuggerDefaults", + "category": "Memory" + }, + { + "command": "memory-inspector.reset-display-options", + "title": "Reset to Defaults", + "enablement": "optionsMenu", + "category": "Memory" } ], "menus": { @@ -183,37 +195,47 @@ { "command": "memory-inspector.toggle-variables-column", "group": "a_display@1", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.toggle-ascii-column", "group": "a_display@2", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.toggle-radix-prefix", "group": "a_display@3", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.store-file", "group": "c_store-and-restore@1", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.apply-file", "group": "c_store-and-restore@2", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.show-advanced-display-options", "group": "z_more", - "when": "webviewId === memory-inspector.memory" + "when": "webviewId === memory-inspector.memory && !optionsMenu" }, { "command": "memory-inspector.go-to-value", "group": "display@7", - "when": "webviewId === memory-inspector.memory && memory-inspector.variable.isPointer" + "when": "webviewId === memory-inspector.memory && memory-inspector.variable.isPointer && !optionsMenu" + }, + { + "command": "memory-inspector.reset-display-options-to-debugger-defaults", + "group": "a_reset@1", + "when": "webviewId === memory-inspector.memory && optionsMenu && hasDebuggerDefaults" + }, + { + "command": "memory-inspector.reset-display-options", + "group": "a_reset@2", + "when": "webviewId === memory-inspector.memory && optionsMenu" } ] }, @@ -407,6 +429,11 @@ "type": "boolean", "default": true, "description": "Display the radix prefix (e.g., '0x' for hexadecimal, '0b' for binary) before memory addresses." + }, + "memory-inspector.allowDebuggerOverwriteSettings": { + "type": "boolean", + "default": true, + "description": "Allow debuggers to overwrite the default memory display settings." } } } diff --git a/src/common/manifest.ts b/src/common/manifest.ts index be9a6d5..571fdc3 100644 --- a/src/common/manifest.ts +++ b/src/common/manifest.ts @@ -74,5 +74,9 @@ export const CONFIG_SHOW_RADIX_PREFIX = 'showRadixPrefix'; export const DEFAULT_SHOW_RADIX_PREFIX = true; // Columns -export const CONFIG_SHOW_VARIABLES_COLUMN = 'columns.variables'; -export const CONFIG_SHOW_ASCII_COLUMN = 'columns.ascii'; +export const CONFIG_SHOW_VARIABLES_COLUMN = 'variables'; +export const CONFIG_SHOW_ASCII_COLUMN = 'ascii'; +export const DEFAULT_VISIBLE_COLUMNS = [CONFIG_SHOW_VARIABLES_COLUMN, CONFIG_SHOW_ASCII_COLUMN]; + +// Extension Settings +export const CONFIG_ALLOW_DEBUGGER_OVERWRITE_SETTINGS = 'allowDebuggerOverwriteSettings'; diff --git a/src/common/messaging.ts b/src/common/messaging.ts index c30d2f5..fb5954d 100644 --- a/src/common/messaging.ts +++ b/src/common/messaging.ts @@ -48,7 +48,6 @@ export interface SessionContext { // Notifications export const readyType: NotificationType = { method: 'ready' }; export const setMemoryViewSettingsType: NotificationType> = { method: 'setMemoryViewSettings' }; -export const resetMemoryViewSettingsType: NotificationType = { method: 'resetMemoryViewSettings' }; export const setTitleType: NotificationType = { method: 'setTitle' }; export const memoryWrittenType: NotificationType = { method: 'memoryWritten' }; export const sessionContextChangedType: NotificationType = { method: 'sessionContextChanged' }; diff --git a/src/common/webview-configuration.ts b/src/common/webview-configuration.ts index 5532e65..7968abf 100644 --- a/src/common/webview-configuration.ts +++ b/src/common/webview-configuration.ts @@ -17,16 +17,22 @@ import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; import { Endianness, GroupsPerRowOption, PeriodicRefresh, RefreshOnStop } from './manifest'; import { Radix } from './memory-range'; -/** The memory display configuration that can be specified for the memory widget. */ -export interface MemoryDisplayConfiguration { +/** Specifies the settings for displaying memory addresses in the memory data table. */ +export interface MemoryAddressDisplaySettings { + addressPadding: AddressPadding; + addressRadix: Radix; + showRadixPrefix: boolean; +} + +export type AddressPadding = 'Min' | 0 | 32 | 64; + +/** Specifies the settings for displaying memory data in the memory data table, including the memory addresses. */ +export interface MemoryDataDisplaySettings extends MemoryAddressDisplaySettings { bytesPerMau: number; mausPerGroup: number; groupsPerRow: GroupsPerRowOption; endianness: Endianness; scrollingBehavior: ScrollingBehavior; - addressPadding: AddressPadding; - addressRadix: Radix; - showRadixPrefix: boolean; refreshOnStop: RefreshOnStop; periodicRefresh: PeriodicRefresh; periodicRefreshInterval: number; @@ -34,14 +40,21 @@ export interface MemoryDisplayConfiguration { export type ScrollingBehavior = 'Paginate' | 'Grow' | 'Auto-Append'; -export type AddressPadding = 'Minimal' | number; - -export interface ColumnVisibilityStatus { +/** Specifies the display settings of the memory data table, including the memory data and addresses. */ +export interface MemoryDisplaySettings extends MemoryDataDisplaySettings { visibleColumns: string[]; } +/** An extender's contribution to the `MemoryDisplaySettings` via the `AdapterCapabilities`. */ +export interface MemoryDisplaySettingsContribution { + message?: string; + settings?: Partial; +} + /** All settings related to memory view that can be specified for the webview from the extension "main". */ -export interface MemoryViewSettings extends ColumnVisibilityStatus, MemoryDisplayConfiguration { +export interface MemoryViewSettings extends MemoryDisplaySettings { title: string messageParticipant: WebviewIdMessageParticipant; + hasDebuggerDefaults?: boolean; + contributionMessage?: string; } diff --git a/src/common/webview-context.ts b/src/common/webview-context.ts index c652dc5..ac142f2 100644 --- a/src/common/webview-context.ts +++ b/src/common/webview-context.ts @@ -15,6 +15,7 @@ ********************************************************************************/ import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; +import * as manifest from '../common/manifest'; import { Endianness } from './manifest'; import { VariableMetadata } from './memory-range'; import { ReadMemoryArguments } from './messaging'; @@ -25,6 +26,7 @@ export interface WebviewContext { showAsciiColumn: boolean showVariablesColumn: boolean, showRadixPrefix: boolean, + hasDebuggerDefaults?: boolean, endianness: Endianness, bytesPerMau: number, activeReadArguments: Required @@ -46,10 +48,10 @@ export interface WebviewVariableContext extends WebviewCellContext { export function getVisibleColumns(context: WebviewContext): string[] { const columns = []; if (context.showAsciiColumn) { - columns.push('ascii'); + columns.push(manifest.CONFIG_SHOW_ASCII_COLUMN); } if (context.showVariablesColumn) { - columns.push('variables'); + columns.push(manifest.CONFIG_SHOW_VARIABLES_COLUMN); } return columns; } diff --git a/src/plugin/adapter-registry/adapter-capabilities.ts b/src/plugin/adapter-registry/adapter-capabilities.ts index d3509e5..67886d4 100644 --- a/src/plugin/adapter-registry/adapter-capabilities.ts +++ b/src/plugin/adapter-registry/adapter-capabilities.ts @@ -18,6 +18,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import * as vscode from 'vscode'; import { isDebugRequest, isDebugResponse } from '../../common/debug-requests'; import { VariableRange } from '../../common/memory-range'; +import { MemoryDisplaySettingsContribution } from '../../common/webview-configuration'; import { Logger } from '../logger'; /** Represents capabilities that may be achieved with particular debug adapters but are not part of the DAP */ @@ -30,6 +31,9 @@ export interface AdapterCapabilities { getAddressOfVariable?(session: vscode.DebugSession, variableName: string): Promise; /** Resolves the size of a given variable in bytes within the current context. */ getSizeOfVariable?(session: vscode.DebugSession, variableName: string): Promise; + /** Retrieve the suggested default display settings for the memory view. */ + getMemoryDisplaySettings?(session: vscode.DebugSession): Promise>; + /** Initialize the trackers of this adapter's for the debug session. */ initializeAdapterTracker?(session: vscode.DebugSession): vscode.DebugAdapterTracker | undefined; } diff --git a/src/plugin/memory-provider.ts b/src/plugin/memory-provider.ts index 10b3e75..71584a2 100644 --- a/src/plugin/memory-provider.ts +++ b/src/plugin/memory-provider.ts @@ -20,6 +20,7 @@ import { sendRequest } from '../common/debug-requests'; import { stringToBytesMemory } from '../common/memory'; import { VariableRange } from '../common/memory-range'; import { ReadMemoryResult, WriteMemoryResult } from '../common/messaging'; +import { MemoryDisplaySettingsContribution } from '../common/webview-configuration'; import { AdapterRegistry } from './adapter-registry/adapter-registry'; import { isSessionEvent, SessionTracker } from './session-tracker'; @@ -84,8 +85,14 @@ export class MemoryProvider { } public async getSizeOfVariable(variableName: string): Promise { - const session = this.sessionTracker.assertActiveSession('get address of variable'); + const session = this.sessionTracker.assertActiveSession('get size of variable'); const handler = this.adapterRegistry?.getHandlerForSession(session.type); return handler?.getSizeOfVariable?.(session, variableName); } + + public async getMemoryDisplaySettingsContribution(): Promise { + const session = this.sessionTracker.assertActiveSession('get memory display settings contribution'); + const handler = this.adapterRegistry?.getHandlerForSession(session.type); + return handler?.getMemoryDisplaySettings?.(session) ?? {}; + } } diff --git a/src/plugin/memory-webview-main.ts b/src/plugin/memory-webview-main.ts index 92a3b88..d13914a 100644 --- a/src/plugin/memory-webview-main.ts +++ b/src/plugin/memory-webview-main.ts @@ -31,7 +31,6 @@ import { ReadMemoryResult, readMemoryType, readyType, - resetMemoryViewSettingsType, SessionContext, sessionContextChangedType, setMemoryViewSettingsType, @@ -45,8 +44,9 @@ import { WriteMemoryResult, writeMemoryType, } from '../common/messaging'; -import { MemoryViewSettings, ScrollingBehavior } from '../common/webview-configuration'; +import { MemoryDisplaySettings, MemoryDisplaySettingsContribution, MemoryViewSettings, ScrollingBehavior } from '../common/webview-configuration'; import { getVisibleColumns, isWebviewVariableContext, WebviewContext } from '../common/webview-context'; +import { AddressPaddingOptions } from '../webview/utils/view-types'; import { outputChannelLogger } from './logger'; import { MemoryProvider } from './memory-provider'; import { ApplyCommandType, StoreCommandType } from './memory-storage'; @@ -65,6 +65,8 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { public static ToggleAsciiColumnCommandType = `${manifest.PACKAGE_NAME}.toggle-ascii-column`; public static ToggleVariablesColumnCommandType = `${manifest.PACKAGE_NAME}.toggle-variables-column`; public static ToggleRadixPrefixCommandType = `${manifest.PACKAGE_NAME}.toggle-radix-prefix`; + public static ResetDisplayOptionsToDefaultsType = `${manifest.PACKAGE_NAME}.reset-display-options`; + public static ResetDisplayOptionsToDebuggerDefaultsType = `${manifest.PACKAGE_NAME}.reset-display-options-to-debugger-defaults`; public static ShowAdvancedDisplayConfigurationCommandType = `${manifest.PACKAGE_NAME}.show-advanced-display-options`; public static GetWebviewSelectionCommandType = `${manifest.PACKAGE_NAME}.get-webview-selection`; @@ -92,17 +94,26 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { } }), vscode.commands.registerCommand(MemoryWebview.ToggleVariablesColumnCommandType, (ctx: WebviewContext) => { - this.toggleWebviewColumn(ctx, 'variables'); + this.toggleWebviewColumn(ctx, manifest.CONFIG_SHOW_VARIABLES_COLUMN); }), vscode.commands.registerCommand(MemoryWebview.ToggleAsciiColumnCommandType, (ctx: WebviewContext) => { - this.toggleWebviewColumn(ctx, 'ascii'); + this.toggleWebviewColumn(ctx, manifest.CONFIG_SHOW_ASCII_COLUMN); }), vscode.commands.registerCommand(MemoryWebview.ToggleRadixPrefixCommandType, (ctx: WebviewContext) => { this.setMemoryViewSettings(ctx.messageParticipant, { showRadixPrefix: !ctx.showRadixPrefix }); }), + vscode.commands.registerCommand(MemoryWebview.ShowAdvancedDisplayConfigurationCommandType, async (ctx: WebviewContext) => { this.messenger.sendNotification(showAdvancedOptionsType, ctx.messageParticipant, undefined); }), + + vscode.commands.registerCommand(MemoryWebview.ResetDisplayOptionsToDefaultsType, (ctx: WebviewContext) => { + this.setMemoryDisplaySettings(ctx.messageParticipant, undefined, false); + }), + vscode.commands.registerCommand(MemoryWebview.ResetDisplayOptionsToDebuggerDefaultsType, (ctx: WebviewContext) => { + this.setMemoryDisplaySettings(ctx.messageParticipant); + }), + vscode.commands.registerCommand(MemoryWebview.GetWebviewSelectionCommandType, (ctx: WebviewContext) => this.getWebviewSelection(ctx.messageParticipant)), ); }; @@ -192,13 +203,16 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { protected setWebviewMessageListener(panel: vscode.WebviewPanel, options?: MemoryOptions): void { const participant = this.messenger.registerWebviewPanel(panel); const disposables = [ - this.messenger.onNotification(readyType, () => this.initialize(participant, panel, options), { sender: participant }), + this.messenger.onNotification(readyType, async () => { + this.setSessionContext(participant, this.createContext()); + await this.setMemoryDisplaySettings(participant, panel.title); + this.refresh(participant, options); + }, { sender: participant }), this.messenger.onRequest(setOptionsType, newOptions => { options = { ...options, ...newOptions }; }, { sender: participant }), this.messenger.onRequest(logMessageType, message => outputChannelLogger.info('[webview]:', message), { sender: participant }), this.messenger.onRequest(readMemoryType, request => this.readMemory(request), { sender: participant }), this.messenger.onRequest(writeMemoryType, request => this.writeMemory(request), { sender: participant }), this.messenger.onRequest(getVariablesType, request => this.getVariables(request), { sender: participant }), - this.messenger.onNotification(resetMemoryViewSettingsType, () => this.setInitialSettings(participant, panel.title), { sender: participant }), this.messenger.onNotification(setTitleType, title => { panel.title = title; }, { sender: participant }), this.messenger.onRequest(storeMemoryType, args => this.storeMemory(args), { sender: participant }), this.messenger.onRequest(applyMemoryType, () => this.applyMemory(), { sender: participant }), @@ -207,20 +221,23 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { panel.onDidDispose(() => disposables.forEach(disposable => disposable.dispose())); } - protected async initialize(participant: WebviewIdMessageParticipant, panel: vscode.WebviewPanel, options?: MemoryOptions): Promise { - this.setSessionContext(participant, this.createContext()); - this.setInitialSettings(participant, panel.title); - this.refresh(participant, options); + protected async setMemoryDisplaySettings(messageParticipant: WebviewIdMessageParticipant, title?: string, includeContributions: boolean = true): Promise { + const defaultSettings = this.getDefaultMemoryDisplaySettings(); + const settingsContribution = includeContributions ? await this.getMemoryDisplaySettingsContribution() : {}; + const settings = settingsContribution.settings ? { ...settingsContribution.settings, hasDebuggerDefaults: true } : {}; + this.setMemoryViewSettings(messageParticipant, { + messageParticipant, + title, + ...defaultSettings, + ...settings, + contributionMessage: settingsContribution.message + }); } protected async refresh(participant: WebviewIdMessageParticipant, options: MemoryOptions = {}): Promise { this.messenger.sendRequest(setOptionsType, participant, options); } - protected setInitialSettings(webviewParticipant: WebviewIdMessageParticipant, title: string): void { - this.setMemoryViewSettings(webviewParticipant, this.getMemoryViewSettings(webviewParticipant, title)); - } - protected setMemoryViewSettings(webviewParticipant: WebviewIdMessageParticipant, settings: Partial): void { this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, settings); } @@ -229,29 +246,36 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.messenger.sendNotification(sessionContextChangedType, webviewParticipant, context); } - protected getMemoryViewSettings(messageParticipant: WebviewIdMessageParticipant, title: string): MemoryViewSettings { - const memoryInspectorConfiguration = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME); - const bytesPerMau = memoryInspectorConfiguration.get(manifest.CONFIG_BYTES_PER_MAU, manifest.DEFAULT_BYTES_PER_MAU); - const mausPerGroup = memoryInspectorConfiguration.get(manifest.CONFIG_MAUS_PER_GROUP, manifest.DEFAULT_MAUS_PER_GROUP); - const groupsPerRow = memoryInspectorConfiguration.get(manifest.CONFIG_GROUPS_PER_ROW, manifest.DEFAULT_GROUPS_PER_ROW); - const endianness = memoryInspectorConfiguration.get(manifest.CONFIG_ENDIANNESS, manifest.DEFAULT_ENDIANNESS); - const scrollingBehavior = memoryInspectorConfiguration.get(manifest.CONFIG_SCROLLING_BEHAVIOR, manifest.DEFAULT_SCROLLING_BEHAVIOR); - const visibleColumns = CONFIGURABLE_COLUMNS - .filter(column => vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(column, false)) - .map(columnId => columnId.replace('columns.', '')); - const addressPadding = memoryInspectorConfiguration.get(manifest.CONFIG_ADDRESS_PADDING, manifest.DEFAULT_ADDRESS_PADDING); - const addressRadix = memoryInspectorConfiguration.get(manifest.CONFIG_ADDRESS_RADIX, manifest.DEFAULT_ADDRESS_RADIX); - const showRadixPrefix = memoryInspectorConfiguration.get(manifest.CONFIG_SHOW_RADIX_PREFIX, manifest.DEFAULT_SHOW_RADIX_PREFIX); - const refreshOnStop = memoryInspectorConfiguration.get(manifest.CONFIG_REFRESH_ON_STOP, manifest.DEFAULT_REFRESH_ON_STOP); - const periodicRefresh = memoryInspectorConfiguration.get(manifest.CONFIG_PERIODIC_REFRESH, manifest.DEFAULT_PERIODIC_REFRESH); - const periodicRefreshInterval = memoryInspectorConfiguration.get(manifest.CONFIG_PERIODIC_REFRESH_INTERVAL, manifest.DEFAULT_PERIODIC_REFRESH_INTERVAL); + protected getDefaultMemoryDisplaySettings(): MemoryDisplaySettings { + const memoryInspectorSettings = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME); + const bytesPerMau = memoryInspectorSettings.get(manifest.CONFIG_BYTES_PER_MAU, manifest.DEFAULT_BYTES_PER_MAU); + const mausPerGroup = memoryInspectorSettings.get(manifest.CONFIG_MAUS_PER_GROUP, manifest.DEFAULT_MAUS_PER_GROUP); + const groupsPerRow = memoryInspectorSettings.get(manifest.CONFIG_GROUPS_PER_ROW, manifest.DEFAULT_GROUPS_PER_ROW); + const endianness = memoryInspectorSettings.get(manifest.CONFIG_ENDIANNESS, manifest.DEFAULT_ENDIANNESS); + const scrollingBehavior = memoryInspectorSettings.get(manifest.CONFIG_SCROLLING_BEHAVIOR, manifest.DEFAULT_SCROLLING_BEHAVIOR); + const visibleColumns = CONFIGURABLE_COLUMNS.filter(column => memoryInspectorSettings.get(`columns.${column}`, false)); + const addressPadding = AddressPaddingOptions[memoryInspectorSettings.get(manifest.CONFIG_ADDRESS_PADDING, manifest.DEFAULT_ADDRESS_PADDING)]; + const addressRadix = memoryInspectorSettings.get(manifest.CONFIG_ADDRESS_RADIX, manifest.DEFAULT_ADDRESS_RADIX); + const showRadixPrefix = memoryInspectorSettings.get(manifest.CONFIG_SHOW_RADIX_PREFIX, manifest.DEFAULT_SHOW_RADIX_PREFIX); + const refreshOnStop = memoryInspectorSettings.get(manifest.CONFIG_REFRESH_ON_STOP, manifest.DEFAULT_REFRESH_ON_STOP); + const periodicRefresh = memoryInspectorSettings.get(manifest.CONFIG_PERIODIC_REFRESH, manifest.DEFAULT_PERIODIC_REFRESH); + const periodicRefreshInterval = memoryInspectorSettings.get(manifest.CONFIG_PERIODIC_REFRESH_INTERVAL, manifest.DEFAULT_PERIODIC_REFRESH_INTERVAL); return { - messageParticipant, title, bytesPerMau, mausPerGroup, groupsPerRow, - endianness, scrollingBehavior, visibleColumns, addressPadding, addressRadix, showRadixPrefix, + bytesPerMau, mausPerGroup, groupsPerRow, endianness, scrollingBehavior, + visibleColumns, addressPadding, addressRadix, showRadixPrefix, refreshOnStop, periodicRefresh, periodicRefreshInterval }; } + protected async getMemoryDisplaySettingsContribution(): Promise { + const memoryInspectorSettings = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME); + const allowDebuggerOverwriteSettings = memoryInspectorSettings.get(manifest.CONFIG_ALLOW_DEBUGGER_OVERWRITE_SETTINGS, true); + if (allowDebuggerOverwriteSettings) { + return this.memoryProvider.getMemoryDisplaySettingsContribution(); + } + return { settings: {}, message: undefined }; + } + protected handleSessionEvent(participant: WebviewIdMessageParticipant, event: SessionEvent): void { if (isSessionEvent('active', event)) { this.setSessionContext(participant, this.createContext(event.session?.raw)); diff --git a/src/webview/columns/ascii-column.ts b/src/webview/columns/ascii-column.ts index b90deb7..e350b73 100644 --- a/src/webview/columns/ascii-column.ts +++ b/src/webview/columns/ascii-column.ts @@ -15,6 +15,7 @@ ********************************************************************************/ import { ReactNode } from 'react'; +import * as manifest from '../../common/manifest'; import { Memory } from '../../common/memory'; import { BigIntMemoryRange, toOffset } from '../../common/memory-range'; import { ColumnContribution, TableRenderOptions } from './column-contribution-service'; @@ -29,7 +30,7 @@ function getASCIIForSingleByte(byte: number | undefined): string { } export class AsciiColumn implements ColumnContribution { - readonly id = 'ascii'; + readonly id = manifest.CONFIG_SHOW_ASCII_COLUMN; readonly label = 'ASCII'; readonly priority = 3; render(range: BigIntMemoryRange, memory: Memory, options: TableRenderOptions): ReactNode { diff --git a/src/webview/components/memory-table.tsx b/src/webview/components/memory-table.tsx index 6dbde5c..d3a0493 100644 --- a/src/webview/components/memory-table.tsx +++ b/src/webview/components/memory-table.tsx @@ -25,10 +25,9 @@ import { TooltipEvent } from 'primereact/tooltip/tooltipoptions'; import { classNames } from 'primereact/utils'; import React from 'react'; import { Memory } from '../../common/memory'; -import { WebviewSelection } from '../../common/messaging'; -import { MemoryOptions, ReadMemoryArguments } from '../../common/messaging'; +import { MemoryOptions, ReadMemoryArguments, WebviewSelection } from '../../common/messaging'; import { tryToNumber } from '../../common/typescript'; -import { MemoryDisplayConfiguration, ScrollingBehavior } from '../../common/webview-configuration'; +import { MemoryDataDisplaySettings, ScrollingBehavior } from '../../common/webview-configuration'; import { TableRenderOptions } from '../columns/column-contribution-service'; import { DataColumn } from '../columns/data-column'; import type { HoverService } from '../hovers/hover-service'; @@ -129,7 +128,7 @@ export const MoreMemorySelect: React.FC; activeReadArguments: Required; memory?: Memory; @@ -244,11 +243,18 @@ export class MemoryTable extends React.PureComponent ({ ...prev, selection: null })); } - const hasDisplayChanged = prevProps.bytesPerMau !== this.props.bytesPerMau || prevProps.mausPerGroup !== this.props.mausPerGroup || - (prevProps.groupsPerRow !== 'Autofit' && this.props.groupsPerRow === 'Autofit'); + // update the groups per row to render if the display options that impact the available width may have changed or we didn't have a memory before + const hasDisplayChanged = prevProps.bytesPerMau !== this.props.bytesPerMau + || prevProps.mausPerGroup !== this.props.mausPerGroup + || (prevProps.groupsPerRow !== 'Autofit' && this.props.groupsPerRow === 'Autofit') + || prevProps.columnOptions !== this.props.columnOptions + || prevProps.effectiveAddressLength !== this.props.effectiveAddressLength + || prevProps.showRadixPrefix !== this.props.showRadixPrefix + || prevProps.memory === undefined; if (hasDisplayChanged) { this.ensureGroupsPerRowToRenderIsSet(); } + if (this.props.memory !== undefined && this.props.scrollingBehavior === 'Auto-Append') { this.ensureSufficientVisibleRowsForScrollbar(); diff --git a/src/webview/components/memory-widget.tsx b/src/webview/components/memory-widget.tsx index e0829df..1c46fbf 100644 --- a/src/webview/components/memory-widget.tsx +++ b/src/webview/components/memory-widget.tsx @@ -16,10 +16,11 @@ import React from 'react'; import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; +import * as manifest from '../../common/manifest'; import { Memory } from '../../common/memory'; import { WebviewSelection } from '../../common/messaging'; import { MemoryOptions, ReadMemoryArguments, SessionContext } from '../../common/messaging'; -import { MemoryDisplayConfiguration } from '../../common/webview-configuration'; +import { MemoryDataDisplaySettings } from '../../common/webview-configuration'; import { ColumnStatus } from '../columns/column-contribution-service'; import { HoverService } from '../hovers/hover-service'; import { Decoration, MemoryState } from '../utils/view-types'; @@ -27,7 +28,7 @@ import { createAppVscodeContext, VscodeContext } from '../utils/vscode-contexts' import { MemoryTable } from './memory-table'; import { OptionsWidget } from './options-widget'; -interface MemoryWidgetProps extends MemoryDisplayConfiguration { +interface MemoryWidgetProps extends MemoryDataDisplaySettings { messageParticipant: WebviewIdMessageParticipant; sessionContext: SessionContext; configuredReadArguments: Required; @@ -39,12 +40,13 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration { columns: ColumnStatus[]; effectiveAddressLength: number; isMemoryFetching: boolean; + hasDebuggerDefaults?: boolean; + settingsContributionMessage?: string; updateMemoryState: (state: Partial) => void; toggleColumn(id: string, active: boolean): void; isFrozen: boolean; toggleFrozen: () => void; - updateMemoryDisplayConfiguration: (memoryArguments: Partial) => void; - resetMemoryDisplayConfiguration: () => void; + updateMemoryDisplaySettings: (memoryArguments: Partial) => void; updateTitle: (title: string) => void; fetchMemory(partialOptions?: MemoryOptions): Promise; storeMemory(): void; @@ -67,15 +69,16 @@ export class MemoryWidget extends React.Component candidate.active).map(column => column.contribution.id); - const { messageParticipant, showRadixPrefix, endianness, bytesPerMau, activeReadArguments } = this.props; + const { messageParticipant, showRadixPrefix, endianness, bytesPerMau, activeReadArguments, hasDebuggerDefaults } = this.props; return createAppVscodeContext({ messageParticipant, showRadixPrefix, - showAsciiColumn: visibleColumns.includes('ascii'), - showVariablesColumn: visibleColumns.includes('variables'), + showAsciiColumn: visibleColumns.includes(manifest.CONFIG_SHOW_ASCII_COLUMN), + showVariablesColumn: visibleColumns.includes(manifest.CONFIG_SHOW_VARIABLES_COLUMN), + activeReadArguments, + hasDebuggerDefaults, endianness, bytesPerMau, - activeReadArguments }); } @@ -98,11 +101,11 @@ export class MemoryWidget extends React.Component unknown; } -const MultiSelectBar: React.FC = ({ items, onSelectionChanged, id }) => { +export const MultiSelectBar: React.FC = ({ items, onSelectionChanged, id }) => { const changeHandler: ((e: CheckboxChangeEvent) => unknown) = React.useCallback(e => { const target = e.target as HTMLInputElement; if (target) { @@ -54,10 +55,3 @@ const MultiSelectBar: React.FC = ({ items, onSelectionChanged, ); }; - -export const MultiSelectWithLabel: React.FC = ({ id, label, items, onSelectionChanged }) => ( -
-

{label}

- -
-); diff --git a/src/webview/components/options-widget.tsx b/src/webview/components/options-widget.tsx index 13ccd66..741147f 100644 --- a/src/webview/components/options-widget.tsx +++ b/src/webview/components/options-widget.tsx @@ -15,6 +15,7 @@ ********************************************************************************/ import { Formik, FormikConfig, FormikErrors, FormikProps } from 'formik'; +import { Accordion, AccordionTab, AccordionTabChangeEvent } from 'primereact/accordion'; import { Button } from 'primereact/button'; import { Checkbox } from 'primereact/checkbox'; import { Dropdown, DropdownChangeEvent } from 'primereact/dropdown'; @@ -22,7 +23,7 @@ import { InputNumber } from 'primereact/inputnumber'; import { InputText } from 'primereact/inputtext'; import { OverlayPanel } from 'primereact/overlaypanel'; import { classNames } from 'primereact/utils'; -import React, { FocusEventHandler, KeyboardEvent, KeyboardEventHandler, MouseEventHandler, ReactNode } from 'react'; +import React, { FocusEventHandler, KeyboardEvent, KeyboardEventHandler, ReactNode } from 'react'; import { CONFIG_BYTES_PER_MAU_CHOICES, CONFIG_GROUPS_PER_ROW_CHOICES, CONFIG_MAUS_PER_GROUP_CHOICES, ENDIANNESS_CHOICES, PERIODIC_REFRESH_CHOICES } from '../../common/manifest'; import { validateCount, validateMemoryReference, validateOffset } from '../../common/memory'; import { MemoryOptions, ReadMemoryArguments, SessionContext } from '../../common/messaging'; @@ -30,8 +31,8 @@ import { tryToNumber } from '../../common/typescript'; import { TableRenderOptions } from '../columns/column-contribution-service'; import { DEFAULT_MEMORY_DISPLAY_CONFIGURATION } from '../memory-webview-view'; import { AddressPaddingOptions, DEFAULT_READ_ARGUMENTS, MemoryState, SerializedTableRenderOptions } from '../utils/view-types'; -import { createSectionVscodeContext } from '../utils/vscode-contexts'; -import { MultiSelectWithLabel } from './multi-select'; +import { createOverlayMoreActionsVscodeContext, createSectionVscodeContext } from '../utils/vscode-contexts'; +import { MultiSelectBar } from './multi-select'; export interface OptionsWidgetProps extends Omit { @@ -39,8 +40,8 @@ export interface OptionsWidgetProps configuredReadArguments: Required; activeReadArguments: Required; title: string; + settingsContributionMessage?: string; updateRenderOptions: (options: Partial) => void; - resetRenderOptions: () => void; updateTitle: (title: string) => void; updateMemoryState: (state: Partial) => void; fetchMemory(partialOptions?: MemoryOptions): Promise @@ -53,6 +54,10 @@ export interface OptionsWidgetProps interface OptionsWidgetState { isTitleEditing: boolean; + showColumnsOptions: boolean; + showMemoryOptions: boolean; + showAddressOptions: boolean; + showRefreshOptions: boolean; } const enum InputId { @@ -85,6 +90,13 @@ export class OptionsWidget extends React.Component(); protected optionsMenuContext = createSectionVscodeContext('optionsWidget'); protected advancedOptionsContext = createSectionVscodeContext('advancedOptionsOverlay'); + protected moreActionsOverlayMenuContext = createOverlayMoreActionsVscodeContext(); + protected advancedOptionsSections: { key: keyof OptionsWidgetState, index: number }[] = [ + { key: 'showColumnsOptions', index: 0 }, + { key: 'showMemoryOptions', index: 1 }, + { key: 'showAddressOptions', index: 2 }, + { key: 'showRefreshOptions', index: 3 } + ]; protected get optionsFormValues(): OptionsForm { return { @@ -103,7 +115,13 @@ export class OptionsWidget extends React.Component this.props.fetchMemory(this.props.configuredReadArguments), }; - this.state = { isTitleEditing: false }; + this.state = { + isTitleEditing: false, + showColumnsOptions: false, + showMemoryOptions: true, + showAddressOptions: false, + showRefreshOptions: true + }; } protected validate = (values: OptionsForm) => { @@ -286,171 +304,190 @@ export class OptionsWidget extends React.Component )} - + +

Advanced Options

+ {this.props.settingsContributionMessage && ( +

{this.props.settingsContributionMessage}

+ )}