diff --git a/package.json b/package.json index 3f3a852..3e3bbee 100644 --- a/package.json +++ b/package.json @@ -371,6 +371,11 @@ "type": "boolean", "default": true, "description": "Display the radix prefix (e.g., '0x' for hexadecimal, '0b' for binary) before memory addresses." + }, + "memory-inspector.allowSettingsExtension": { + "type": "boolean", + "default": true, + "description": "Allow other extensions to overwrite the default memory display settings." } } } diff --git a/src/common/memory-display-settings.ts b/src/common/memory-display-settings.ts new file mode 100644 index 0000000..24e35c4 --- /dev/null +++ b/src/common/memory-display-settings.ts @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (C) 2024 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { Endianness, Radix } from './memory-range'; + +/** 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: 'Autofit' | number; + endianness: Endianness; + scrollingBehavior: ScrollingBehavior; +} + +export type ScrollingBehavior = 'Paginate' | 'Grow' | 'Auto-Append'; + +/** 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; +} diff --git a/src/plugin/adapter-registry/adapter-capabilities.ts b/src/plugin/adapter-registry/adapter-capabilities.ts index 0afd107..36e5b48 100644 --- a/src/plugin/adapter-registry/adapter-capabilities.ts +++ b/src/plugin/adapter-registry/adapter-capabilities.ts @@ -17,6 +17,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import * as vscode from 'vscode'; import { isDebugRequest, isDebugResponse } from '../../common/debug-requests'; +import { MemoryDisplaySettingsContribution } from '../../common/memory-display-settings'; import { VariableRange } from '../../common/memory-range'; import { Logger } from '../logger'; @@ -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 enforced 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/manifest.ts b/src/plugin/manifest.ts index 98f76fc..23a10cb 100644 --- a/src/plugin/manifest.ts +++ b/src/plugin/manifest.ts @@ -64,3 +64,6 @@ export const DEFAULT_SHOW_RADIX_PREFIX = true; // Columns export const CONFIG_SHOW_VARIABLES_COLUMN = 'columns.variables'; export const CONFIG_SHOW_ASCII_COLUMN = 'columns.ascii'; + +// Extension Settings +export const CONFIG_ALLOW_SETTINGS_EXTENSION = 'allowSettingsExtension'; diff --git a/src/plugin/memory-provider.ts b/src/plugin/memory-provider.ts index 7a77996..786647b 100644 --- a/src/plugin/memory-provider.ts +++ b/src/plugin/memory-provider.ts @@ -18,6 +18,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import * as vscode from 'vscode'; import { isDebugEvent, isDebugRequest, isDebugResponse, sendRequest } from '../common/debug-requests'; import { stringToBytesMemory } from '../common/memory'; +import { MemoryDisplaySettingsContribution } from '../common/memory-display-settings'; import { VariableRange, WrittenMemory } from '../common/memory-range'; import { ReadMemoryResult, SessionContext, WriteMemoryResult } from '../common/messaging'; import { AdapterRegistry } from './adapter-registry/adapter-registry'; @@ -183,4 +184,11 @@ export class MemoryProvider { const handler = this.adapterRegistry?.getHandlerForSession(session.type); return handler?.getSizeOfVariable?.(session, variableName); } + + public async getMemoryDisplaySettingsContribution(): Promise { + const session = this.assertActiveSession('get settings of variable'); + 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 38f9473..c9a2652 100644 --- a/src/plugin/memory-webview-main.ts +++ b/src/plugin/memory-webview-main.ts @@ -17,6 +17,7 @@ import * as vscode from 'vscode'; import { Messenger } from 'vscode-messenger'; import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; +import { MemoryDisplaySettings, MemoryDisplaySettingsContribution, ScrollingBehavior } from '../common/memory-display-settings'; import { Endianness, VariableRange } from '../common/memory-range'; import { applyMemoryType, @@ -44,7 +45,7 @@ import { writeMemoryType, } from '../common/messaging'; import { getVisibleColumns, WebviewContext } from '../common/webview-context'; -import { AddressPaddingOptions, MemoryViewSettings, ScrollingBehavior } from '../webview/utils/view-types'; +import { AddressPaddingOptions, MemoryViewSettings } from '../webview/utils/view-types'; import { isVariablesContext } from './external-views'; import { outputChannelLogger } from './logger'; import * as manifest from './manifest'; @@ -204,9 +205,9 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { const participant = this.messenger.registerWebviewPanel(panel); const disposables = [ - this.messenger.onNotification(readyType, () => { - this.setInitialSettings(participant, panel.title); + this.messenger.onNotification(readyType, async () => { this.setSessionContext(participant, this.memoryProvider.createContext()); + await this.setMemoryDisplaySettings(participant, panel.title); this.refresh(participant, options); }, { sender: participant }), this.messenger.onRequest(setOptionsType, o => { @@ -216,7 +217,7 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { 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(resetMemoryViewSettingsType, () => this.setMemoryDisplaySettings(participant), { 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 }), @@ -237,39 +238,59 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { panel.onDidDispose(() => disposables.forEach(disposable => disposable.dispose())); } - protected async refresh(participant: WebviewIdMessageParticipant, options: MemoryOptions = {}): Promise { - this.messenger.sendRequest(setOptionsType, participant, options); + protected async setMemoryDisplaySettings(participant: WebviewIdMessageParticipant, title?: string): Promise { + const defaultSettings = this.getDefaultMemoryDisplaySettings(); + const settingsContribution = await this.getSettingsContribution(); + this.messenger.sendNotification(setMemoryViewSettingsType, participant, { + title, + ...defaultSettings, + ...settingsContribution.settings, + contributionMessage: settingsContribution.message + }); } - protected setInitialSettings(webviewParticipant: WebviewIdMessageParticipant, title: string): void { - this.setMemoryViewSettings(webviewParticipant, this.getMemoryViewSettings(webviewParticipant, title)); + protected async refresh(participant: WebviewIdMessageParticipant, options: MemoryOptions = {}): Promise { + this.messenger.sendRequest(setOptionsType, participant, options); } protected setMemoryViewSettings(webviewParticipant: WebviewIdMessageParticipant, settings: Partial): void { this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, settings); } + protected applyDisplaySettingContributions(webviewParticipant: WebviewIdMessageParticipant, settingsContribution: Partial): void { + const { settings, message } = settingsContribution; + if (settings && Object.keys(settings).length) { + this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, { ...settings, contributionMessage: message }); + } + } + protected setSessionContext(webviewParticipant: WebviewIdMessageParticipant, context: SessionContext): void { 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); + 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 => vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(column, false)) + .filter(column => memoryInspectorSettings.get(column, false)) .map(columnId => columnId.replace('columns.', '')); - const addressPadding = AddressPaddingOptions[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); - return { - messageParticipant, title, bytesPerMau, mausPerGroup, groupsPerRow, - endianness, scrollingBehavior, visibleColumns, addressPadding, addressRadix, showRadixPrefix - }; + 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); + return { bytesPerMau, mausPerGroup, groupsPerRow, endianness, scrollingBehavior, visibleColumns, addressPadding, addressRadix, showRadixPrefix }; + } + + protected async getSettingsContribution(): Promise { + const memoryInspectorSettings = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME); + const allowSettingsExtension = memoryInspectorSettings.get(manifest.CONFIG_ALLOW_SETTINGS_EXTENSION, true); + if (allowSettingsExtension) { + return this.memoryProvider.getMemoryDisplaySettingsContribution(); + } + return { settings: {}, message: '' }; } protected async readMemory(request: ReadMemoryArguments): Promise { diff --git a/src/webview/components/memory-table.tsx b/src/webview/components/memory-table.tsx index a860d76..bb79879 100644 --- a/src/webview/components/memory-table.tsx +++ b/src/webview/components/memory-table.tsx @@ -25,13 +25,14 @@ import { TooltipEvent } from 'primereact/tooltip/tooltipoptions'; import { classNames } from 'primereact/utils'; import React from 'react'; import { Memory } from '../../common/memory'; +import { MemoryDataDisplaySettings, ScrollingBehavior } from '../../common/memory-display-settings'; import { WebviewSelection } from '../../common/messaging'; import { MemoryOptions, ReadMemoryArguments } from '../../common/messaging'; import { tryToNumber } from '../../common/typescript'; import { TableRenderOptions } from '../columns/column-contribution-service'; import { DataColumn } from '../columns/data-column'; import type { HoverService } from '../hovers/hover-service'; -import { Decoration, isTrigger, MemoryDisplayConfiguration, ScrollingBehavior } from '../utils/view-types'; +import { Decoration, isTrigger } from '../utils/view-types'; import { createColumnVscodeContext, createSectionVscodeContext } from '../utils/vscode-contexts'; export interface MoreMemorySelectProps { @@ -128,7 +129,7 @@ export const MoreMemorySelect: React.FC; activeReadArguments: Required; memory?: Memory; diff --git a/src/webview/components/memory-widget.tsx b/src/webview/components/memory-widget.tsx index dcf09d4..285c097 100644 --- a/src/webview/components/memory-widget.tsx +++ b/src/webview/components/memory-widget.tsx @@ -17,16 +17,16 @@ import React from 'react'; import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; import { Memory } from '../../common/memory'; -import { WebviewSelection } from '../../common/messaging'; -import { MemoryOptions, ReadMemoryArguments, SessionContext } from '../../common/messaging'; +import { MemoryDataDisplaySettings } from '../../common/memory-display-settings'; +import { MemoryOptions, ReadMemoryArguments, SessionContext, WebviewSelection } from '../../common/messaging'; import { ColumnStatus } from '../columns/column-contribution-service'; import { HoverService } from '../hovers/hover-service'; -import { Decoration, MemoryDisplayConfiguration, MemoryState } from '../utils/view-types'; +import { Decoration, MemoryState } from '../utils/view-types'; 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; @@ -38,12 +38,13 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration { columns: ColumnStatus[]; effectiveAddressLength: number; isMemoryFetching: 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; + resetMemoryDisplaySettings: () => void; updateTitle: (title: string) => void; fetchMemory(partialOptions?: MemoryOptions): Promise; storeMemory(): void; @@ -91,11 +92,12 @@ export class MemoryWidget extends React.Component; activeReadArguments: Required; title: string; + settingsContributionMessage?: string; updateRenderOptions: (options: Partial) => void; resetRenderOptions: () => void; updateTitle: (title: string) => void; @@ -133,6 +134,8 @@ export class OptionsWidget extends React.Component { if (userValue !== memoryValue) { return Actual: {memoryValue}; @@ -278,14 +281,14 @@ export class OptionsWidget extends React.Component - +