Code change:
<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IResizeEvent, ResizableHTMLElement } from 'vs/base/browser/ui/resizable/resizable'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; import { PositionAffinity } from 'vs/editor/common/model'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Emitter, Event } from 'vs/base/common/event'; export abstract class ResizableWidget implements IDisposable { readonly element: ResizableHTMLElement; private readonly _disposables = new DisposableStore(); private readonly _persistingMechanism: IPersistingMechanism; constructor( private readonly _editor: ICodeEditor, private readonly _persistingOptions: IPersistingOptions, ) { console.log('Inside of ResizableWidget constructor'); this.element = new ResizableHTMLElement(); this.element.domNode.classList.add('editor-widget', 'resizable-widget'); if (this._persistingOptions instanceof SingleSizePersistingOptions) { this._persistingMechanism = new SingleSizePersistingMechanism(this, this.element, this._editor, this._persistingOptions); } else if (this._persistingOptions instanceof MultipleSizePersistingOptions) { this._persistingMechanism = new MultipleSizePersistingMechanism(this, this.element, this._editor); } else { throw new Error('Please specify a valid persisting mechanism'); } } dispose(): void { this._disposables.dispose(); this.element.dispose(); } resize(dimension: dom.Dimension): void { } hide(): void { this.element.clearSashHoverState(); } findMaximumRenderingHeight(): number | undefined { return Infinity; } findMaximumRenderingWidth(): number | undefined { return Infinity; } findPersistedSize(): dom.Dimension | undefined { return this._persistingMechanism.findSize(); } } export abstract class ResizableContentWidget implements IContentWidget { abstract ID: string; private _position: IPosition | null = null; private _secondaryPosition: IPosition | null = null; private _preference: ContentWidgetPositionPreference[] = []; private _positionAffinity: PositionAffinity | undefined = undefined; constructor(private readonly resizableWidget: ResizableWidget, private readonly editor: ICodeEditor) { this.editor.addContentWidget(this); console.log('Inisde of ResizableContentWidget constructor'); } findPersistedSize(): dom.Dimension | undefined { return this.resizableWidget.findPersistedSize(); } getId(): string { console.log('this.ID : ', this.ID); return this.ID; } getDomNode(): HTMLElement { console.log('Inside of getDomNode of ResizableContentWidget'); console.log('this.resizableWidget.element.domNode : ', this.resizableWidget.element.domNode); this.resizableWidget.element.domNode.style.zIndex = '49'; this.resizableWidget.element.domNode.style.position = 'fixed'; // this.resizableWidget.element.domNode.style.display = 'block'; return this.resizableWidget.element.domNode; } getPosition(): IContentWidgetPosition | null { console.log('Inside of getPosition of ResizableContentWidget'); const contentWidgetPosition = { position: this._position, secondaryPosition: this._secondaryPosition, preference: (this._preference), positionAffinity: this._positionAffinity }; console.log('contentWidgetPosition: ', contentWidgetPosition); return contentWidgetPosition; } hide(): void { this.editor.layoutContentWidget(this); } set position(position: IPosition | null) { this._position = position; } set secondaryPosition(position: IPosition | null) { this._secondaryPosition = position; } set preference(preference: ContentWidgetPositionPreference[]) { this._preference = preference; } set positionAffinity(affinity: PositionAffinity | undefined) { this._positionAffinity = affinity; } } interface IPersistingOptions { } export class SingleSizePersistingOptions implements IPersistingOptions { constructor( public readonly key: string, public readonly defaultSize: dom.Dimension, @IStorageService public readonly storageService: IStorageService ) { } } export class MultipleSizePersistingOptions implements IPersistingOptions { constructor() { } } interface IPersistingMechanism extends IDisposable { findSize(): dom.Dimension | undefined; } // TODO: maybe need to make more generic, this is specific to the suggest widget class SingleSizePersistingMechanism implements IPersistingMechanism { private readonly _persistedWidgetSize: PersistedWidgetSize | null = null; private readonly _disposables = new DisposableStore(); constructor( private readonly resizableWidget: ResizableWidget, private readonly element: ResizableHTMLElement, private readonly editor: ICodeEditor, private readonly persistingOptions: SingleSizePersistingOptions ) { this._persistedWidgetSize = new PersistedWidgetSize(this.persistingOptions.key, this.persistingOptions.storageService, this.editor); class ResizeState { constructor( readonly persistedSize: dom.Dimension | undefined, readonly currentSize: dom.Dimension, public persistHeight = false, public persistWidth = false, ) { } } let state: ResizeState | undefined; this._disposables.add(this.element.onDidWillResize(() => { // TODO: add back, this._contentWidget.lockPreference(); state = new ResizeState(this._persistedWidgetSize!.restore(), this.element.size); })); this._disposables.add(this.element.onDidResize(e => { this.resizableWidget.resize(new dom.Dimension(e.dimension.width, e.dimension.height)); if (state) { state.persistHeight = state.persistHeight || !!e.north || !!e.south; state.persistWidth = state.persistWidth || !!e.east || !!e.west; } if (!e.done) { return; } if (state) { const fontInfo = this.editor.getOption(EditorOption.fontInfo); const itemHeight = clamp(this.editor.getOption(EditorOption.suggestLineHeight) || fontInfo.lineHeight, 8, 1000); const threshold = Math.round(itemHeight / 2); let { width, height } = this.element.size; if (!state.persistHeight || Math.abs(state.currentSize.height - height) <= threshold) { height = state.persistedSize?.height ?? this.persistingOptions.defaultSize.height; } if (!state.persistWidth || Math.abs(state.currentSize.width - width) <= threshold) { width = state.persistedSize?.width ?? this.persistingOptions.defaultSize.width; } this._persistedWidgetSize!.store(new dom.Dimension(width, height)); } // TODO: add back, reset working state // this._contentWidget.unlockPreference(); state = undefined; })); } findSize(): dom.Dimension | undefined { return undefined; } dispose(): void { this._disposables.dispose(); } } class MultipleSizePersistingMechanism implements IPersistingMechanism { private readonly _persistedWidgetSizes: ResourceMap<Map<string, dom.Dimension>> = new ResourceMap<Map<string, dom.Dimension>>(); private readonly _disposables = new DisposableStore(); private _tooltipPosition: IPosition | null = null; // TODO: not sure if I need the following // private _initialHeight: number = 0; // private _initialTop: number = 0; private _resizing: boolean = false; private _size: dom.Dimension | undefined = undefined; private _maxRenderingHeight: number | undefined = Infinity; private _maxRenderingWidth: number | undefined = Infinity; private readonly _onDidResize = new Emitter<IResizeEvent>(); readonly onDidResize: Event<IResizeEvent> = this._onDidResize.event; // private _renderingAbove: ContentWidgetPositionPreference | undefined = undefined; constructor( private readonly resizableWidget: ResizableWidget, private readonly element: ResizableHTMLElement, public readonly editor: ICodeEditor ) { this.element.minSize = new dom.Dimension(10, 24); this._disposables.add(this.editor.onDidChangeModelContent((e) => { const uri = this.editor.getModel()?.uri; if (!uri || !this._persistedWidgetSizes.has(uri)) { return; } const persistedSizesForUri = this._persistedWidgetSizes.get(uri)!; const updatedPersistedSizesForUri = new Map<string, dom.Dimension>(); for (const change of e.changes) { const changeOffset = change.rangeOffset; const rangeLength = change.rangeLength; const endOffset = changeOffset + rangeLength; const textLength = change.text.length; for (const key of persistedSizesForUri.keys()) { const parsedKey = JSON.parse(key); const tokenOffset = parsedKey[0]; const tokenLength = parsedKey[1]; if (endOffset < tokenOffset) { const oldSize = persistedSizesForUri.get(key)!; const newKey: [number, number] = [tokenOffset - rangeLength + textLength, tokenLength]; updatedPersistedSizesForUri.set(JSON.stringify(newKey), oldSize); } else if (changeOffset >= tokenOffset + tokenLength) { updatedPersistedSizesForUri.set(key, persistedSizesForUri.get(key)!); } } } this._persistedWidgetSizes.set(uri, updatedPersistedSizesForUri); })); this._disposables.add(this.element.onDidWillResize(() => { this._resizing = true; // this._initialHeight = this.element.domNode.clientHeight; // this._initialTop = this.element.domNode.offsetTop; })); this._disposables.add(this.element.onDidResize(e => { let height = e.dimension.height; let width = e.dimension.width; const maxWidth = this.element.maxSize.width; const maxHeight = this.element.maxSize.height; width = Math.min(maxWidth, width); height = Math.min(maxHeight, height); if (!this._maxRenderingHeight) { return; } this._size = new dom.Dimension(width, height); this.element.layout(height, width); // Calling the resize function of the this.resizableWidget.resize(new dom.Dimension(width, height)); // Update the top parameters only when we decided to render above // TODO: presumably do not need to resize the element // if (this._renderingAbove === ContentWidgetPositionPreference.ABOVE) { // 	this.element.domNode.style.top = this._initialTop - (height - this._initialHeight) + 'px'; // } // const horizontalSashWidth = width - 2 * SASH_WIDTH + 2 * TOTAL_BORDER_WIDTH + 'px'; // this.element.northSash.el.style.width = horizontalSashWidth; // this.element.southSash.el.style.width = horizontalSashWidth; // const verticalSashWidth = height - 2 * SASH_WIDTH + 2 * TOTAL_BORDER_WIDTH + 'px'; // this.element.eastSash.el.style.height = verticalSashWidth; // this.element.westSash.el.style.height = verticalSashWidth; // this.element.eastSash.el.style.top = TOTAL_BORDER_WIDTH + 'px'; // Fire the current dimension // TODO: probably don't need to listen on the firing event? // this._onDidResize.fire({ dimension: this._size, done: false }); this._maxRenderingWidth = this.resizableWidget.findMaximumRenderingWidth(); this._maxRenderingHeight = this.resizableWidget.findMaximumRenderingHeight(); // this._maxRenderingHeight = this.resizableWidget.findMaximumRenderingHeight(this._renderingAbove); if (!this._maxRenderingHeight || !this._maxRenderingWidth) { return; } this.element.maxSize = new dom.Dimension(this._maxRenderingWidth, this._maxRenderingHeight); // Persist the height only when the resizing has stopped if (e.done) { if (!this.editor.hasModel()) { return; } const uri = this.editor.getModel().uri; if (!uri || !this._tooltipPosition) { return; } const persistedSize = new dom.Dimension(width, height); const wordPosition = this.editor.getModel().getWordAtPosition(this._tooltipPosition); if (!wordPosition) { return; } const offset = this.editor.getModel().getOffsetAt({ lineNumber: this._tooltipPosition.lineNumber, column: wordPosition.startColumn }); const length = wordPosition.word.length; // Suppose that the uri does not exist in the persisted widget hover sizes, then create a map if (!this._persistedWidgetSizes.get(uri)) { const persistedWidgetSizesForUri = new Map<string, dom.Dimension>([]); persistedWidgetSizesForUri.set(JSON.stringify([offset, length]), persistedSize); this._persistedWidgetSizes.set(uri, persistedWidgetSizesForUri); } else { const persistedWidgetSizesForUri = this._persistedWidgetSizes.get(uri)!; persistedWidgetSizesForUri.set(JSON.stringify([offset, length]), persistedSize); } this._resizing = false; } // this.editor.layoutOverlayWidget(this); // this.editor.render(); })); } set tooltipPosition(position: IPosition) { this._tooltipPosition = position; } findSize(): dom.Dimension | undefined { console.log('Inside of findSize of the MultiplePersistingMechanisms'); if (!this._tooltipPosition || !this.editor.hasModel()) { return; } const wordPosition = this.editor.getModel().getWordAtPosition(this._tooltipPosition); if (!wordPosition) { return; } const offset = this.editor.getModel().getOffsetAt({ lineNumber: this._tooltipPosition.lineNumber, column: wordPosition.startColumn }); const length = wordPosition.word.length; const uri = this.editor.getModel().uri; const persistedSizesForUri = this._persistedWidgetSizes.get(uri); if (!persistedSizesForUri) { return; } return persistedSizesForUri.get(JSON.stringify([offset, length])); } dispose(): void { this._disposables.dispose(); } } class PersistedWidgetSize { constructor( private readonly _key: string, private readonly _service: IStorageService, editor: ICodeEditor ) { } restore(): dom.Dimension | undefined { const raw = this._service.get(this._key, StorageScope.PROFILE) ?? ''; try { const obj = JSON.parse(raw); if (dom.Dimension.is(obj)) { return dom.Dimension.lift(obj); } } catch { // ignore } return undefined; } store(size: dom.Dimension) { this._service.store(this._key, JSON.stringify(size), StorageScope.PROFILE, StorageTarget.MACHINE); } reset(): void { this._service.remove(this._key, StorageScope.PROFILE); } } </ins>

Related discussion:
text: And do you consider increasing the default size, that is unfitting to most real world scenarios? The windows is so small, that I feel the need to increase it even on my WXGA Laptop.
Hey all, thank you for the patience. A PR has been merged in order to make the content hover resizable. The change should be visible in tomorrow's VS Code insiders version and in the next stable release which will happen in the coming two weeks. There currently is no mechanism to save the content hover - we are discussing how best to approach it. You may track the issue on persisting the hover content here: we are currently not thinking about increasing the default maximum size. However it is possible that we implement the persisting mechanism as follows: when you increase the content hover size to a specific size, this size is persisted, and it is applied as maximum default size on the next hover appearances. This potential implementation is still under discussion by our team.
Just want to say thank you you seem to have been tackling a lot of these high-level nitpicks that have been in the VSCode backlog for years, and for that I am very appreciative.
I can't find a command to clear/reset hover widget size, is this has been ?
Hi currently the command does not exist because a persisting mechanism has not been put into place as of yet. It will appear when the latter is .
Oh okay, thanks a lot