diff --git a/src/common/messaging.ts b/src/common/messaging.ts index 9b23854..48fdcde 100644 --- a/src/common/messaging.ts +++ b/src/common/messaging.ts @@ -38,6 +38,11 @@ export type StoreMemoryResult = void; export type ApplyMemoryArguments = URI | undefined; export type ApplyMemoryResult = MemoryOptions; +export interface Context { + name: string; + id: number; +} + export interface SessionContext { sessionId?: string; canRead: boolean; @@ -51,16 +56,16 @@ export const setMemoryViewSettingsType: NotificationType = { method: 'resetMemoryViewSettings' }; export const setTitleType: NotificationType = { method: 'setTitle' }; export const memoryWrittenType: NotificationType = { method: 'memoryWritten' }; -export const sessionContextChangedType: NotificationType = { method: 'sessionContextChanged' }; +export const sessionContextChangedType: NotificationType<[SessionContext, Context?, Context[]?]> = { method: 'sessionContextChanged' }; // Requests export const setOptionsType: RequestType = { method: 'setOptions' }; export const logMessageType: RequestType = { method: 'logMessage' }; -export const readMemoryType: RequestType = { method: 'readMemory' }; -export const writeMemoryType: RequestType = { method: 'writeMemory' }; -export const getVariablesType: RequestType = { method: 'getVariables' }; -export const storeMemoryType: RequestType = { method: 'storeMemory' }; -export const applyMemoryType: RequestType = { method: 'applyMemory' }; +export const readMemoryType: RequestType<[ReadMemoryArguments, Context?], ReadMemoryResult> = { method: 'readMemory' }; +export const writeMemoryType: RequestType<[WriteMemoryArguments, Context?], WriteMemoryResult> = { method: 'writeMemory' }; +export const getVariablesType: RequestType<[ReadMemoryArguments, Context?], VariableRange[]> = { method: 'getVariables' }; +export const storeMemoryType: RequestType<[StoreMemoryArguments, Context?], void> = { method: 'storeMemory' }; +export const applyMemoryType: RequestType<[ApplyMemoryArguments, Context?], ApplyMemoryResult> = { method: 'applyMemory' }; export const showAdvancedOptionsType: NotificationType = { method: 'showAdvancedOptions' }; export const getWebviewSelectionType: RequestType = { method: 'getWebviewSelection' }; diff --git a/src/plugin/adapter-registry/adapter-capabilities.ts b/src/plugin/adapter-registry/adapter-capabilities.ts index 4ad03d9..bc51355 100644 --- a/src/plugin/adapter-registry/adapter-capabilities.ts +++ b/src/plugin/adapter-registry/adapter-capabilities.ts @@ -18,22 +18,24 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import * as vscode from 'vscode'; import { isDebugRequest, isDebugResponse } from '../../common/debug-requests'; import { VariableRange } from '../../common/memory-range'; -import { ReadMemoryArguments, ReadMemoryResult, WriteMemoryArguments, WriteMemoryResult } from '../../common/messaging'; +import { Context, ReadMemoryArguments, ReadMemoryResult, WriteMemoryArguments, WriteMemoryResult } from '../../common/messaging'; import { Logger } from '../logger'; /** Represents capabilities that may be achieved with particular debug adapters but are not part of the DAP */ export interface AdapterCapabilities { /** Resolve variables known to the adapter to their locations. Fallback if {@link getResidents} is not present */ - getVariables?(session: vscode.DebugSession): Promise; + getVariables?(session: vscode.DebugSession, context?: Context): Promise; /** Resolve symbols resident in the memory at the specified range. Will be preferred to {@link getVariables} if present. */ - getResidents?(session: vscode.DebugSession, params: DebugProtocol.ReadMemoryArguments): Promise; + getResidents?(session: vscode.DebugSession, params: DebugProtocol.ReadMemoryArguments, context?: Context): Promise; /** Resolves the address of a given variable in bytes with the current context. */ - getAddressOfVariable?(session: vscode.DebugSession, variableName: string): Promise; + getAddressOfVariable?(session: vscode.DebugSession, variableName: string, context?: Context): Promise; /** Resolves the size of a given variable in bytes within the current context. */ - getSizeOfVariable?(session: vscode.DebugSession, variableName: string): Promise; + getSizeOfVariable?(session: vscode.DebugSession, variableName: string, context?: Context): Promise; initializeAdapterTracker?(session: vscode.DebugSession): vscode.DebugAdapterTracker | undefined; - readMemory?(session: vscode.DebugSession, params: ReadMemoryArguments): Promise; - writeMemory?(session: vscode.DebugSession, params: WriteMemoryArguments): Promise; + readMemory?(session: vscode.DebugSession, params: ReadMemoryArguments, context?: Context): Promise; + writeMemory?(session: vscode.DebugSession, params: WriteMemoryArguments, context?: Context): Promise; + getContexts?(session: vscode.DebugSession): Promise; + getCurrentContext?(session: vscode.DebugSession): Promise; } export type WithChildren = Original & { children?: Array> }; @@ -104,14 +106,14 @@ export class AdapterVariableTracker implements vscode.DebugAdapterTracker { this.pendingMessages.clear(); } - async getLocals(session: vscode.DebugSession): Promise { + async getLocals(session: vscode.DebugSession, context?: Context): Promise { this.logger.debug('Retrieving local variables in', session.name + ' Current variables:\n', this.variablesTree); if (this.currentFrame === undefined) { return []; } const maybeRanges = await Promise.all(Object.values(this.variablesTree).reduce>>((previous, parent) => { if (this.isDesiredVariable(parent) && parent.children?.length) { this.logger.debug('Resolving children of', parent.name); parent.children.forEach(child => { - previous.push(this.variableToVariableRange(child, session)); + previous.push(this.variableToVariableRange(child, session, context)); }); } else { this.logger.debug('Ignoring', parent.name); @@ -125,19 +127,22 @@ export class AdapterVariableTracker implements vscode.DebugAdapterTracker { return candidate.presentationHint !== 'registers' && candidate.name !== 'Registers'; } - protected variableToVariableRange(_variable: DebugProtocol.Variable, _session: vscode.DebugSession): Promise { + protected variableToVariableRange(_variable: DebugProtocol.Variable, _session: vscode.DebugSession, _context?: Context): Promise { throw new Error('To be implemented by derived classes!'); } /** Resolves the address of a given variable in bytes within the current context. */ - getAddressOfVariable?(variableName: string, session: vscode.DebugSession): Promise; + getAddressOfVariable?(variableName: string, session: vscode.DebugSession, context?: Context): Promise; /** Resolves the size of a given variable in bytes within the current context. */ - getSizeOfVariable?(variableName: string, session: vscode.DebugSession): Promise; + getSizeOfVariable?(variableName: string, session: vscode.DebugSession, context?: Context): Promise; - readMemory?(session: vscode.DebugSession, params: ReadMemoryArguments): Promise; + readMemory?(session: vscode.DebugSession, params: ReadMemoryArguments, context?: Context): Promise; - writeMemory?(session: vscode.DebugSession, params: WriteMemoryArguments): Promise; + writeMemory?(session: vscode.DebugSession, params: WriteMemoryArguments, context?: Context): Promise; + + getContexts?(session: vscode.DebugSession): Promise; + getCurrentContext?(session: vscode.DebugSession): Promise; } export class VariableTracker implements AdapterCapabilities { @@ -159,15 +164,15 @@ export class VariableTracker implements AdapterCapabilities { } } - async getVariables(session: vscode.DebugSession): Promise { - return this.sessions.get(session.id)?.getLocals(session) ?? []; + async getVariables(session: vscode.DebugSession, context?: Context): Promise { + return this.sessions.get(session.id)?.getLocals(session, context) ?? []; } - async getAddressOfVariable(session: vscode.DebugSession, variableName: string): Promise { - return this.sessions.get(session.id)?.getAddressOfVariable?.(variableName, session); + async getAddressOfVariable(session: vscode.DebugSession, variableName: string, context?: Context): Promise { + return this.sessions.get(session.id)?.getAddressOfVariable?.(variableName, session, context); } - async getSizeOfVariable(session: vscode.DebugSession, variableName: string): Promise { - return this.sessions.get(session.id)?.getSizeOfVariable?.(variableName, session); + async getSizeOfVariable(session: vscode.DebugSession, variableName: string, context?: Context): Promise { + return this.sessions.get(session.id)?.getSizeOfVariable?.(variableName, session, context); } } diff --git a/src/plugin/adapter-registry/amalgamator-gdb-tracker.ts b/src/plugin/adapter-registry/amalgamator-gdb-tracker.ts new file mode 100644 index 0000000..efc31b9 --- /dev/null +++ b/src/plugin/adapter-registry/amalgamator-gdb-tracker.ts @@ -0,0 +1,110 @@ +/******************************************************************************** + * Copyright (C) 2024 Ericsson, Arm 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 { DebugProtocol } from '@vscode/debugprotocol'; +import * as vscode from 'vscode'; +import { Context, ReadMemoryArguments, ReadMemoryResult, WriteMemoryArguments, WriteMemoryResult } from '../../common/messaging'; +import { AdapterCapabilities, AdapterVariableTracker, VariableTracker } from './adapter-capabilities'; + +// Copied from cdt-amalgamator [AmalgamatorSession.d.ts] file +/** + * Response for our custom 'cdt-amalgamator/getChildDaps' request. + */ +export interface Contexts { + children?: Context[]; +} +export interface GetContextsResponse extends DebugProtocol.Response { + body: Contexts; +} +export type GetContextsResult = GetContextsResponse['body']; + +export interface AmalgamatorReadArgs extends ReadMemoryArguments { + child: Context; +} + +export class AmalgamatorSessionManager extends VariableTracker implements AdapterCapabilities { + async getContexts(session: vscode.DebugSession): Promise { + return this.sessions.get(session.id)?.getContexts?.(session) || []; + } + + async readMemory(session: vscode.DebugSession, args: ReadMemoryArguments, context: Context): Promise { + if (!context) { + vscode.window.showErrorMessage('Invalid context for Amalgamator. Select Context in Dropdown'); + return { + address: args.memoryReference + }; + } + return this.sessions.get(session.id)?.readMemory?.(session, args, context); + } + + async writeMemory(session: vscode.DebugSession, args: WriteMemoryArguments, context: Context): Promise { + return this.sessions.get(session.id)?.writeMemory?.(session, args, context); + } + + async getCurrentContext(session: vscode.DebugSession): Promise { + return this.sessions.get(session.id)?.getCurrentContext?.(session); + } +} + +export class AmalgamatorGdbVariableTransformer extends AdapterVariableTracker { + protected contexts?: Context[]; + protected currentContext?: Context; + + onWillReceiveMessage(message: unknown): void { + if (isStacktraceRequest(message)) { + if (typeof(message.arguments.threadId) !== 'undefined') { + this.currentContext = { + id: message.arguments.threadId, + name: message.arguments.threadId.toString() + }; + } else { + this.logger.warn('Invalid ThreadID in stackTrace'); + this.currentContext = undefined; + } + } else { + super.onWillReceiveMessage(message); + } + } + + get frame(): number | undefined { return this.currentFrame; } + + async getContexts(session: vscode.DebugSession): Promise { + if (!this.contexts) { + const contexts: GetContextsResult = (await session.customRequest('cdt-amalgamator/getChildDaps')); + this.contexts = contexts.children?.map(({ name, id }) => ({ name, id })) ?? []; + } + return Promise.resolve(this.contexts); + } + + async getCurrentContext(_session: vscode.DebugSession): Promise { + return Promise.resolve(this.currentContext); + } + + readMemory(session: vscode.DebugSession, args: ReadMemoryArguments, context: Context): Promise { + const amalReadArgs: AmalgamatorReadArgs = { ...args, child: context }; + return Promise.resolve(session.customRequest('cdt-amalgamator/readMemory', amalReadArgs)); + } +} + +export function isStacktraceRequest(message: unknown): message is DebugProtocol.StackTraceRequest { + const candidate = message as DebugProtocol.StackTraceRequest; + return !!candidate && candidate.command === 'stackTrace'; +} + +export function isStacktraceResponse(message: unknown): message is DebugProtocol.StackTraceResponse { + const candidate = message as DebugProtocol.StackTraceResponse; + return !!candidate && candidate.command === 'stackTrace' && Array.isArray(candidate.body.stackFrames); +} diff --git a/src/plugin/memory-provider.ts b/src/plugin/memory-provider.ts index 2fd77ef..24a3c3f 100644 --- a/src/plugin/memory-provider.ts +++ b/src/plugin/memory-provider.ts @@ -19,7 +19,7 @@ import * as vscode from 'vscode'; import { sendRequest } from '../common/debug-requests'; import { stringToBytesMemory } from '../common/memory'; import { VariableRange } from '../common/memory-range'; -import { ReadMemoryResult, WriteMemoryResult } from '../common/messaging'; +import { Context, ReadMemoryResult, WriteMemoryResult } from '../common/messaging'; import { AdapterRegistry } from './adapter-registry/adapter-registry'; import { isSessionEvent, SessionTracker } from './session-tracker'; @@ -47,14 +47,14 @@ export class MemoryProvider { context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('*', { createDebugAdapterTracker })); } - public async readMemory(args: DebugProtocol.ReadMemoryArguments): Promise { + public async readMemory(args: DebugProtocol.ReadMemoryArguments, context?: Context): Promise { const session = this.sessionTracker.assertDebugCapability(this.sessionTracker.activeSession, 'supportsReadMemoryRequest', 'read memory'); const handler = this.adapterRegistry?.getHandlerForSession(session.type); - if (handler?.readMemory) { return handler.readMemory(session, args); } + if (handler?.readMemory) { return handler.readMemory(session, args, context); } return sendRequest(session, 'readMemory', args); } - public async writeMemory(args: DebugProtocol.WriteMemoryArguments): Promise { + public async writeMemory(args: DebugProtocol.WriteMemoryArguments, context?: Context): Promise { const session = this.sessionTracker.assertDebugCapability(this.sessionTracker.activeSession, 'supportsWriteMemoryRequest', 'write memory'); // Schedule a emit in case we don't retrieve a memory event this.scheduledOnDidMemoryWriteEvents[session.id + '_' + args.memoryReference] = response => { @@ -67,7 +67,7 @@ export class MemoryProvider { }; const handler = this.adapterRegistry?.getHandlerForSession(session.type); if (handler?.writeMemory) { - return handler.writeMemory(session, args).then(response => { + return handler.writeMemory(session, args, context).then(response => { // The memory event is handled before we got here, if the scheduled event still exists, we need to handle it this.scheduledOnDidMemoryWriteEvents[args.memoryReference]?.(response); return response; @@ -81,22 +81,34 @@ export class MemoryProvider { }); } - public async getVariables(variableArguments: DebugProtocol.ReadMemoryArguments): Promise { + public async getVariables(variableArguments: DebugProtocol.ReadMemoryArguments, context?: Context): Promise { const session = this.sessionTracker.assertActiveSession('get variables'); const handler = this.adapterRegistry?.getHandlerForSession(session.type); - if (handler?.getResidents) { return handler.getResidents(session, variableArguments); } - return handler?.getVariables?.(session) ?? []; + if (handler?.getResidents) { return handler.getResidents(session, variableArguments, context); } + return handler?.getVariables?.(session, context) ?? []; } - public async getAddressOfVariable(variableName: string): Promise { + public async getAddressOfVariable(variableName: string, context?: Context): Promise { const session = this.sessionTracker.assertActiveSession('get address of variable'); const handler = this.adapterRegistry?.getHandlerForSession(session.type); - return handler?.getAddressOfVariable?.(session, variableName); + return handler?.getAddressOfVariable?.(session, variableName, context); } - public async getSizeOfVariable(variableName: string): Promise { + public async getSizeOfVariable(variableName: string, context?: Context): Promise { const session = this.sessionTracker.assertActiveSession('get address of variable'); const handler = this.adapterRegistry?.getHandlerForSession(session.type); - return handler?.getSizeOfVariable?.(session, variableName); + return handler?.getSizeOfVariable?.(session, variableName, context); + } + + public async getContexts(): Promise { + const session = this.sessionTracker.assertActiveSession('get list of debug Contexts'); + const handler = this.adapterRegistry?.getHandlerForSession(session.type); + return handler?.getContexts?.(session) ?? []; + } + + public async getCurrentContext(): Promise { + const session = this.sessionTracker.assertActiveSession('get current debug Context'); + const handler = this.adapterRegistry?.getHandlerForSession(session.type); + return handler?.getCurrentContext?.(session); } } diff --git a/src/plugin/memory-storage.ts b/src/plugin/memory-storage.ts index 8604036..369f0e9 100644 --- a/src/plugin/memory-storage.ts +++ b/src/plugin/memory-storage.ts @@ -24,7 +24,7 @@ import { validateCount, validateMemoryReference, validateOffset } from '../common/memory'; import { toHexStringWithRadixMarker } from '../common/memory-range'; -import { ApplyMemoryArguments, ApplyMemoryResult, MemoryOptions, StoreMemoryArguments } from '../common/messaging'; +import { ApplyMemoryArguments, ApplyMemoryResult, Context, MemoryOptions, StoreMemoryArguments } from '../common/messaging'; import { isWebviewContext } from '../common/webview-context'; import { isVariablesContext } from './external-views'; import { MemoryProvider } from './memory-provider'; @@ -60,7 +60,8 @@ export class MemoryStorage { ); } - public async storeMemory(args?: StoreMemoryArguments): Promise { + public async storeMemory(storeMemArgs?: [StoreMemoryArguments, Context?]): Promise { + const [args, context] = storeMemArgs ?? []; const providedDefaultOptions = await this.storeArgsToOptions(args); const options = await this.getStoreMemoryOptions(providedDefaultOptions); if (!options) { @@ -70,7 +71,7 @@ export class MemoryStorage { const { outputFile, ...readArgs } = options; try { - const memoryResponse = await this.memoryProvider.readMemory(readArgs); + const memoryResponse = await this.memoryProvider.readMemory(readArgs, context); const memory = createMemoryFromRead(memoryResponse); const memoryMap = new MemoryMap({ [Number(memory.address)]: memory.bytes }); await vscode.workspace.fs.writeFile(outputFile, new TextEncoder().encode(memoryMap.asHexString())); @@ -89,7 +90,7 @@ export class MemoryStorage { } } - protected async storeArgsToOptions(args?: StoreMemoryArguments): Promise> { + protected async storeArgsToOptions(args?: StoreMemoryArguments, context?: Context): Promise> { if (!args) { return {}; } @@ -99,8 +100,8 @@ export class MemoryStorage { if (isVariablesContext(args)) { try { const variableName = args.variable.evaluateName ?? args.variable.name; - const count = await this.memoryProvider.getSizeOfVariable(variableName); - const memoryReference = args.variable.memoryReference ?? await this.memoryProvider.getAddressOfVariable(variableName); + const count = await this.memoryProvider.getSizeOfVariable(variableName, context); + const memoryReference = args.variable.memoryReference ?? await this.memoryProvider.getAddressOfVariable(variableName, context); return { count: Number(count), memoryReference, offset: 0, proposedOutputName: variableName }; } catch (error) { // ignore, we are just using them as default values @@ -169,7 +170,7 @@ export class MemoryStorage { memoryReference = toHexStringWithRadixMarker(address); count = memory.length; const data = bytesToStringMemory(memory); - await this.memoryProvider.writeMemory({ memoryReference, data }); + await this.memoryProvider.writeMemory({ memoryReference, data }, undefined); } await vscode.window.showInformationMessage(`Memory from '${vscode.workspace.asRelativePath(options.uri)}' applied.`); return { memoryReference, count, offset: 0 }; diff --git a/src/plugin/memory-webview-main.ts b/src/plugin/memory-webview-main.ts index e94771a..f39e1c3 100644 --- a/src/plugin/memory-webview-main.ts +++ b/src/plugin/memory-webview-main.ts @@ -21,6 +21,7 @@ import * as manifest from '../common/manifest'; import { VariableRange } from '../common/memory-range'; import { applyMemoryType, + Context, getVariablesType, getWebviewSelectionType, logMessageType, @@ -57,6 +58,9 @@ const CONFIGURABLE_COLUMNS = [ manifest.CONFIG_SHOW_VARIABLES_COLUMN, ]; +type ReadMemoryWithContext = [ReadMemoryArguments, Context?]; +type WriteMemoryWithContext = [WriteMemoryArguments, Context?]; + export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { public static ViewType = `${manifest.PACKAGE_NAME}.memory`; public static ShowCommandType = `${manifest.PACKAGE_NAME}.show`; @@ -204,13 +208,21 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.messenger.onRequest(applyMemoryType, () => this.applyMemory(), { sender: participant }), this.sessionTracker.onSessionEvent(event => this.handleSessionEvent(participant, event)) ]; + let panelHasBeenVisible = false; + panel.onDidChangeViewState(newState => { + // Skip on first appearance. + if (panelHasBeenVisible && newState.webviewPanel.visible) { + this.refresh(participant, options); + } + panelHasBeenVisible ||= newState.webviewPanel.visible; + }); 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); + await this.setSessionContext(participant, this.createContext()); + await this.refresh(participant, options); } protected async refresh(participant: WebviewIdMessageParticipant, options: MemoryOptions = {}): Promise { @@ -225,8 +237,10 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.messenger.sendNotification(setMemoryViewSettingsType, webviewParticipant, settings); } - protected setSessionContext(webviewParticipant: WebviewIdMessageParticipant, context: SessionContext): void { - this.messenger.sendNotification(sessionContextChangedType, webviewParticipant, context); + protected async setSessionContext(webviewParticipant: WebviewIdMessageParticipant, context: SessionContext): Promise { + const contexts = await this.getContexts(); // Read available Debug Contexts first. + await this.messenger.sendRequest(sessionContextChangedType, webviewParticipant, [context, + await this.getCurrentContext(), contexts]); } protected getMemoryViewSettings(messageParticipant: WebviewIdMessageParticipant, title: string): MemoryViewSettings { @@ -278,25 +292,28 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { }; } - protected async readMemory(request: ReadMemoryArguments): Promise { + protected async readMemory(request: ReadMemoryWithContext): Promise { try { - return await this.memoryProvider.readMemory(request); + const [readMemoryArgs, context] = request; + return await this.memoryProvider.readMemory(readMemoryArgs, context); } catch (err) { this.logError('Error fetching memory', err); } } - protected async writeMemory(request: WriteMemoryArguments): Promise { + protected async writeMemory(request: WriteMemoryWithContext): Promise { try { - return await this.memoryProvider.writeMemory(request); + const [writeMemoryArgs, context] = request; + return await this.memoryProvider.writeMemory(writeMemoryArgs, context); } catch (err) { this.logError('Error writing memory', err); } } - protected async getVariables(request: ReadMemoryArguments): Promise { + protected async getVariables(request: ReadMemoryWithContext): Promise { try { - return await this.memoryProvider.getVariables(request); + const [readMemoryArgs, context] = request; + return await this.memoryProvider.getVariables(readMemoryArgs, context); } catch (err) { this.logError('Error fetching variables', err); return []; @@ -319,7 +336,7 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { this.setMemoryViewSettings(ctx.messageParticipant, { visibleColumns }); } - protected async storeMemory(storeArguments: StoreMemoryArguments): Promise { + protected async storeMemory(storeArguments: [StoreMemoryArguments, Context?]): 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 this.sessionTracker.assertDebugCapability(this.sessionTracker.activeSession, 'supportsReadMemoryRequest', 'store memory'); @@ -336,4 +353,22 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider { protected logError(msg: string, err: unknown): void { outputChannelLogger.error(msg, err instanceof Error ? `: ${err.message}\n${err.stack}` : ''); } + + protected async getContexts(): Promise { + try { + return await this.memoryProvider.getContexts(); + } catch (err) { + this.logError('Error getting Contexts', err); + } + return []; + } + + protected async getCurrentContext(): Promise { + try { + return await this.memoryProvider.getCurrentContext(); + } catch (err) { + this.logError('Error getting Current Context', err); + } + return undefined; + } } diff --git a/src/webview/columns/data-column.tsx b/src/webview/columns/data-column.tsx index c6a8b59..892f45d 100644 --- a/src/webview/columns/data-column.tsx +++ b/src/webview/columns/data-column.tsx @@ -201,10 +201,10 @@ export class EditableDataColumnRow extends React.Component { }); + }, undefined]).catch(() => { }); } this.disableEdit(); diff --git a/src/webview/components/memory-widget.tsx b/src/webview/components/memory-widget.tsx index 8accd2c..12f702a 100644 --- a/src/webview/components/memory-widget.tsx +++ b/src/webview/components/memory-widget.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { WebviewIdMessageParticipant } from 'vscode-messenger-common'; import { Memory } from '../../common/memory'; -import { WebviewSelection } from '../../common/messaging'; +import { Context, WebviewSelection } from '../../common/messaging'; import { MemoryOptions, ReadMemoryArguments, SessionContext } from '../../common/messaging'; import { MemoryDisplayConfiguration } from '../../common/webview-configuration'; import { ColumnStatus } from '../columns/column-contribution-service'; @@ -49,6 +49,9 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration { fetchMemory(partialOptions?: MemoryOptions): Promise; storeMemory(): void; applyMemory(): void; + contexts: Context[]; + context?: Context; + setContext: (context: Context) => void; } interface MemoryWidgetState { @@ -106,6 +109,9 @@ export class MemoryWidget extends React.Component void; } interface OptionsWidgetState { @@ -69,6 +72,7 @@ const enum InputId { RefreshOnStop = 'refresh-on-stop', PeriodicRefresh = 'periodic-refresh', PeriodicRefreshInterval = 'periodic-refresh-interval', + Contexts = 'debug-contexts' } interface OptionsForm { @@ -133,6 +137,30 @@ export class OptionsWidget extends React.Component { + const { setContext, contexts } = this.props; + setContext(contexts.filter(context => context.id === Number(e.value))[0]); + }; + + protected showContexts = () => { + if (this.props.contexts.length === 0) { + return undefined; + } + return ( + + + + ); + }; + override render(): React.ReactNode { this.formConfig.initialValues = this.optionsFormValues; const isLabelEditing = this.state.isTitleEditing; @@ -211,6 +239,7 @@ export class OptionsWidget extends React.Component {formik => (
+ {this.showContexts()}