diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 6f7cd9d1d86..a8d9c3557f0 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -702,7 +702,6 @@ "--background-light", "--dropdown-padding-bottom", "--dropdown-padding-top", - "--hover-maxWidth", "--insert-border-color", "--last-tab-margin-right", "--monaco-monospace-font", @@ -732,6 +731,9 @@ "--vscode-editorCodeLens-fontSize", "--vscode-editorCodeLens-lineHeight", "--vscode-explorer-align-offset-margin-left", + "--vscode-hover-maxWidth", + "--vscode-hover-sourceWhiteSpace", + "--vscode-hover-whiteSpace", "--vscode-inline-chat-cropped", "--vscode-inline-chat-expanded", "--vscode-interactive-session-foreground", @@ -765,4 +767,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index f008173e716..ee294b766fb 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -12,6 +12,7 @@ box-sizing: border-box; animation: fadein 100ms linear; line-height: 1.5em; + white-space: var(--vscode-hover-whiteSpace, normal); } .monaco-hover.hidden { @@ -27,7 +28,7 @@ } .monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { - max-width: var(--hover-maxWidth, 500px); + max-width: var(--vscode-hover-maxWidth, 500px); word-wrap: break-word; } @@ -105,7 +106,7 @@ } .monaco-hover .monaco-tokenized-source { - white-space: pre-wrap; + white-space: var(--vscode-hover-sourceWhiteSpace, pre-wrap); } .monaco-hover .hover-row.status-bar { diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 9f5c8dab06f..691545a3e9e 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -474,7 +474,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _visibleData: ContentHoverVisibleData | undefined; private _positionPreference: ContentWidgetPositionPreference | undefined; private _minimumSize: dom.Dimension; - private _contentWidth: number; + private _contentWidth: number | undefined; private readonly _hover: HoverWidget = this._register(new HoverWidget()); private readonly _hoverVisibleKey: IContextKey; @@ -508,7 +508,6 @@ export class ContentHoverWidget extends ResizableContentWidget { const minimumSize = new dom.Dimension(minimumWidth, minimumHeight); super(editor, minimumSize); this._minimumSize = minimumSize; - this._contentWidth = minimumWidth; // we initially assume the content width to be the minimum width this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(contextKeyService); this._hoverFocusedKey = EditorContextKeys.hoverFocused.bindTo(contextKeyService); @@ -576,7 +575,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _setHoverWidgetMaxDimensions(width: number | string, height: number | string): void { ContentHoverWidget._applyMaxDimensions(this._hover.contentsDomNode, width, height); ContentHoverWidget._applyMaxDimensions(this._hover.containerDomNode, width, height); - this._hover.containerDomNode.style.setProperty('--hover-maxWidth', typeof width === 'number' ? `${width}px` : width); + this._hover.containerDomNode.style.setProperty('--vscode-hover-maxWidth', typeof width === 'number' ? `${width}px` : width); this._layoutContentWidget(); } @@ -606,7 +605,7 @@ export class ContentHoverWidget extends ResizableContentWidget { } } - private _setResizableNodeMaxDimensions(): void { + private _updateResizableNodeMaxDimensions(): void { const maxRenderingWidth = this._findMaximumRenderingWidth() ?? Infinity; const maxRenderingHeight = this._findMaximumRenderingHeight() ?? Infinity; this._resizableNode.maxSize = new dom.Dimension(maxRenderingWidth, maxRenderingHeight); @@ -617,7 +616,7 @@ export class ContentHoverWidget extends ResizableContentWidget { ContentHoverWidget._lastDimensions = new dom.Dimension(size.width, size.height); this._setAdjustedHoverWidgetDimensions(size); this._resizableNode.layout(size.height, size.width); - this._setResizableNodeMaxDimensions(); + this._updateResizableNodeMaxDimensions(); this._hover.scrollbar.scanDomNode(); this._editor.layoutContentWidget(this); this._visibleData?.colorPicker?.layout(); @@ -647,13 +646,41 @@ export class ContentHoverWidget extends ResizableContentWidget { return Math.min(availableSpace, maximumHeight); } + private _isHoverTextOverflowing(): boolean { + // To find out if the text is overflowing, we will disable wrapping, check the widths, and then re-enable wrapping + this._hover.containerDomNode.style.setProperty('--vscode-hover-whiteSpace', 'nowrap'); + this._hover.containerDomNode.style.setProperty('--vscode-hover-sourceWhiteSpace', 'nowrap'); + + const overflowing = Array.from(this._hover.contentsDomNode.children).some((hoverElement) => { + return hoverElement.scrollWidth > hoverElement.clientWidth; + }); + + this._hover.containerDomNode.style.removeProperty('--vscode-hover-whiteSpace'); + this._hover.containerDomNode.style.removeProperty('--vscode-hover-sourceWhiteSpace'); + + return overflowing; + } + private _findMaximumRenderingWidth(): number | undefined { if (!this._editor || !this._editor.hasModel()) { return; } - const bodyBoxWidth = dom.getClientArea(document.body).width; - const horizontalPadding = 14; - return bodyBoxWidth - horizontalPadding; + + const overflowing = this._isHoverTextOverflowing(); + + const initialWidth = ( + typeof this._contentWidth === 'undefined' + ? 0 + : this._contentWidth - 2 // - 2 for the borders + ); + + if (overflowing || this._hover.containerDomNode.clientWidth < initialWidth) { + const bodyBoxWidth = dom.getClientArea(document.body).width; + const horizontalPadding = 14; + return bodyBoxWidth - horizontalPadding; + } else { + return this._hover.containerDomNode.clientWidth + 2; + } } public isMouseGettingCloser(posx: number, posy: number): boolean { @@ -804,8 +831,13 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _updateMinimumWidth(): void { + const width = ( + typeof this._contentWidth === 'undefined' + ? this._minimumSize.width + : Math.min(this._contentWidth, this._minimumSize.width) + ); // We want to avoid that the hover is artificially large, so we use the content width as minimum width - this._resizableNode.minSize = new dom.Dimension(Math.min(this._contentWidth, this._minimumSize.width), this._minimumSize.height); + this._resizableNode.minSize = new dom.Dimension(width, this._minimumSize.height); } public onContentsChanged(): void { @@ -822,6 +854,7 @@ export class ContentHoverWidget extends ResizableContentWidget { width = dom.getTotalWidth(containerDomNode); this._contentWidth = width; this._updateMinimumWidth(); + this._updateResizableNodeMaxDimensions(); this._resizableNode.layout(height, width); if (this._hasHorizontalScrollbar()) {