UNPKG

monaco-editor

Version:
884 lines (883 loc) • 42.5 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var ContentHoverController_1, ContentHoverWidget_1; import * as dom from '../../../../base/browser/dom.js'; import { HoverAction, HoverWidget, getHoverAccessibleViewHint } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { coalesce } from '../../../../base/common/arrays.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; import { ModelDecorationOptions } from '../../../common/model/textModel.js'; import { TokenizationRegistry } from '../../../common/languages.js'; import { HoverOperation } from './hoverOperation.js'; import { HoverParticipantRegistry, HoverRangeAnchor } from './hoverTypes.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { AsyncIterableObject } from '../../../../base/common/async.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ResizableContentWidget } from './resizableContentWidget.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; const $ = dom.$; let ContentHoverController = ContentHoverController_1 = class ContentHoverController extends Disposable { constructor(_editor, _instantiationService, _keybindingService) { super(); this._editor = _editor; this._instantiationService = _instantiationService; this._keybindingService = _keybindingService; this._currentResult = null; this._widget = this._register(this._instantiationService.createInstance(ContentHoverWidget, this._editor)); // Instantiate participants and sort them by `hoverOrdinal` which is relevant for rendering order. this._participants = []; for (const participant of HoverParticipantRegistry.getAll()) { this._participants.push(this._instantiationService.createInstance(participant, this._editor)); } this._participants.sort((p1, p2) => p1.hoverOrdinal - p2.hoverOrdinal); this._computer = new ContentHoverComputer(this._editor, this._participants); this._hoverOperation = this._register(new HoverOperation(this._editor, this._computer)); this._register(this._hoverOperation.onResult((result) => { if (!this._computer.anchor) { // invalid state, ignore result return; } const messages = (result.hasLoadingMessage ? this._addLoadingMessage(result.value) : result.value); this._withResult(new HoverResult(this._computer.anchor, messages, result.isComplete)); })); this._register(dom.addStandardDisposableListener(this._widget.getDomNode(), 'keydown', (e) => { if (e.equals(9 /* KeyCode.Escape */)) { this.hide(); } })); this._register(TokenizationRegistry.onDidChange(() => { if (this._widget.position && this._currentResult) { this._setCurrentResult(this._currentResult); // render again } })); } /** * Returns true if the hover shows now or will show. */ _startShowingOrUpdateHover(anchor, mode, source, focus, mouseEvent) { if (!this._widget.position || !this._currentResult) { // The hover is not visible if (anchor) { this._startHoverOperationIfNecessary(anchor, mode, source, focus, false); return true; } return false; } // The hover is currently visible const isHoverSticky = this._editor.getOption(60 /* EditorOption.hover */).sticky; const isGettingCloser = (isHoverSticky && mouseEvent && this._widget.isMouseGettingCloser(mouseEvent.event.posx, mouseEvent.event.posy)); if (isGettingCloser) { // The mouse is getting closer to the hover, so we will keep the hover untouched // But we will kick off a hover update at the new anchor, insisting on keeping the hover visible. if (anchor) { this._startHoverOperationIfNecessary(anchor, mode, source, focus, true); } return true; } if (!anchor) { this._setCurrentResult(null); return false; } if (anchor && this._currentResult.anchor.equals(anchor)) { // The widget is currently showing results for the exact same anchor, so no update is needed return true; } if (!anchor.canAdoptVisibleHover(this._currentResult.anchor, this._widget.position)) { // The new anchor is not compatible with the previous anchor this._setCurrentResult(null); this._startHoverOperationIfNecessary(anchor, mode, source, focus, false); return true; } // We aren't getting any closer to the hover, so we will filter existing results // and keep those which also apply to the new anchor. this._setCurrentResult(this._currentResult.filter(anchor)); this._startHoverOperationIfNecessary(anchor, mode, source, focus, false); return true; } _startHoverOperationIfNecessary(anchor, mode, source, focus, insistOnKeepingHoverVisible) { if (this._computer.anchor && this._computer.anchor.equals(anchor)) { // We have to start a hover operation at the exact same anchor as before, so no work is needed return; } this._hoverOperation.cancel(); this._computer.anchor = anchor; this._computer.shouldFocus = focus; this._computer.source = source; this._computer.insistOnKeepingHoverVisible = insistOnKeepingHoverVisible; this._hoverOperation.start(mode); } _setCurrentResult(hoverResult) { if (this._currentResult === hoverResult) { // avoid updating the DOM to avoid resetting the user selection return; } if (hoverResult && hoverResult.messages.length === 0) { hoverResult = null; } this._currentResult = hoverResult; if (this._currentResult) { this._renderMessages(this._currentResult.anchor, this._currentResult.messages); } else { this._widget.hide(); } } _addLoadingMessage(result) { if (this._computer.anchor) { for (const participant of this._participants) { if (participant.createLoadingMessage) { const loadingMessage = participant.createLoadingMessage(this._computer.anchor); if (loadingMessage) { return result.slice(0).concat([loadingMessage]); } } } } return result; } _withResult(hoverResult) { if (this._widget.position && this._currentResult && this._currentResult.isComplete) { // The hover is visible with a previous complete result. if (!hoverResult.isComplete) { // Instead of rendering the new partial result, we wait for the result to be complete. return; } if (this._computer.insistOnKeepingHoverVisible && hoverResult.messages.length === 0) { // The hover would now hide normally, so we'll keep the previous messages return; } } this._setCurrentResult(hoverResult); } _renderMessages(anchor, messages) { const { showAtPosition, showAtSecondaryPosition, highlightRange } = ContentHoverController_1.computeHoverRanges(this._editor, anchor.range, messages); const disposables = new DisposableStore(); const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService)); const fragment = document.createDocumentFragment(); let colorPicker = null; const context = { fragment, statusBar, setColorPicker: (widget) => colorPicker = widget, onContentsChanged: () => this._widget.onContentsChanged(), setMinimumDimensions: (dimensions) => this._widget.setMinimumDimensions(dimensions), hide: () => this.hide() }; for (const participant of this._participants) { const hoverParts = messages.filter(msg => msg.owner === participant); if (hoverParts.length > 0) { disposables.add(participant.renderHoverParts(context, hoverParts)); } } const isBeforeContent = messages.some(m => m.isBeforeContent); if (statusBar.hasContent) { fragment.appendChild(statusBar.hoverElement); } if (fragment.hasChildNodes()) { if (highlightRange) { const highlightDecoration = this._editor.createDecorationsCollection(); highlightDecoration.set([{ range: highlightRange, options: ContentHoverController_1._DECORATION_OPTIONS }]); disposables.add(toDisposable(() => { highlightDecoration.clear(); })); } this._widget.showAt(fragment, new ContentHoverVisibleData(anchor.initialMousePosX, anchor.initialMousePosY, colorPicker, showAtPosition, showAtSecondaryPosition, this._editor.getOption(60 /* EditorOption.hover */).above, this._computer.shouldFocus, this._computer.source, isBeforeContent, disposables)); } else { disposables.dispose(); } } static computeHoverRanges(editor, anchorRange, messages) { let startColumnBoundary = 1; if (editor.hasModel()) { // Ensure the range is on the current view line const viewModel = editor._getViewModel(); const coordinatesConverter = viewModel.coordinatesConverter; const anchorViewRange = coordinatesConverter.convertModelRangeToViewRange(anchorRange); const anchorViewRangeStart = new Position(anchorViewRange.startLineNumber, viewModel.getLineMinColumn(anchorViewRange.startLineNumber)); startColumnBoundary = coordinatesConverter.convertViewPositionToModelPosition(anchorViewRangeStart).column; } // The anchor range is always on a single line const anchorLineNumber = anchorRange.startLineNumber; let renderStartColumn = anchorRange.startColumn; let highlightRange = messages[0].range; let forceShowAtRange = null; for (const msg of messages) { highlightRange = Range.plusRange(highlightRange, msg.range); if (msg.range.startLineNumber === anchorLineNumber && msg.range.endLineNumber === anchorLineNumber) { // this message has a range that is completely sitting on the line of the anchor renderStartColumn = Math.max(Math.min(renderStartColumn, msg.range.startColumn), startColumnBoundary); } if (msg.forceShowAtRange) { forceShowAtRange = msg.range; } } const showAtPosition = forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorLineNumber, anchorRange.startColumn); const showAtSecondaryPosition = forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorLineNumber, renderStartColumn); return { showAtPosition, showAtSecondaryPosition, highlightRange }; } /** * Returns true if the hover shows now or will show. */ showsOrWillShow(mouseEvent) { if (this._widget.isResizing) { return true; } const anchorCandidates = []; for (const participant of this._participants) { if (participant.suggestHoverAnchor) { const anchor = participant.suggestHoverAnchor(mouseEvent); if (anchor) { anchorCandidates.push(anchor); } } } const target = mouseEvent.target; if (target.type === 6 /* MouseTargetType.CONTENT_TEXT */) { anchorCandidates.push(new HoverRangeAnchor(0, target.range, mouseEvent.event.posx, mouseEvent.event.posy)); } if (target.type === 7 /* MouseTargetType.CONTENT_EMPTY */) { const epsilon = this._editor.getOption(50 /* EditorOption.fontInfo */).typicalHalfwidthCharacterWidth / 2; if (!target.detail.isAfterLines && typeof target.detail.horizontalDistanceToText === 'number' && target.detail.horizontalDistanceToText < epsilon) { // Let hover kick in even when the mouse is technically in the empty area after a line, given the distance is small enough anchorCandidates.push(new HoverRangeAnchor(0, target.range, mouseEvent.event.posx, mouseEvent.event.posy)); } } if (anchorCandidates.length === 0) { return this._startShowingOrUpdateHover(null, 0 /* HoverStartMode.Delayed */, 0 /* HoverStartSource.Mouse */, false, mouseEvent); } anchorCandidates.sort((a, b) => b.priority - a.priority); return this._startShowingOrUpdateHover(anchorCandidates[0], 0 /* HoverStartMode.Delayed */, 0 /* HoverStartSource.Mouse */, false, mouseEvent); } startShowingAtRange(range, mode, source, focus) { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } containsNode(node) { return (node ? this._widget.getDomNode().contains(node) : false); } focus() { this._widget.focus(); } scrollUp() { this._widget.scrollUp(); } scrollDown() { this._widget.scrollDown(); } scrollLeft() { this._widget.scrollLeft(); } scrollRight() { this._widget.scrollRight(); } pageUp() { this._widget.pageUp(); } pageDown() { this._widget.pageDown(); } goToTop() { this._widget.goToTop(); } goToBottom() { this._widget.goToBottom(); } hide() { this._computer.anchor = null; this._hoverOperation.cancel(); this._setCurrentResult(null); } get isColorPickerVisible() { return this._widget.isColorPickerVisible; } get isVisibleFromKeyboard() { return this._widget.isVisibleFromKeyboard; } get isVisible() { return this._widget.isVisible; } get isFocused() { return this._widget.isFocused; } get isResizing() { return this._widget.isResizing; } get widget() { return this._widget; } }; ContentHoverController._DECORATION_OPTIONS = ModelDecorationOptions.register({ description: 'content-hover-highlight', className: 'hoverHighlight' }); ContentHoverController = ContentHoverController_1 = __decorate([ __param(1, IInstantiationService), __param(2, IKeybindingService) ], ContentHoverController); export { ContentHoverController }; class HoverResult { constructor(anchor, messages, isComplete) { this.anchor = anchor; this.messages = messages; this.isComplete = isComplete; } filter(anchor) { const filteredMessages = this.messages.filter((m) => m.isValidForHoverAnchor(anchor)); if (filteredMessages.length === this.messages.length) { return this; } return new FilteredHoverResult(this, this.anchor, filteredMessages, this.isComplete); } } class FilteredHoverResult extends HoverResult { constructor(original, anchor, messages, isComplete) { super(anchor, messages, isComplete); this.original = original; } filter(anchor) { return this.original.filter(anchor); } } class ContentHoverVisibleData { constructor(initialMousePosX, initialMousePosY, colorPicker, showAtPosition, showAtSecondaryPosition, preferAbove, stoleFocus, source, isBeforeContent, disposables) { this.initialMousePosX = initialMousePosX; this.initialMousePosY = initialMousePosY; this.colorPicker = colorPicker; this.showAtPosition = showAtPosition; this.showAtSecondaryPosition = showAtSecondaryPosition; this.preferAbove = preferAbove; this.stoleFocus = stoleFocus; this.source = source; this.isBeforeContent = isBeforeContent; this.disposables = disposables; this.closestMouseDistance = undefined; } } const HORIZONTAL_SCROLLING_BY = 30; const SCROLLBAR_WIDTH = 10; const CONTAINER_HEIGHT_PADDING = 6; let ContentHoverWidget = ContentHoverWidget_1 = class ContentHoverWidget extends ResizableContentWidget { get isColorPickerVisible() { var _a; return Boolean((_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.colorPicker); } get isVisibleFromKeyboard() { var _a; return (((_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.source) === 1 /* HoverStartSource.Keyboard */); } get isVisible() { var _a; return (_a = this._hoverVisibleKey.get()) !== null && _a !== void 0 ? _a : false; } get isFocused() { var _a; return (_a = this._hoverFocusedKey.get()) !== null && _a !== void 0 ? _a : false; } constructor(editor, contextKeyService, _configurationService, _accessibilityService, _keybindingService) { const minimumHeight = editor.getOption(66 /* EditorOption.lineHeight */) + 8; const minimumWidth = 150; const minimumSize = new dom.Dimension(minimumWidth, minimumHeight); super(editor, minimumSize); this._configurationService = _configurationService; this._accessibilityService = _accessibilityService; this._keybindingService = _keybindingService; this._hover = this._register(new HoverWidget()); this._minimumSize = minimumSize; this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(contextKeyService); this._hoverFocusedKey = EditorContextKeys.hoverFocused.bindTo(contextKeyService); dom.append(this._resizableNode.domNode, this._hover.containerDomNode); this._resizableNode.domNode.style.zIndex = '50'; this._register(this._editor.onDidLayoutChange(() => { if (this.isVisible) { this._updateMaxDimensions(); } })); this._register(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(50 /* EditorOption.fontInfo */)) { this._updateFont(); } })); const focusTracker = this._register(dom.trackFocus(this._resizableNode.domNode)); this._register(focusTracker.onDidFocus(() => { this._hoverFocusedKey.set(true); })); this._register(focusTracker.onDidBlur(() => { this._hoverFocusedKey.set(false); })); this._setHoverData(undefined); this._editor.addContentWidget(this); } dispose() { var _a; super.dispose(); (_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.disposables.dispose(); this._editor.removeContentWidget(this); } getId() { return ContentHoverWidget_1.ID; } static _applyDimensions(container, width, height) { const transformedWidth = typeof width === 'number' ? `${width}px` : width; const transformedHeight = typeof height === 'number' ? `${height}px` : height; container.style.width = transformedWidth; container.style.height = transformedHeight; } _setContentsDomNodeDimensions(width, height) { const contentsDomNode = this._hover.contentsDomNode; return ContentHoverWidget_1._applyDimensions(contentsDomNode, width, height); } _setContainerDomNodeDimensions(width, height) { const containerDomNode = this._hover.containerDomNode; return ContentHoverWidget_1._applyDimensions(containerDomNode, width, height); } _setHoverWidgetDimensions(width, height) { this._setContentsDomNodeDimensions(width, height); this._setContainerDomNodeDimensions(width, height); this._layoutContentWidget(); } static _applyMaxDimensions(container, width, height) { const transformedWidth = typeof width === 'number' ? `${width}px` : width; const transformedHeight = typeof height === 'number' ? `${height}px` : height; container.style.maxWidth = transformedWidth; container.style.maxHeight = transformedHeight; } _setHoverWidgetMaxDimensions(width, height) { ContentHoverWidget_1._applyMaxDimensions(this._hover.contentsDomNode, width, height); ContentHoverWidget_1._applyMaxDimensions(this._hover.containerDomNode, width, height); this._hover.containerDomNode.style.setProperty('--vscode-hover-maxWidth', typeof width === 'number' ? `${width}px` : width); this._layoutContentWidget(); } _hasHorizontalScrollbar() { const scrollDimensions = this._hover.scrollbar.getScrollDimensions(); const hasHorizontalScrollbar = scrollDimensions.scrollWidth > scrollDimensions.width; return hasHorizontalScrollbar; } _adjustContentsBottomPadding() { const contentsDomNode = this._hover.contentsDomNode; const extraBottomPadding = `${this._hover.scrollbar.options.horizontalScrollbarSize}px`; if (contentsDomNode.style.paddingBottom !== extraBottomPadding) { contentsDomNode.style.paddingBottom = extraBottomPadding; } } _setAdjustedHoverWidgetDimensions(size) { this._setHoverWidgetMaxDimensions('none', 'none'); const width = size.width; const height = size.height; this._setHoverWidgetDimensions(width, height); // measure if widget has horizontal scrollbar after setting the dimensions if (this._hasHorizontalScrollbar()) { this._adjustContentsBottomPadding(); this._setContentsDomNodeDimensions(width, height - SCROLLBAR_WIDTH); } } _updateResizableNodeMaxDimensions() { var _a, _b; const maxRenderingWidth = (_a = this._findMaximumRenderingWidth()) !== null && _a !== void 0 ? _a : Infinity; const maxRenderingHeight = (_b = this._findMaximumRenderingHeight()) !== null && _b !== void 0 ? _b : Infinity; this._resizableNode.maxSize = new dom.Dimension(maxRenderingWidth, maxRenderingHeight); this._setHoverWidgetMaxDimensions(maxRenderingWidth, maxRenderingHeight); } _resize(size) { var _a, _b; ContentHoverWidget_1._lastDimensions = new dom.Dimension(size.width, size.height); this._setAdjustedHoverWidgetDimensions(size); this._resizableNode.layout(size.height, size.width); this._updateResizableNodeMaxDimensions(); this._hover.scrollbar.scanDomNode(); this._editor.layoutContentWidget(this); (_b = (_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.colorPicker) === null || _b === void 0 ? void 0 : _b.layout(); } _findAvailableSpaceVertically() { var _a; const position = (_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.showAtPosition; if (!position) { return; } return this._positionPreference === 1 /* ContentWidgetPositionPreference.ABOVE */ ? this._availableVerticalSpaceAbove(position) : this._availableVerticalSpaceBelow(position); } _findMaximumRenderingHeight() { const availableSpace = this._findAvailableSpaceVertically(); if (!availableSpace) { return; } // Padding needed in order to stop the resizing down to a smaller height let maximumHeight = CONTAINER_HEIGHT_PADDING; Array.from(this._hover.contentsDomNode.children).forEach((hoverPart) => { maximumHeight += hoverPart.clientHeight; }); if (this._hasHorizontalScrollbar()) { maximumHeight += SCROLLBAR_WIDTH; } return Math.min(availableSpace, maximumHeight); } _isHoverTextOverflowing() { // 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; } _findMaximumRenderingWidth() { if (!this._editor || !this._editor.hasModel()) { return; } 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(this._hover.containerDomNode.ownerDocument.body).width; const horizontalPadding = 14; return bodyBoxWidth - horizontalPadding; } else { return this._hover.containerDomNode.clientWidth + 2; } } isMouseGettingCloser(posx, posy) { if (!this._visibleData) { return false; } if (typeof this._visibleData.initialMousePosX === 'undefined' || typeof this._visibleData.initialMousePosY === 'undefined') { this._visibleData.initialMousePosX = posx; this._visibleData.initialMousePosY = posy; return false; } const widgetRect = dom.getDomNodePagePosition(this.getDomNode()); if (typeof this._visibleData.closestMouseDistance === 'undefined') { this._visibleData.closestMouseDistance = computeDistanceFromPointToRectangle(this._visibleData.initialMousePosX, this._visibleData.initialMousePosY, widgetRect.left, widgetRect.top, widgetRect.width, widgetRect.height); } const distance = computeDistanceFromPointToRectangle(posx, posy, widgetRect.left, widgetRect.top, widgetRect.width, widgetRect.height); if (distance > this._visibleData.closestMouseDistance + 4 /* tolerance of 4 pixels */) { // The mouse is getting farther away return false; } this._visibleData.closestMouseDistance = Math.min(this._visibleData.closestMouseDistance, distance); return true; } _setHoverData(hoverData) { var _a; (_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.disposables.dispose(); this._visibleData = hoverData; this._hoverVisibleKey.set(!!hoverData); this._hover.containerDomNode.classList.toggle('hidden', !hoverData); } _updateFont() { const { fontSize, lineHeight } = this._editor.getOption(50 /* EditorOption.fontInfo */); const contentsDomNode = this._hover.contentsDomNode; contentsDomNode.style.fontSize = `${fontSize}px`; contentsDomNode.style.lineHeight = `${lineHeight / fontSize}`; const codeClasses = Array.prototype.slice.call(this._hover.contentsDomNode.getElementsByClassName('code')); codeClasses.forEach(node => this._editor.applyFontInfo(node)); } _updateContent(node) { const contentsDomNode = this._hover.contentsDomNode; contentsDomNode.style.paddingBottom = ''; contentsDomNode.textContent = ''; contentsDomNode.appendChild(node); } _layoutContentWidget() { this._editor.layoutContentWidget(this); this._hover.onContentsChanged(); } _updateMaxDimensions() { const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget_1._lastDimensions.height); const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget_1._lastDimensions.width); this._setHoverWidgetMaxDimensions(width, height); } _render(node, hoverData) { this._setHoverData(hoverData); this._updateFont(); this._updateContent(node); this._updateMaxDimensions(); this.onContentsChanged(); // Simply force a synchronous render on the editor // such that the widget does not really render with left = '0px' this._editor.render(); } getPosition() { var _a; if (!this._visibleData) { return null; } return { position: this._visibleData.showAtPosition, secondaryPosition: this._visibleData.showAtSecondaryPosition, positionAffinity: this._visibleData.isBeforeContent ? 3 /* PositionAffinity.LeftOfInjectedText */ : undefined, preference: [(_a = this._positionPreference) !== null && _a !== void 0 ? _a : 1 /* ContentWidgetPositionPreference.ABOVE */] }; } showAt(node, hoverData) { var _a, _b, _c, _d; if (!this._editor || !this._editor.hasModel()) { return; } this._render(node, hoverData); const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); const widgetPosition = hoverData.showAtPosition; this._positionPreference = (_a = this._findPositionPreference(widgetHeight, widgetPosition)) !== null && _a !== void 0 ? _a : 1 /* ContentWidgetPositionPreference.ABOVE */; // See https://github.com/microsoft/vscode/issues/140339 // TODO: Doing a second layout of the hover after force rendering the editor this.onContentsChanged(); if (hoverData.stoleFocus) { this._hover.containerDomNode.focus(); } (_b = hoverData.colorPicker) === null || _b === void 0 ? void 0 : _b.layout(); // The aria label overrides the label, so if we add to it, add the contents of the hover const hoverFocused = this._hover.containerDomNode.ownerDocument.activeElement === this._hover.containerDomNode; const accessibleViewHint = hoverFocused && getHoverAccessibleViewHint(this._configurationService.getValue('accessibility.verbosity.hover') === true && this._accessibilityService.isScreenReaderOptimized(), (_d = (_c = this._keybindingService.lookupKeybinding('editor.action.accessibleView')) === null || _c === void 0 ? void 0 : _c.getAriaLabel()) !== null && _d !== void 0 ? _d : ''); if (accessibleViewHint) { this._hover.contentsDomNode.ariaLabel = this._hover.contentsDomNode.textContent + ', ' + accessibleViewHint; } } hide() { if (!this._visibleData) { return; } const stoleFocus = this._visibleData.stoleFocus || this._hoverFocusedKey.get(); this._setHoverData(undefined); this._resizableNode.maxSize = new dom.Dimension(Infinity, Infinity); this._resizableNode.clearSashHoverState(); this._hoverFocusedKey.set(false); this._editor.layoutContentWidget(this); if (stoleFocus) { this._editor.focus(); } } _removeConstraintsRenderNormally() { // Added because otherwise the initial size of the hover content is smaller than should be const layoutInfo = this._editor.getLayoutInfo(); this._resizableNode.layout(layoutInfo.height, layoutInfo.width); this._setHoverWidgetDimensions('auto', 'auto'); } _adjustHoverHeightForScrollbar(height) { var _a; const containerDomNode = this._hover.containerDomNode; const contentsDomNode = this._hover.contentsDomNode; const maxRenderingHeight = (_a = this._findMaximumRenderingHeight()) !== null && _a !== void 0 ? _a : Infinity; this._setContainerDomNodeDimensions(dom.getTotalWidth(containerDomNode), Math.min(maxRenderingHeight, height)); this._setContentsDomNodeDimensions(dom.getTotalWidth(contentsDomNode), Math.min(maxRenderingHeight, height - SCROLLBAR_WIDTH)); } setMinimumDimensions(dimensions) { // We combine the new minimum dimensions with the previous ones this._minimumSize = new dom.Dimension(Math.max(this._minimumSize.width, dimensions.width), Math.max(this._minimumSize.height, dimensions.height)); this._updateMinimumWidth(); } _updateMinimumWidth() { 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(width, this._minimumSize.height); } onContentsChanged() { var _a; this._removeConstraintsRenderNormally(); const containerDomNode = this._hover.containerDomNode; let height = dom.getTotalHeight(containerDomNode); let width = dom.getTotalWidth(containerDomNode); this._resizableNode.layout(height, width); this._setHoverWidgetDimensions(width, height); height = dom.getTotalHeight(containerDomNode); width = dom.getTotalWidth(containerDomNode); this._contentWidth = width; this._updateMinimumWidth(); this._resizableNode.layout(height, width); if (this._hasHorizontalScrollbar()) { this._adjustContentsBottomPadding(); this._adjustHoverHeightForScrollbar(height); } if ((_a = this._visibleData) === null || _a === void 0 ? void 0 : _a.showAtPosition) { const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); this._positionPreference = this._findPositionPreference(widgetHeight, this._visibleData.showAtPosition); } this._layoutContentWidget(); } focus() { this._hover.containerDomNode.focus(); } scrollUp() { const scrollTop = this._hover.scrollbar.getScrollPosition().scrollTop; const fontInfo = this._editor.getOption(50 /* EditorOption.fontInfo */); this._hover.scrollbar.setScrollPosition({ scrollTop: scrollTop - fontInfo.lineHeight }); } scrollDown() { const scrollTop = this._hover.scrollbar.getScrollPosition().scrollTop; const fontInfo = this._editor.getOption(50 /* EditorOption.fontInfo */); this._hover.scrollbar.setScrollPosition({ scrollTop: scrollTop + fontInfo.lineHeight }); } scrollLeft() { const scrollLeft = this._hover.scrollbar.getScrollPosition().scrollLeft; this._hover.scrollbar.setScrollPosition({ scrollLeft: scrollLeft - HORIZONTAL_SCROLLING_BY }); } scrollRight() { const scrollLeft = this._hover.scrollbar.getScrollPosition().scrollLeft; this._hover.scrollbar.setScrollPosition({ scrollLeft: scrollLeft + HORIZONTAL_SCROLLING_BY }); } pageUp() { const scrollTop = this._hover.scrollbar.getScrollPosition().scrollTop; const scrollHeight = this._hover.scrollbar.getScrollDimensions().height; this._hover.scrollbar.setScrollPosition({ scrollTop: scrollTop - scrollHeight }); } pageDown() { const scrollTop = this._hover.scrollbar.getScrollPosition().scrollTop; const scrollHeight = this._hover.scrollbar.getScrollDimensions().height; this._hover.scrollbar.setScrollPosition({ scrollTop: scrollTop + scrollHeight }); } goToTop() { this._hover.scrollbar.setScrollPosition({ scrollTop: 0 }); } goToBottom() { this._hover.scrollbar.setScrollPosition({ scrollTop: this._hover.scrollbar.getScrollDimensions().scrollHeight }); } }; ContentHoverWidget.ID = 'editor.contrib.resizableContentHoverWidget'; ContentHoverWidget._lastDimensions = new dom.Dimension(0, 0); ContentHoverWidget = ContentHoverWidget_1 = __decorate([ __param(1, IContextKeyService), __param(2, IConfigurationService), __param(3, IAccessibilityService), __param(4, IKeybindingService) ], ContentHoverWidget); export { ContentHoverWidget }; let EditorHoverStatusBar = class EditorHoverStatusBar extends Disposable { get hasContent() { return this._hasContent; } constructor(_keybindingService) { super(); this._keybindingService = _keybindingService; this._hasContent = false; this.hoverElement = $('div.hover-row.status-bar'); this.actionsElement = dom.append(this.hoverElement, $('div.actions')); } addAction(actionOptions) { const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); const keybindingLabel = keybinding ? keybinding.getLabel() : null; this._hasContent = true; return this._register(HoverAction.render(this.actionsElement, actionOptions, keybindingLabel)); } append(element) { const result = dom.append(this.actionsElement, element); this._hasContent = true; return result; } }; EditorHoverStatusBar = __decorate([ __param(0, IKeybindingService) ], EditorHoverStatusBar); export { EditorHoverStatusBar }; class ContentHoverComputer { get anchor() { return this._anchor; } set anchor(value) { this._anchor = value; } get shouldFocus() { return this._shouldFocus; } set shouldFocus(value) { this._shouldFocus = value; } get source() { return this._source; } set source(value) { this._source = value; } get insistOnKeepingHoverVisible() { return this._insistOnKeepingHoverVisible; } set insistOnKeepingHoverVisible(value) { this._insistOnKeepingHoverVisible = value; } constructor(_editor, _participants) { this._editor = _editor; this._participants = _participants; this._anchor = null; this._shouldFocus = false; this._source = 0 /* HoverStartSource.Mouse */; this._insistOnKeepingHoverVisible = false; } static _getLineDecorations(editor, anchor) { if (anchor.type !== 1 /* HoverAnchorType.Range */ && !anchor.supportsMarkerHover) { return []; } const model = editor.getModel(); const lineNumber = anchor.range.startLineNumber; if (lineNumber > model.getLineCount()) { // invalid line return []; } const maxColumn = model.getLineMaxColumn(lineNumber); return editor.getLineDecorations(lineNumber).filter((d) => { if (d.options.isWholeLine) { return true; } const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; if (d.options.showIfCollapsed) { // Relax check around `showIfCollapsed` decorations to also include +/- 1 character if (startColumn > anchor.range.startColumn + 1 || anchor.range.endColumn - 1 > endColumn) { return false; } } else { if (startColumn > anchor.range.startColumn || anchor.range.endColumn > endColumn) { return false; } } return true; }); } computeAsync(token) { const anchor = this._anchor; if (!this._editor.hasModel() || !anchor) { return AsyncIterableObject.EMPTY; } const lineDecorations = ContentHoverComputer._getLineDecorations(this._editor, anchor); return AsyncIterableObject.merge(this._participants.map((participant) => { if (!participant.computeAsync) { return AsyncIterableObject.EMPTY; } return participant.computeAsync(anchor, lineDecorations, token); })); } computeSync() { if (!this._editor.hasModel() || !this._anchor) { return []; } const lineDecorations = ContentHoverComputer._getLineDecorations(this._editor, this._anchor); let result = []; for (const participant of this._participants) { result = result.concat(participant.computeSync(this._anchor, lineDecorations)); } return coalesce(result); } } function computeDistanceFromPointToRectangle(pointX, pointY, left, top, width, height) { const x = (left + width / 2); // x center of rectangle const y = (top + height / 2); // y center of rectangle const dx = Math.max(Math.abs(pointX - x) - width / 2, 0); const dy = Math.max(Math.abs(pointY - y) - height / 2, 0); return Math.sqrt(dx * dx + dy * dy); }