Skip to content

Commit

Permalink
Enable extenders to overwrite default settings
Browse files Browse the repository at this point in the history
Extenders can use the `AdapterCapabilities` to specify their own default settings,
including the visible columns (see `MemoryDisplaySettings` interface). If an extension
specifies such settings for a specific debug session (or type), those settings take
priority over the user's VS Code settings of the Memory Inspector.

To indicate that default settings are overwritten by an extension, an additional
message is shown in the hover of the reset button in the options overlay. Extenders
can specify this message to give more specific reasons for overwriting the settings.

Users can prevent extenders from overwriting the default settings via a dedicated VS Code
setting (`allowSettingsExtension`).

Fixes #77
  • Loading branch information
planger committed Apr 29, 2024
1 parent 835ef75 commit cf71e6d
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 69 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
Expand Down
48 changes: 48 additions & 0 deletions src/common/memory-display-settings.ts
Original file line number Diff line number Diff line change
@@ -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<MemoryDisplaySettings>;
}
4 changes: 4 additions & 0 deletions src/plugin/adapter-registry/adapter-capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -30,6 +31,9 @@ export interface AdapterCapabilities {
getAddressOfVariable?(session: vscode.DebugSession, variableName: string): Promise<string | undefined>;
/** Resolves the size of a given variable in bytes within the current context. */
getSizeOfVariable?(session: vscode.DebugSession, variableName: string): Promise<bigint | undefined>;
/** Retrieve the enforced default display settings for the memory view. */
getMemoryDisplaySettings?(session: vscode.DebugSession): Promise<Partial<MemoryDisplaySettingsContribution>>;
/** Initialize the trackers of this adapter's for the debug session. */
initializeAdapterTracker?(session: vscode.DebugSession): vscode.DebugAdapterTracker | undefined;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plugin/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
8 changes: 8 additions & 0 deletions src/plugin/memory-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -183,4 +184,11 @@ export class MemoryProvider {
const handler = this.adapterRegistry?.getHandlerForSession(session.type);
return handler?.getSizeOfVariable?.(session, variableName);
}

public async getMemoryDisplaySettingsContribution(): Promise<MemoryDisplaySettingsContribution> {
const session = this.assertActiveSession('get settings of variable');
const handler = this.adapterRegistry?.getHandlerForSession(session.type);
return handler?.getMemoryDisplaySettings?.(session) ?? {};
}

}
67 changes: 44 additions & 23 deletions src/plugin/memory-webview-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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 => {
Expand All @@ -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 }),
Expand All @@ -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<void> {
this.messenger.sendRequest(setOptionsType, participant, options);
protected async setMemoryDisplaySettings(participant: WebviewIdMessageParticipant, title?: string): Promise<void> {
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<void> {
this.messenger.sendRequest(setOptionsType, participant, options);
}

protected setMemoryViewSettings(webviewParticipant: WebviewIdMessageParticipant, settings: Partial<MemoryViewSettings>): void {
this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, settings);
}

protected applyDisplaySettingContributions(webviewParticipant: WebviewIdMessageParticipant, settingsContribution: Partial<MemoryDisplaySettingsContribution>): 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<number>(manifest.CONFIG_BYTES_PER_MAU, manifest.DEFAULT_BYTES_PER_MAU);
const mausPerGroup = memoryInspectorConfiguration.get<number>(manifest.CONFIG_MAUS_PER_GROUP, manifest.DEFAULT_MAUS_PER_GROUP);
const groupsPerRow = memoryInspectorConfiguration.get<manifest.GroupsPerRowOption>(manifest.CONFIG_GROUPS_PER_ROW, manifest.DEFAULT_GROUPS_PER_ROW);
const endianness = memoryInspectorConfiguration.get<Endianness>(manifest.CONFIG_ENDIANNESS, manifest.DEFAULT_ENDIANNESS);
const scrollingBehavior = memoryInspectorConfiguration.get<ScrollingBehavior>(manifest.CONFIG_SCROLLING_BEHAVIOR, manifest.DEFAULT_SCROLLING_BEHAVIOR);
protected getDefaultMemoryDisplaySettings(): MemoryDisplaySettings {
const memoryInspectorSettings = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME);
const bytesPerMau = memoryInspectorSettings.get<number>(manifest.CONFIG_BYTES_PER_MAU, manifest.DEFAULT_BYTES_PER_MAU);
const mausPerGroup = memoryInspectorSettings.get<number>(manifest.CONFIG_MAUS_PER_GROUP, manifest.DEFAULT_MAUS_PER_GROUP);
const groupsPerRow = memoryInspectorSettings.get<manifest.GroupsPerRowOption>(manifest.CONFIG_GROUPS_PER_ROW, manifest.DEFAULT_GROUPS_PER_ROW);
const endianness = memoryInspectorSettings.get<Endianness>(manifest.CONFIG_ENDIANNESS, manifest.DEFAULT_ENDIANNESS);
const scrollingBehavior = memoryInspectorSettings.get<ScrollingBehavior>(manifest.CONFIG_SCROLLING_BEHAVIOR, manifest.DEFAULT_SCROLLING_BEHAVIOR);
const visibleColumns = CONFIGURABLE_COLUMNS
.filter(column => vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<boolean>(column, false))
.filter(column => memoryInspectorSettings.get<boolean>(column, false))
.map(columnId => columnId.replace('columns.', ''));
const addressPadding = AddressPaddingOptions[memoryInspectorConfiguration.get(manifest.CONFIG_ADDRESS_PADDING, manifest.DEFAULT_ADDRESS_PADDING)];
const addressRadix = memoryInspectorConfiguration.get<number>(manifest.CONFIG_ADDRESS_RADIX, manifest.DEFAULT_ADDRESS_RADIX);
const showRadixPrefix = memoryInspectorConfiguration.get<boolean>(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<number>(manifest.CONFIG_ADDRESS_RADIX, manifest.DEFAULT_ADDRESS_RADIX);
const showRadixPrefix = memoryInspectorSettings.get<boolean>(manifest.CONFIG_SHOW_RADIX_PREFIX, manifest.DEFAULT_SHOW_RADIX_PREFIX);
return { bytesPerMau, mausPerGroup, groupsPerRow, endianness, scrollingBehavior, visibleColumns, addressPadding, addressRadix, showRadixPrefix };
}

protected async getSettingsContribution(): Promise<MemoryDisplaySettingsContribution> {
const memoryInspectorSettings = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME);
const allowSettingsExtension = memoryInspectorSettings.get<boolean>(manifest.CONFIG_ALLOW_SETTINGS_EXTENSION, true);
if (allowSettingsExtension) {
return this.memoryProvider.getMemoryDisplaySettingsContribution();
}
return { settings: {}, message: '' };
}

