UNPKG

monaco-editor

Version:
437 lines (436 loc) • 20.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 __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); } return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import * as dom from '../../../../base/browser/dom.js'; import { createFastDomNode } from '../../../../base/browser/fastDomNode.js'; import { PartFingerprints, ViewPart } from '../../view/viewPart.js'; var Coordinate = /** @class */ (function () { function Coordinate(top, left) { this.top = top; this.left = left; } return Coordinate; }()); var ViewContentWidgets = /** @class */ (function (_super) { __extends(ViewContentWidgets, _super); function ViewContentWidgets(context, viewDomNode) { var _this = _super.call(this, context) || this; _this._viewDomNode = viewDomNode; _this._widgets = {}; _this.domNode = createFastDomNode(document.createElement('div')); PartFingerprints.write(_this.domNode, 1 /* ContentWidgets */); _this.domNode.setClassName('contentWidgets'); _this.domNode.setPosition('absolute'); _this.domNode.setTop(0); _this.overflowingContentWidgetsDomNode = createFastDomNode(document.createElement('div')); PartFingerprints.write(_this.overflowingContentWidgetsDomNode, 2 /* OverflowingContentWidgets */); _this.overflowingContentWidgetsDomNode.setClassName('overflowingContentWidgets'); return _this; } ViewContentWidgets.prototype.dispose = function () { _super.prototype.dispose.call(this); this._widgets = {}; }; // --- begin event handlers ViewContentWidgets.prototype.onConfigurationChanged = function (e) { var keys = Object.keys(this._widgets); for (var i = 0, len = keys.length; i < len; i++) { var widgetId = keys[i]; this._widgets[widgetId].onConfigurationChanged(e); } return true; }; ViewContentWidgets.prototype.onDecorationsChanged = function (e) { // true for inline decorations that can end up relayouting text return true; }; ViewContentWidgets.prototype.onFlushed = function (e) { return true; }; ViewContentWidgets.prototype.onLineMappingChanged = function (e) { var keys = Object.keys(this._widgets); for (var i = 0, len = keys.length; i < len; i++) { var widgetId = keys[i]; this._widgets[widgetId].onLineMappingChanged(e); } return true; }; ViewContentWidgets.prototype.onLinesChanged = function (e) { return true; }; ViewContentWidgets.prototype.onLinesDeleted = function (e) { return true; }; ViewContentWidgets.prototype.onLinesInserted = function (e) { return true; }; ViewContentWidgets.prototype.onScrollChanged = function (e) { return true; }; ViewContentWidgets.prototype.onZonesChanged = function (e) { return true; }; // ---- end view event handlers ViewContentWidgets.prototype.addWidget = function (_widget) { var myWidget = new Widget(this._context, this._viewDomNode, _widget); this._widgets[myWidget.id] = myWidget; if (myWidget.allowEditorOverflow) { this.overflowingContentWidgetsDomNode.appendChild(myWidget.domNode); } else { this.domNode.appendChild(myWidget.domNode); } this.setShouldRender(); }; ViewContentWidgets.prototype.setWidgetPosition = function (widget, position, range, preference) { var myWidget = this._widgets[widget.getId()]; myWidget.setPosition(position, range, preference); this.setShouldRender(); }; ViewContentWidgets.prototype.removeWidget = function (widget) { var widgetId = widget.getId(); if (this._widgets.hasOwnProperty(widgetId)) { var myWidget = this._widgets[widgetId]; delete this._widgets[widgetId]; var domNode = myWidget.domNode.domNode; domNode.parentNode.removeChild(domNode); domNode.removeAttribute('monaco-visible-content-widget'); this.setShouldRender(); } }; ViewContentWidgets.prototype.shouldSuppressMouseDownOnWidget = function (widgetId) { if (this._widgets.hasOwnProperty(widgetId)) { return this._widgets[widgetId].suppressMouseDown; } return false; }; ViewContentWidgets.prototype.onBeforeRender = function (viewportData) { var keys = Object.keys(this._widgets); for (var i = 0, len = keys.length; i < len; i++) { var widgetId = keys[i]; this._widgets[widgetId].onBeforeRender(viewportData); } }; ViewContentWidgets.prototype.prepareRender = function (ctx) { var keys = Object.keys(this._widgets); for (var i = 0, len = keys.length; i < len; i++) { var widgetId = keys[i]; this._widgets[widgetId].prepareRender(ctx); } }; ViewContentWidgets.prototype.render = function (ctx) { var keys = Object.keys(this._widgets); for (var i = 0, len = keys.length; i < len; i++) { var widgetId = keys[i]; this._widgets[widgetId].render(ctx); } }; return ViewContentWidgets; }(ViewPart)); export { ViewContentWidgets }; var Widget = /** @class */ (function () { function Widget(context, viewDomNode, actual) { this._context = context; this._viewDomNode = viewDomNode; this._actual = actual; this.domNode = createFastDomNode(this._actual.getDomNode()); this.id = this._actual.getId(); this.allowEditorOverflow = this._actual.allowEditorOverflow || false; this.suppressMouseDown = this._actual.suppressMouseDown || false; this._fixedOverflowWidgets = this._context.configuration.editor.viewInfo.fixedOverflowWidgets; this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; this._lineHeight = this._context.configuration.editor.lineHeight; this._setPosition(null, null); this._preference = []; this._cachedDomNodeClientWidth = -1; this._cachedDomNodeClientHeight = -1; this._maxWidth = this._getMaxWidth(); this._isVisible = false; this._renderData = null; this.domNode.setPosition((this._fixedOverflowWidgets && this.allowEditorOverflow) ? 'fixed' : 'absolute'); this.domNode.setVisibility('hidden'); this.domNode.setAttribute('widgetId', this.id); this.domNode.setMaxWidth(this._maxWidth); } Widget.prototype.onConfigurationChanged = function (e) { if (e.lineHeight) { this._lineHeight = this._context.configuration.editor.lineHeight; } if (e.layoutInfo) { this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; this._maxWidth = this._getMaxWidth(); } }; Widget.prototype.onLineMappingChanged = function (e) { this._setPosition(this._position, this._range); }; Widget.prototype._setPosition = function (position, range) { this._position = position || null; this._range = range || null; this._viewPosition = null; this._viewRange = null; if (this._position) { // Do not trust that widgets give a valid position var validModelPosition = this._context.model.validateModelPosition(this._position); if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); } } if (this._range) { // Do not trust that widgets give a valid position var validModelRange = this._context.model.validateModelRange(this._range); this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange); } }; Widget.prototype._getMaxWidth = function () { return (this.allowEditorOverflow ? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth : this._contentWidth); }; Widget.prototype.setPosition = function (position, range, preference) { this._setPosition(position, range); this._preference = preference || null; this._cachedDomNodeClientWidth = -1; this._cachedDomNodeClientHeight = -1; }; Widget.prototype._layoutBoxInViewport = function (topLeft, bottomLeft, width, height, ctx) { // Our visible box is split horizontally by the current line => 2 boxes // a) the box above the line var aboveLineTop = topLeft.top; var heightAboveLine = aboveLineTop; // b) the box under the line var underLineTop = bottomLeft.top + this._lineHeight; var heightUnderLine = ctx.viewportHeight - underLineTop; var aboveTop = aboveLineTop - height; var fitsAbove = (heightAboveLine >= height); var belowTop = underLineTop; var fitsBelow = (heightUnderLine >= height); // And its left var actualAboveLeft = topLeft.left; var actualBelowLeft = bottomLeft.left; if (actualAboveLeft + width > ctx.scrollLeft + ctx.viewportWidth) { actualAboveLeft = ctx.scrollLeft + ctx.viewportWidth - width; } if (actualBelowLeft + width > ctx.scrollLeft + ctx.viewportWidth) { actualBelowLeft = ctx.scrollLeft + ctx.viewportWidth - width; } if (actualAboveLeft < ctx.scrollLeft) { actualAboveLeft = ctx.scrollLeft; } if (actualBelowLeft < ctx.scrollLeft) { actualBelowLeft = ctx.scrollLeft; } return { fitsAbove: fitsAbove, aboveTop: aboveTop, aboveLeft: actualAboveLeft, fitsBelow: fitsBelow, belowTop: belowTop, belowLeft: actualBelowLeft, }; }; Widget.prototype._layoutBoxInPage = function (topLeft, bottomLeft, width, height, ctx) { var aboveLeft0 = topLeft.left - ctx.scrollLeft; var belowLeft0 = bottomLeft.left - ctx.scrollLeft; if (aboveLeft0 < 0 || aboveLeft0 > this._contentWidth) { // Don't render if position is scrolled outside viewport return null; } var aboveTop = topLeft.top - height; var belowTop = bottomLeft.top + this._lineHeight; var aboveLeft = aboveLeft0 + this._contentLeft; var belowLeft = belowLeft0 + this._contentLeft; var domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); var absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; var absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; var absoluteAboveLeft = domNodePosition.left + aboveLeft - dom.StandardWindow.scrollX; var absoluteBelowLeft = domNodePosition.left + belowLeft - dom.StandardWindow.scrollX; var INNER_WIDTH = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var INNER_HEIGHT = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; // Leave some clearance to the bottom var TOP_PADDING = 22; var BOTTOM_PADDING = 22; var fitsAbove = (absoluteAboveTop >= TOP_PADDING), fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING); if (absoluteAboveLeft + width + 20 > INNER_WIDTH) { var delta = absoluteAboveLeft - (INNER_WIDTH - width - 20); absoluteAboveLeft -= delta; aboveLeft -= delta; } if (absoluteBelowLeft + width + 20 > INNER_WIDTH) { var delta = absoluteBelowLeft - (INNER_WIDTH - width - 20); absoluteBelowLeft -= delta; belowLeft -= delta; } if (absoluteAboveLeft < 0) { var delta = absoluteAboveLeft; absoluteAboveLeft -= delta; aboveLeft -= delta; } if (absoluteBelowLeft < 0) { var delta = absoluteBelowLeft; absoluteBelowLeft -= delta; belowLeft -= delta; } if (this._fixedOverflowWidgets) { aboveTop = absoluteAboveTop; belowTop = absoluteBelowTop; aboveLeft = absoluteAboveLeft; belowLeft = absoluteBelowLeft; } return { fitsAbove: fitsAbove, aboveTop: aboveTop, aboveLeft: aboveLeft, fitsBelow: fitsBelow, belowTop: belowTop, belowLeft: belowLeft }; }; Widget.prototype._prepareRenderWidgetAtExactPositionOverflowing = function (topLeft) { return new Coordinate(topLeft.top, topLeft.left + this._contentLeft); }; /** * Compute `this._topLeft` */ Widget.prototype._getTopAndBottomLeft = function (ctx) { if (!this._viewPosition) { return [null, null]; } var visibleRangeForPosition = ctx.visibleRangeForPosition(this._viewPosition); if (!visibleRangeForPosition) { return [null, null]; } var topForPosition = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop; var topLeft = new Coordinate(topForPosition, visibleRangeForPosition.left); var largestLineNumber = this._viewPosition.lineNumber; var smallestLeft = visibleRangeForPosition.left; if (this._viewRange) { var visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false); if (visibleRangesForRange && visibleRangesForRange.length > 0) { for (var i = visibleRangesForRange.length - 1; i >= 0; i--) { var visibleRangesForLine = visibleRangesForRange[i]; if (visibleRangesForLine.lineNumber >= largestLineNumber) { if (visibleRangesForLine.lineNumber > largestLineNumber) { largestLineNumber = visibleRangesForLine.lineNumber; smallestLeft = 1073741824 /* MAX_SAFE_SMALL_INTEGER */; } for (var j = 0, lenJ = visibleRangesForLine.ranges.length; j < lenJ; j++) { var visibleRange = visibleRangesForLine.ranges[j]; if (visibleRange.left < smallestLeft) { smallestLeft = visibleRange.left; } } } } } } var topForBottomLine = ctx.getVerticalOffsetForLineNumber(largestLineNumber) - ctx.scrollTop; var bottomLeft = new Coordinate(topForBottomLine, smallestLeft); return [topLeft, bottomLeft]; }; Widget.prototype._prepareRenderWidget = function (ctx) { var _a = this._getTopAndBottomLeft(ctx), topLeft = _a[0], bottomLeft = _a[1]; if (!topLeft || !bottomLeft) { return null; } if (this._cachedDomNodeClientWidth === -1 || this._cachedDomNodeClientHeight === -1) { var domNode = this.domNode.domNode; this._cachedDomNodeClientWidth = domNode.clientWidth; this._cachedDomNodeClientHeight = domNode.clientHeight; } var placement; if (this.allowEditorOverflow) { placement = this._layoutBoxInPage(topLeft, bottomLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx); } else { placement = this._layoutBoxInViewport(topLeft, bottomLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx); } // Do two passes, first for perfect fit, second picks first option if (this._preference) { for (var pass = 1; pass <= 2; pass++) { for (var i = 0; i < this._preference.length; i++) { // placement var pref = this._preference[i]; if (pref === 1 /* ABOVE */) { if (!placement) { // Widget outside of viewport return null; } if (pass === 2 || placement.fitsAbove) { return new Coordinate(placement.aboveTop, placement.aboveLeft); } } else if (pref === 2 /* BELOW */) { if (!placement) { // Widget outside of viewport return null; } if (pass === 2 || placement.fitsBelow) { return new Coordinate(placement.belowTop, placement.belowLeft); } } else { if (this.allowEditorOverflow) { return this._prepareRenderWidgetAtExactPositionOverflowing(topLeft); } else { return topLeft; } } } } } return null; }; /** * On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly. */ Widget.prototype.onBeforeRender = function (viewportData) { if (!this._viewPosition || !this._preference) { return; } if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) { // Outside of viewport return; } this.domNode.setMaxWidth(this._maxWidth); }; Widget.prototype.prepareRender = function (ctx) { this._renderData = this._prepareRenderWidget(ctx); }; Widget.prototype.render = function (ctx) { if (!this._renderData) { // This widget should be invisible if (this._isVisible) { this.domNode.removeAttribute('monaco-visible-content-widget'); this._isVisible = false; this.domNode.setVisibility('hidden'); } return; } // This widget should be visible if (this.allowEditorOverflow) { this.domNode.setTop(this._renderData.top); this.domNode.setLeft(this._renderData.left); } else { this.domNode.setTop(this._renderData.top + ctx.scrollTop - ctx.bigNumbersDelta); this.domNode.setLeft(this._renderData.left); } if (!this._isVisible) { this.domNode.setVisibility('inherit'); this.domNode.setAttribute('monaco-visible-content-widget', 'true'); this._isVisible = true; } }; return Widget; }());