diff --git a/src/common/messaging.ts b/src/common/messaging.ts index 030fc97..ef67ab8 100644 --- a/src/common/messaging.ts +++ b/src/common/messaging.ts @@ -37,12 +37,19 @@ export type StoreMemoryResult = void; export type ApplyMemoryArguments = URI | undefined; export type ApplyMemoryResult = MemoryOptions; +export interface SessionContext { + sessionId?: string; + canRead: boolean; + canWrite: boolean; +} + // 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' }; // Requests export const setOptionsType: RequestType = { method: 'setOptions' }; diff --git a/src/plugin/memory-provider.ts b/src/plugin/memory-provider.ts index f6c4f34..690cbae 100644 --- a/src/plugin/memory-provider.ts +++ b/src/plugin/memory-provider.ts @@ -17,7 +17,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import * as vscode from 'vscode'; import { VariableRange, WrittenMemory } from '../common/memory-range'; -import { ReadMemoryResult, WriteMemoryResult } from '../common/messaging'; +import { ReadMemoryResult, SessionContext, WriteMemoryResult } from '../common/messaging'; import { AdapterRegistry } from './adapter-registry/adapter-registry'; import * as manifest from './manifest'; import { sendRequest } from '../common/debug-requests'; @@ -42,11 +42,19 @@ export class MemoryProvider { private _onDidWriteMemory = new vscode.EventEmitter(); public readonly onDidWriteMemory = this._onDidWriteMemory.event; + private _sessionContext: SessionContext = { canRead: false, canWrite: false }; + private _onDidChangeSessionContext = new vscode.EventEmitter(); + public readonly onDidChangeSessionContext = this._onDidChangeSessionContext.event; + protected readonly sessions = new Map(); constructor(protected adapterRegistry: AdapterRegistry) { } + get sessionContext(): SessionContext { + return this._sessionContext; + } + public activate(context: vscode.ExtensionContext): void { const createDebugAdapterTracker = (session: vscode.DebugSession): Required => { const handlerForSession = this.adapterRegistry.getHandlerForSession(session.type); @@ -66,7 +74,7 @@ export class MemoryProvider { // Check for right capabilities in the adapter this.sessions.set(session.id, message.body); if (vscode.debug.activeDebugSession?.id === session.id) { - this.setContext(message.body); + this.setContext(session); } } if (isStoppedEvent(message)) { @@ -82,10 +90,7 @@ export class MemoryProvider { context.subscriptions.push( vscode.debug.registerDebugAdapterTrackerFactory('*', { createDebugAdapterTracker }), - vscode.debug.onDidChangeActiveDebugSession(session => { - const capabilities = session && this.sessions.get(session.id); - this.setContext(capabilities); - }) + vscode.debug.onDidChangeActiveDebugSession(session => this.setContext(session)) ); } @@ -97,9 +102,16 @@ export class MemoryProvider { this.sessions.delete(session.id); } - protected setContext(capabilities?: DebugProtocol.Capabilities): void { - vscode.commands.executeCommand('setContext', MemoryProvider.ReadKey, !!capabilities?.supportsReadMemoryRequest); - vscode.commands.executeCommand('setContext', MemoryProvider.WriteKey, !!capabilities?.supportsWriteMemoryRequest); + protected setContext(session?: vscode.DebugSession): void { + const capabilities = session && this.sessions.get(session.id); + this._sessionContext = { + sessionId: session?.id, + canRead: !!capabilities?.supportsReadMemoryRequest, + canWrite: !!capabilities?.supportsWriteMemoryRequest + }; + vscode.commands.executeCommand('setContext', MemoryProvider.ReadKey, this.sessionContext.canRead); + vscode.commands.executeCommand('setContext', MemoryProvider.WriteKey, this.sessionContext.canWrite); + this._onDidChangeSessionContext.fire(this.sessionContext); } /** Returns the session if the capability is present, otherwise throws. */ diff --git a/src/plugin/memory-webview-main.ts b/src/plugin/memory-webview-main.ts index ab7be36..9153227 100644 --- a/src/plugin/memory-webview-main.ts +++ b/src/plugin/memory-webview-main.ts @@ -22,6 +22,7 @@ import { MemoryOptions, ReadMemoryArguments, ReadMemoryResult, + SessionContext, StoreMemoryArguments, WriteMemoryArguments, WriteMemoryResult, @@ -32,6 +33,7 @@ import { readMemoryType, readyType, resetMemoryViewSettingsType, + sessionContextChangedType, setMemoryViewSettingsType, setOptionsType, setTitleType, @@ -182,6 +184,7 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { const disposables = [ this.messenger.onNotification(readyType, () => { this.setInitialSettings(participant, panel.title); + this.setSessionContext(participant, this.memoryProvider.sessionContext); this.refresh(participant, options); }, { sender: participant }), this.messenger.onRequest(setOptionsType, o => { @@ -201,6 +204,7 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.refresh(participant); } }), + this.memoryProvider.onDidChangeSessionContext(context => this.setSessionContext(participant, context)), this.memoryProvider.onDidWriteMemory(writtenMemory => this.messenger.sendNotification(memoryWrittenType, participant, writtenMemory)) ]; panel.onDidChangeViewState(newState => { @@ -219,6 +223,10 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, this.getMemoryViewSettings(title)); } + protected setSessionContext(webviewParticipant: WebviewIdMessageParticipant, context: SessionContext): void { + this.messenger.sendNotification(sessionContextChangedType, webviewParticipant, context); + } + protected getMemoryViewSettings(title: string): MemoryViewSettings { const memoryInspectorConfiguration = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME); const bytesPerWord = memoryInspectorConfiguration.get(manifest.CONFIG_BYTES_PER_WORD, manifest.DEFAULT_BYTES_PER_WORD); @@ -261,10 +269,20 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { } protected async storeMemory(storeArguments: StoreMemoryArguments): Promise { + // Even if we disable the command in VS Code through enablement or when condition, programmatic execution is still possible. + // However, we want to fail early in case the user tries to execute a disabled command + if (!this.memoryProvider.sessionContext.canRead) { + throw new Error('Cannot read memory, no valid debug session.'); + } return vscode.commands.executeCommand(StoreCommandType, storeArguments); } protected async applyMemory(): Promise { + // Even if we disable the command in VS Code through enablement or when condition, programmatic execution is still possible. + // However, we want to fail early in case the user tries to execute a disabled command + if (!this.memoryProvider.sessionContext.canWrite) { + throw new Error('Cannot write memory, no valid debug session.'); + } return vscode.commands.executeCommand(ApplyCommandType); } diff --git a/src/webview/components/memory-widget.tsx b/src/webview/components/memory-widget.tsx index aef1c41..cb24e57 100644 --- a/src/webview/components/memory-widget.tsx +++ b/src/webview/components/memory-widget.tsx @@ -19,10 +19,11 @@ import { ColumnStatus } from '../columns/column-contribution-service'; import { Decoration, MemoryDisplayConfiguration, MemoryState } from '../utils/view-types'; import { MemoryTable } from './memory-table'; import { OptionsWidget } from './options-widget'; -import { MemoryOptions, ReadMemoryArguments } from '../../common/messaging'; +import { MemoryOptions, ReadMemoryArguments, SessionContext } from '../../common/messaging'; import { Memory } from '../../common/memory'; interface MemoryWidgetProps extends MemoryDisplayConfiguration { + sessionContext: SessionContext; configuredReadArguments: Required; activeReadArguments: Required; memory?: Memory; @@ -58,6 +59,7 @@ export class MemoryWidget extends React.Component { + sessionContext: SessionContext; configuredReadArguments: Required; activeReadArguments: Required; title: string; @@ -126,6 +127,7 @@ export class OptionsWidget extends React.Component { if (userValue !== memoryValue) { @@ -173,6 +175,7 @@ export class OptionsWidget extends React.Component (
-