Repository: vscode
Issue: Investigate debug visualization extension points
Labels: debug,under-discussion,api-proposal

VS proper has some data visualization like this:

<img width="620" alt="image" src="https://github.com/microsoft/vscode/assets/2230985/86649bc2-f128-4d19-80a5-364f264483ea">

...and personally I've never been a fan of how we represent things like XML elements or binary data in debug hovers and the REPL. In large part this is because we have to have a 'generic' view that gives all possible information, so we can't be more concise when showing things like DOM tree.

So it would be nice to have a way for visualizations to be contributed, with similar affordances to what VS provides.

- These **should** be contributable by extensions
- These **should not** be custom webviews. At least not immediately, I have no desired to re-implement the layering of notebooks' webviews in the debug REPL. (Though perhaps we could allow webviews in the hover and as popouts in the console...)
- This **does not** affect DAP in any way. This is a UI extension, not a protocol extension.
- This **should** allow the hex editor to move to using the generic APIs and away from the custom handling we currently have.

My initial thought at an API is something like this. Note that we don't bring in DAP types in vscode.d.ts, so they would be typed as `unknown` and cast by the extension author.

```ts

/** Contribution point in package.json `debugVisualizers` */
type DebugVisualizerContributionPoint = Array<{
  // Corresponding ID in the `registerDebugVisualizationProvider`
  id: string;
  // `when` clause that determines when the visualizer may be shown
  when: string;
}>

declare module 'vscode' {
	export namespace debug {
		/**
		 * Registers a custom data visualization for variables when debugging.
		 *
		 * @param id The corresponding ID in the package.json `debugVisualizers` contribution point.
		 * @param provider The {@link DebugVisualizationProvider} to register
		 */
		export function registerDebugVisualizationProvider<T extends DebugVisualization>(
			id: string,
			provider: DebugVisualizationProvider<T>
		): Disposable;
	}

	export class DebugVisualization {
		/**
		 * The name of the visualization to show to the user.
		 */
		name: string;

		/**
		 * An icon for the view when it's show in inline actions.
		 */
		iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;

		/**
		 * Visualization to use for the variable. This may be either:
		 * - A command to run when the visualization is selected for a variable.
		 * - A {@link TreeDataProvider} which is used to display the data in-line
		 *   where the variable is shown. If a single root item is returned from
		 *   the data provider, it will replace the variable in its tree.
		 *   Otherwise, the items will be shown as children of the variable.
		 */
		visualization?: Command | TreeDataProvider<unknown>;

		/**
		 * Creates a new debug visualization object.
		 * @param name Name of the visualization to show to the user.
		 */
		constructor(name: string);
	}

	export interface DebugVisualizationProvider<T extends DebugVisualization = DebugVisualization> {
		/**
		 * Called for each variable when the debug session stops. It should return
		 * any visualizations the extension wishes to show to the user.
		 *
		 * Note that this is only called when its `when` clause defined under the
		 * `debugVisualizers` contribution point in the `package.json` evaluates
		 * to true.
		 */
		provideDebugVisualization(context: DebugVisualizationContext, token: CancellationToken): ProviderResult<T[]>;

		/**
		 * Invoked for a variable when a user picks the visualizer.
		 *
		 * It may return a {@link TreeView} that's shown in the Debug Console or
		 * inline in a hover. A visualizer may choose to return `undefined` from
		 * this function and instead trigger other actions in the UI, such as opening
		 * a custom {@link WebviewView}.
		 */
		resolveDebugVisualization(visualization: T, token: CancellationToken): ProviderResult<T>;
	}

	export interface DebugVisualizationContext {
		/**
		 * The Debug Adapter Protocol Variable to be visualized.
		 * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable
		 */
		variable: any;
		/**
		 * The Debug Adapter Protocol variable reference the type (such as a scope
		 * or another variable) that contained this one. Empty for variables
		 * that came from user evaluations in the Debug Console.
		 * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable
		 */
		containerId?: number;
		/**
		 * The ID of the Debug Adapter Protocol StackFrame in which the variable was found,
		 * for variables that came from scopes in a stack frame.
		 * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame
		 */
		frameId?: string;
		/**
		 * The debug session the variable belongs to.
		 */
		session: DebugSession;
	}
}
```

cc @hediet @roblourens @joj