monaco-editor
Version:
A browser based code editor
459 lines (458 loc) • 20.7 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createFastDomNode } from '../../../base/browser/fastDomNode.js';
import { createStringBuilder } from '../../common/core/stringBuilder.js';
var RenderedLinesCollection = /** @class */ (function () {
function RenderedLinesCollection(createLine) {
this._createLine = createLine;
this._set(1, []);
}
RenderedLinesCollection.prototype.flush = function () {
this._set(1, []);
};
RenderedLinesCollection.prototype._set = function (rendLineNumberStart, lines) {
this._lines = lines;
this._rendLineNumberStart = rendLineNumberStart;
};
RenderedLinesCollection.prototype._get = function () {
return {
rendLineNumberStart: this._rendLineNumberStart,
lines: this._lines
};
};
/**
* @returns Inclusive line number that is inside this collection
*/
RenderedLinesCollection.prototype.getStartLineNumber = function () {
return this._rendLineNumberStart;
};
/**
* @returns Inclusive line number that is inside this collection
*/
RenderedLinesCollection.prototype.getEndLineNumber = function () {
return this._rendLineNumberStart + this._lines.length - 1;
};
RenderedLinesCollection.prototype.getCount = function () {
return this._lines.length;
};
RenderedLinesCollection.prototype.getLine = function (lineNumber) {
var lineIndex = lineNumber - this._rendLineNumberStart;
if (lineIndex < 0 || lineIndex >= this._lines.length) {
throw new Error('Illegal value for lineNumber');
}
return this._lines[lineIndex];
};
/**
* @returns Lines that were removed from this collection
*/
RenderedLinesCollection.prototype.onLinesDeleted = function (deleteFromLineNumber, deleteToLineNumber) {
if (this.getCount() === 0) {
// no lines
return null;
}
var startLineNumber = this.getStartLineNumber();
var endLineNumber = this.getEndLineNumber();
if (deleteToLineNumber < startLineNumber) {
// deleting above the viewport
var deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1;
this._rendLineNumberStart -= deleteCnt;
return null;
}
if (deleteFromLineNumber > endLineNumber) {
// deleted below the viewport
return null;
}
// Record what needs to be deleted
var deleteStartIndex = 0;
var deleteCount = 0;
for (var lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
var lineIndex = lineNumber - this._rendLineNumberStart;
if (deleteFromLineNumber <= lineNumber && lineNumber <= deleteToLineNumber) {
// this is a line to be deleted
if (deleteCount === 0) {
// this is the first line to be deleted
deleteStartIndex = lineIndex;
deleteCount = 1;
}
else {
deleteCount++;
}
}
}
// Adjust this._rendLineNumberStart for lines deleted above
if (deleteFromLineNumber < startLineNumber) {
// Something was deleted above
var deleteAboveCount = 0;
if (deleteToLineNumber < startLineNumber) {
// the entire deleted lines are above
deleteAboveCount = deleteToLineNumber - deleteFromLineNumber + 1;
}
else {
deleteAboveCount = startLineNumber - deleteFromLineNumber;
}
this._rendLineNumberStart -= deleteAboveCount;
}
var deleted = this._lines.splice(deleteStartIndex, deleteCount);
return deleted;
};
RenderedLinesCollection.prototype.onLinesChanged = function (changeFromLineNumber, changeToLineNumber) {
if (this.getCount() === 0) {
// no lines
return false;
}
var startLineNumber = this.getStartLineNumber();
var endLineNumber = this.getEndLineNumber();
var someoneNotified = false;
for (var changedLineNumber = changeFromLineNumber; changedLineNumber <= changeToLineNumber; changedLineNumber++) {
if (changedLineNumber >= startLineNumber && changedLineNumber <= endLineNumber) {
// Notify the line
this._lines[changedLineNumber - this._rendLineNumberStart].onContentChanged();
someoneNotified = true;
}
}
return someoneNotified;
};
RenderedLinesCollection.prototype.onLinesInserted = function (insertFromLineNumber, insertToLineNumber) {
if (this.getCount() === 0) {
// no lines
return null;
}
var insertCnt = insertToLineNumber - insertFromLineNumber + 1;
var startLineNumber = this.getStartLineNumber();
var endLineNumber = this.getEndLineNumber();
if (insertFromLineNumber <= startLineNumber) {
// inserting above the viewport
this._rendLineNumberStart += insertCnt;
return null;
}
if (insertFromLineNumber > endLineNumber) {
// inserting below the viewport
return null;
}
if (insertCnt + insertFromLineNumber > endLineNumber) {
// insert inside the viewport in such a way that all remaining lines are pushed outside
var deleted = this._lines.splice(insertFromLineNumber - this._rendLineNumberStart, endLineNumber - insertFromLineNumber + 1);
return deleted;
}
// insert inside the viewport, push out some lines, but not all remaining lines
var newLines = [];
for (var i = 0; i < insertCnt; i++) {
newLines[i] = this._createLine();
}
var insertIndex = insertFromLineNumber - this._rendLineNumberStart;
var beforeLines = this._lines.slice(0, insertIndex);
var afterLines = this._lines.slice(insertIndex, this._lines.length - insertCnt);
var deletedLines = this._lines.slice(this._lines.length - insertCnt, this._lines.length);
this._lines = beforeLines.concat(newLines).concat(afterLines);
return deletedLines;
};
RenderedLinesCollection.prototype.onTokensChanged = function (ranges) {
if (this.getCount() === 0) {
// no lines
return false;
}
var startLineNumber = this.getStartLineNumber();
var endLineNumber = this.getEndLineNumber();
var notifiedSomeone = false;
for (var i = 0, len = ranges.length; i < len; i++) {
var rng = ranges[i];
if (rng.toLineNumber < startLineNumber || rng.fromLineNumber > endLineNumber) {
// range outside viewport
continue;
}
var from = Math.max(startLineNumber, rng.fromLineNumber);
var to = Math.min(endLineNumber, rng.toLineNumber);
for (var lineNumber = from; lineNumber <= to; lineNumber++) {
var lineIndex = lineNumber - this._rendLineNumberStart;
this._lines[lineIndex].onTokensChanged();
notifiedSomeone = true;
}
}
return notifiedSomeone;
};
return RenderedLinesCollection;
}());
export { RenderedLinesCollection };
var VisibleLinesCollection = /** @class */ (function () {
function VisibleLinesCollection(host) {
var _this = this;
this._host = host;
this.domNode = this._createDomNode();
this._linesCollection = new RenderedLinesCollection(function () { return _this._host.createVisibleLine(); });
}
VisibleLinesCollection.prototype._createDomNode = function () {
var domNode = createFastDomNode(document.createElement('div'));
domNode.setClassName('view-layer');
domNode.setPosition('absolute');
domNode.domNode.setAttribute('role', 'presentation');
domNode.domNode.setAttribute('aria-hidden', 'true');
return domNode;
};
// ---- begin view event handlers
VisibleLinesCollection.prototype.onConfigurationChanged = function (e) {
return e.layoutInfo;
};
VisibleLinesCollection.prototype.onFlushed = function (e) {
this._linesCollection.flush();
// No need to clear the dom node because a full .innerHTML will occur in ViewLayerRenderer._render
return true;
};
VisibleLinesCollection.prototype.onLinesChanged = function (e) {
return this._linesCollection.onLinesChanged(e.fromLineNumber, e.toLineNumber);
};
VisibleLinesCollection.prototype.onLinesDeleted = function (e) {
var deleted = this._linesCollection.onLinesDeleted(e.fromLineNumber, e.toLineNumber);
if (deleted) {
// Remove from DOM
for (var i = 0, len = deleted.length; i < len; i++) {
var lineDomNode = deleted[i].getDomNode();
if (lineDomNode) {
this.domNode.domNode.removeChild(lineDomNode);
}
}
}
return true;
};
VisibleLinesCollection.prototype.onLinesInserted = function (e) {
var deleted = this._linesCollection.onLinesInserted(e.fromLineNumber, e.toLineNumber);
if (deleted) {
// Remove from DOM
for (var i = 0, len = deleted.length; i < len; i++) {
var lineDomNode = deleted[i].getDomNode();
if (lineDomNode) {
this.domNode.domNode.removeChild(lineDomNode);
}
}
}
return true;
};
VisibleLinesCollection.prototype.onScrollChanged = function (e) {
return e.scrollTopChanged;
};
VisibleLinesCollection.prototype.onTokensChanged = function (e) {
return this._linesCollection.onTokensChanged(e.ranges);
};
VisibleLinesCollection.prototype.onZonesChanged = function (e) {
return true;
};
// ---- end view event handlers
VisibleLinesCollection.prototype.getStartLineNumber = function () {
return this._linesCollection.getStartLineNumber();
};
VisibleLinesCollection.prototype.getEndLineNumber = function () {
return this._linesCollection.getEndLineNumber();
};
VisibleLinesCollection.prototype.getVisibleLine = function (lineNumber) {
return this._linesCollection.getLine(lineNumber);
};
VisibleLinesCollection.prototype.renderLines = function (viewportData) {
var inp = this._linesCollection._get();
var renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData);
var ctx = {
rendLineNumberStart: inp.rendLineNumberStart,
lines: inp.lines,
linesLength: inp.lines.length
};
// Decide if this render will do a single update (single large .innerHTML) or many updates (inserting/removing dom nodes)
var resCtx = renderer.render(ctx, viewportData.startLineNumber, viewportData.endLineNumber, viewportData.relativeVerticalOffset);
this._linesCollection._set(resCtx.rendLineNumberStart, resCtx.lines);
};
return VisibleLinesCollection;
}());
export { VisibleLinesCollection };
var ViewLayerRenderer = /** @class */ (function () {
function ViewLayerRenderer(domNode, host, viewportData) {
this.domNode = domNode;
this.host = host;
this.viewportData = viewportData;
}
ViewLayerRenderer.prototype.render = function (inContext, startLineNumber, stopLineNumber, deltaTop) {
var ctx = {
rendLineNumberStart: inContext.rendLineNumberStart,
lines: inContext.lines.slice(0),
linesLength: inContext.linesLength
};
if ((ctx.rendLineNumberStart + ctx.linesLength - 1 < startLineNumber) || (stopLineNumber < ctx.rendLineNumberStart)) {
// There is no overlap whatsoever
ctx.rendLineNumberStart = startLineNumber;
ctx.linesLength = stopLineNumber - startLineNumber + 1;
ctx.lines = [];
for (var x = startLineNumber; x <= stopLineNumber; x++) {
ctx.lines[x - startLineNumber] = this.host.createVisibleLine();
}
this._finishRendering(ctx, true, deltaTop);
return ctx;
}
// Update lines which will remain untouched
this._renderUntouchedLines(ctx, Math.max(startLineNumber - ctx.rendLineNumberStart, 0), Math.min(stopLineNumber - ctx.rendLineNumberStart, ctx.linesLength - 1), deltaTop, startLineNumber);
if (ctx.rendLineNumberStart > startLineNumber) {
// Insert lines before
var fromLineNumber = startLineNumber;
var toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1);
if (fromLineNumber <= toLineNumber) {
this._insertLinesBefore(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber);
ctx.linesLength += toLineNumber - fromLineNumber + 1;
}
}
else if (ctx.rendLineNumberStart < startLineNumber) {
// Remove lines before
var removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart);
if (removeCnt > 0) {
this._removeLinesBefore(ctx, removeCnt);
ctx.linesLength -= removeCnt;
}
}
ctx.rendLineNumberStart = startLineNumber;
if (ctx.rendLineNumberStart + ctx.linesLength - 1 < stopLineNumber) {
// Insert lines after
var fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength;
var toLineNumber = stopLineNumber;
if (fromLineNumber <= toLineNumber) {
this._insertLinesAfter(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber);
ctx.linesLength += toLineNumber - fromLineNumber + 1;
}
}
else if (ctx.rendLineNumberStart + ctx.linesLength - 1 > stopLineNumber) {
// Remove lines after
var fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1);
var toLineNumber = ctx.linesLength - 1;
var removeCnt = toLineNumber - fromLineNumber + 1;
if (removeCnt > 0) {
this._removeLinesAfter(ctx, removeCnt);
ctx.linesLength -= removeCnt;
}
}
this._finishRendering(ctx, false, deltaTop);
return ctx;
};
ViewLayerRenderer.prototype._renderUntouchedLines = function (ctx, startIndex, endIndex, deltaTop, deltaLN) {
var rendLineNumberStart = ctx.rendLineNumberStart;
var lines = ctx.lines;
for (var i = startIndex; i <= endIndex; i++) {
var lineNumber = rendLineNumberStart + i;
lines[i].layoutLine(lineNumber, deltaTop[lineNumber - deltaLN]);
}
};
ViewLayerRenderer.prototype._insertLinesBefore = function (ctx, fromLineNumber, toLineNumber, deltaTop, deltaLN) {
var newLines = [];
var newLinesLen = 0;
for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
newLines[newLinesLen++] = this.host.createVisibleLine();
}
ctx.lines = newLines.concat(ctx.lines);
};
ViewLayerRenderer.prototype._removeLinesBefore = function (ctx, removeCount) {
for (var i = 0; i < removeCount; i++) {
var lineDomNode = ctx.lines[i].getDomNode();
if (lineDomNode) {
this.domNode.removeChild(lineDomNode);
}
}
ctx.lines.splice(0, removeCount);
};
ViewLayerRenderer.prototype._insertLinesAfter = function (ctx, fromLineNumber, toLineNumber, deltaTop, deltaLN) {
var newLines = [];
var newLinesLen = 0;
for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
newLines[newLinesLen++] = this.host.createVisibleLine();
}
ctx.lines = ctx.lines.concat(newLines);
};
ViewLayerRenderer.prototype._removeLinesAfter = function (ctx, removeCount) {
var removeIndex = ctx.linesLength - removeCount;
for (var i = 0; i < removeCount; i++) {
var lineDomNode = ctx.lines[removeIndex + i].getDomNode();
if (lineDomNode) {
this.domNode.removeChild(lineDomNode);
}
}
ctx.lines.splice(removeIndex, removeCount);
};
ViewLayerRenderer.prototype._finishRenderingNewLines = function (ctx, domNodeIsEmpty, newLinesHTML, wasNew) {
var lastChild = this.domNode.lastChild;
if (domNodeIsEmpty || !lastChild) {
this.domNode.innerHTML = newLinesHTML;
}
else {
lastChild.insertAdjacentHTML('afterend', newLinesHTML);
}
var currChild = this.domNode.lastChild;
for (var i = ctx.linesLength - 1; i >= 0; i--) {
var line = ctx.lines[i];
if (wasNew[i]) {
line.setDomNode(currChild);
currChild = currChild.previousSibling;
}
}
};
ViewLayerRenderer.prototype._finishRenderingInvalidLines = function (ctx, invalidLinesHTML, wasInvalid) {
var hugeDomNode = document.createElement('div');
hugeDomNode.innerHTML = invalidLinesHTML;
for (var i = 0; i < ctx.linesLength; i++) {
var line = ctx.lines[i];
if (wasInvalid[i]) {
var source = hugeDomNode.firstChild;
var lineDomNode = line.getDomNode();
lineDomNode.parentNode.replaceChild(source, lineDomNode);
line.setDomNode(source);
}
}
};
ViewLayerRenderer.prototype._finishRendering = function (ctx, domNodeIsEmpty, deltaTop) {
var sb = ViewLayerRenderer._sb;
var linesLength = ctx.linesLength;
var lines = ctx.lines;
var rendLineNumberStart = ctx.rendLineNumberStart;
var wasNew = [];
{
sb.reset();
var hadNewLine = false;
for (var i = 0; i < linesLength; i++) {
var line = lines[i];
wasNew[i] = false;
var lineDomNode = line.getDomNode();
if (lineDomNode) {
// line is not new
continue;
}
var renderResult = line.renderLine(i + rendLineNumberStart, deltaTop[i], this.viewportData, sb);
if (!renderResult) {
// line does not need rendering
continue;
}
wasNew[i] = true;
hadNewLine = true;
}
if (hadNewLine) {
this._finishRenderingNewLines(ctx, domNodeIsEmpty, sb.build(), wasNew);
}
}
{
sb.reset();
var hadInvalidLine = false;
var wasInvalid = [];
for (var i = 0; i < linesLength; i++) {
var line = lines[i];
wasInvalid[i] = false;
if (wasNew[i]) {
// line was new
continue;
}
var renderResult = line.renderLine(i + rendLineNumberStart, deltaTop[i], this.viewportData, sb);
if (!renderResult) {
// line does not need rendering
continue;
}
wasInvalid[i] = true;
hadInvalidLine = true;
}
if (hadInvalidLine) {
this._finishRenderingInvalidLines(ctx, sb.build(), wasInvalid);
}
}
};
ViewLayerRenderer._sb = createStringBuilder(100000);
return ViewLayerRenderer;
}());