protected async readMemory(request: ReadMemoryArguments): Promise<ReadMemoryResult> {
Expand Down
5 changes: 3 additions & 2 deletions src/webview/components/memory-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -128,7 +129,7 @@ export const MoreMemorySelect: React.FC<MoreMemoryAboveSelectProps | MoreMemoryB
);
};

interface MemoryTableProps extends TableRenderOptions, MemoryDisplayConfiguration {
interface MemoryTableProps extends TableRenderOptions, MemoryDataDisplaySettings {
configuredReadArguments: Required<ReadMemoryArguments>;
activeReadArguments: Required<ReadMemoryArguments>;
memory?: Memory;
Expand Down
18 changes: 10 additions & 8 deletions src/webview/components/memory-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReadMemoryArguments>;
Expand All @@ -38,12 +38,13 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration {
columns: ColumnStatus[];
effectiveAddressLength: number;
isMemoryFetching: boolean;
settingsContributionMessage?: string;
updateMemoryState: (state: Partial<MemoryState>) => void;
toggleColumn(id: string, active: boolean): void;
isFrozen: boolean;
toggleFrozen: () => void;
updateMemoryDisplayConfiguration: (memoryArguments: Partial<MemoryDisplayConfiguration>) => void;
resetMemoryDisplayConfiguration: () => void;
updateMemoryDisplaySettings: (memoryArguments: Partial<MemoryDataDisplaySettings>) => void;
resetMemoryDisplaySettings: () => void;
updateTitle: (title: string) => void;
fetchMemory(partialOptions?: MemoryOptions): Promise<void>;
storeMemory(): void;
Expand Down Expand Up @@ -91,11 +92,12 @@ export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidge
mausPerGroup={this.props.mausPerGroup}
groupsPerRow={this.props.groupsPerRow}
updateMemoryState={this.props.updateMemoryState}
updateRenderOptions={this.props.updateMemoryDisplayConfiguration}
resetRenderOptions={this.props.resetMemoryDisplayConfiguration}
updateRenderOptions={this.props.updateMemoryDisplaySettings}
resetRenderOptions={this.props.resetMemoryDisplaySettings}
addressPadding={this.props.addressPadding}
addressRadix={this.props.addressRadix}
showRadixPrefix={this.props.showRadixPrefix}
settingsContributionMessage={this.props.settingsContributionMessage}
fetchMemory={this.props.fetchMemory}
toggleColumn={this.props.toggleColumn}
toggleFrozen={this.props.toggleFrozen}
Expand Down
Loading

0 comments on commit cf71e6d

Please sign in to comment.