123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var _a;
- import { createFastDomNode } from '../../../base/browser/fastDomNode.js';
- import { createStringBuilder } from '../../common/core/stringBuilder.js';
- export class RenderedLinesCollection {
- constructor(createLine) {
- this._createLine = createLine;
- this._set(1, []);
- }
- flush() {
- this._set(1, []);
- }
- _set(rendLineNumberStart, lines) {
- this._lines = lines;
- this._rendLineNumberStart = rendLineNumberStart;
- }
- _get() {
- return {
- rendLineNumberStart: this._rendLineNumberStart,
- lines: this._lines
- };
- }
- /**
- * @returns Inclusive line number that is inside this collection
- */
- getStartLineNumber() {
- return this._rendLineNumberStart;
- }
- /**
- * @returns Inclusive line number that is inside this collection
- */
- getEndLineNumber() {
- return this._rendLineNumberStart + this._lines.length - 1;
- }
- getCount() {
- return this._lines.length;
- }
- getLine(lineNumber) {
- const 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
- */
- onLinesDeleted(deleteFromLineNumber, deleteToLineNumber) {
- if (this.getCount() === 0) {
- // no lines
- return null;
- }
- const startLineNumber = this.getStartLineNumber();
- const endLineNumber = this.getEndLineNumber();
- if (deleteToLineNumber < startLineNumber) {
- // deleting above the viewport
- const deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1;
- this._rendLineNumberStart -= deleteCnt;
- return null;
- }
- if (deleteFromLineNumber > endLineNumber) {
- // deleted below the viewport
- return null;
- }
- // Record what needs to be deleted
- let deleteStartIndex = 0;
- let deleteCount = 0;
- for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
- const 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
- let deleteAboveCount = 0;
- if (deleteToLineNumber < startLineNumber) {
- // the entire deleted lines are above
- deleteAboveCount = deleteToLineNumber - deleteFromLineNumber + 1;
- }
- else {
- deleteAboveCount = startLineNumber - deleteFromLineNumber;
- }
- this._rendLineNumberStart -= deleteAboveCount;
- }
- const deleted = this._lines.splice(deleteStartIndex, deleteCount);
- return deleted;
- }
- onLinesChanged(changeFromLineNumber, changeToLineNumber) {
- if (this.getCount() === 0) {
- // no lines
- return false;
- }
- const startLineNumber = this.getStartLineNumber();
- const endLineNumber = this.getEndLineNumber();
- let someoneNotified = false;
- for (let changedLineNumber = changeFromLineNumber; changedLineNumber <= changeToLineNumber; changedLineNumber++) {
- if (changedLineNumber >= startLineNumber && changedLineNumber <= endLineNumber) {
- // Notify the line
- this._lines[changedLineNumber - this._rendLineNumberStart].onContentChanged();
- someoneNotified = true;
- }
- }
- return someoneNotified;
- }
- onLinesInserted(insertFromLineNumber, insertToLineNumber) {
- if (this.getCount() === 0) {
- // no lines
- return null;
- }
- const insertCnt = insertToLineNumber - insertFromLineNumber + 1;
- const startLineNumber = this.getStartLineNumber();
- const 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
- const 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
- const newLines = [];
- for (let i = 0; i < insertCnt; i++) {
- newLines[i] = this._createLine();
- }
- const insertIndex = insertFromLineNumber - this._rendLineNumberStart;
- const beforeLines = this._lines.slice(0, insertIndex);
- const afterLines = this._lines.slice(insertIndex, this._lines.length - insertCnt);
- const deletedLines = this._lines.slice(this._lines.length - insertCnt, this._lines.length);
- this._lines = beforeLines.concat(newLines).concat(afterLines);
- return deletedLines;
- }
- onTokensChanged(ranges) {
- if (this.getCount() === 0) {
- // no lines
- return false;
- }
- const startLineNumber = this.getStartLineNumber();
- const endLineNumber = this.getEndLineNumber();
- let notifiedSomeone = false;
- for (let i = 0, len = ranges.length; i < len; i++) {
- const rng = ranges[i];
- if (rng.toLineNumber < startLineNumber || rng.fromLineNumber > endLineNumber) {
- // range outside viewport
- continue;
- }
- const from = Math.max(startLineNumber, rng.fromLineNumber);
- const to = Math.min(endLineNumber, rng.toLineNumber);
- for (let lineNumber = from; lineNumber <= to; lineNumber++) {
- const lineIndex = lineNumber - this._rendLineNumberStart;
- this._lines[lineIndex].onTokensChanged();
- notifiedSomeone = true;
- }
- }
- return notifiedSomeone;
- }
- }
- export class VisibleLinesCollection {
- constructor(host) {
- this._host = host;
- this.domNode = this._createDomNode();
- this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine());
- }
- _createDomNode() {
- const 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
- onConfigurationChanged(e) {
- if (e.hasChanged(130 /* layoutInfo */)) {
- return true;
- }
- return false;
- }
- onFlushed(e) {
- this._linesCollection.flush();
- // No need to clear the dom node because a full .innerHTML will occur in ViewLayerRenderer._render
- return true;
- }
- onLinesChanged(e) {
- return this._linesCollection.onLinesChanged(e.fromLineNumber, e.toLineNumber);
- }
- onLinesDeleted(e) {
- const deleted = this._linesCollection.onLinesDeleted(e.fromLineNumber, e.toLineNumber);
- if (deleted) {
- // Remove from DOM
- for (let i = 0, len = deleted.length; i < len; i++) {
- const lineDomNode = deleted[i].getDomNode();
- if (lineDomNode) {
- this.domNode.domNode.removeChild(lineDomNode);
- }
- }
- }
- return true;
- }
- onLinesInserted(e) {
- const deleted = this._linesCollection.onLinesInserted(e.fromLineNumber, e.toLineNumber);
- if (deleted) {
- // Remove from DOM
- for (let i = 0, len = deleted.length; i < len; i++) {
- const lineDomNode = deleted[i].getDomNode();
- if (lineDomNode) {
- this.domNode.domNode.removeChild(lineDomNode);
- }
- }
- }
- return true;
- }
- onScrollChanged(e) {
- return e.scrollTopChanged;
- }
- onTokensChanged(e) {
- return this._linesCollection.onTokensChanged(e.ranges);
- }
- onZonesChanged(e) {
- return true;
- }
- // ---- end view event handlers
- getStartLineNumber() {
- return this._linesCollection.getStartLineNumber();
- }
- getEndLineNumber() {
- return this._linesCollection.getEndLineNumber();
- }
- getVisibleLine(lineNumber) {
- return this._linesCollection.getLine(lineNumber);
- }
- renderLines(viewportData) {
- const inp = this._linesCollection._get();
- const renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData);
- const 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)
- const resCtx = renderer.render(ctx, viewportData.startLineNumber, viewportData.endLineNumber, viewportData.relativeVerticalOffset);
- this._linesCollection._set(resCtx.rendLineNumberStart, resCtx.lines);
- }
- }
- class ViewLayerRenderer {
- constructor(domNode, host, viewportData) {
- this.domNode = domNode;
- this.host = host;
- this.viewportData = viewportData;
- }
- render(inContext, startLineNumber, stopLineNumber, deltaTop) {
- const 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 (let 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
- const fromLineNumber = startLineNumber;
- const 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
- const 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
- const fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength;
- const 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
- const fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1);
- const toLineNumber = ctx.linesLength - 1;
- const removeCnt = toLineNumber - fromLineNumber + 1;
- if (removeCnt > 0) {
- this._removeLinesAfter(ctx, removeCnt);
- ctx.linesLength -= removeCnt;
- }
- }
- this._finishRendering(ctx, false, deltaTop);
- return ctx;
- }
- _renderUntouchedLines(ctx, startIndex, endIndex, deltaTop, deltaLN) {
- const rendLineNumberStart = ctx.rendLineNumberStart;
- const lines = ctx.lines;
- for (let i = startIndex; i <= endIndex; i++) {
- const lineNumber = rendLineNumberStart + i;
- lines[i].layoutLine(lineNumber, deltaTop[lineNumber - deltaLN]);
- }
- }
- _insertLinesBefore(ctx, fromLineNumber, toLineNumber, deltaTop, deltaLN) {
- const newLines = [];
- let newLinesLen = 0;
- for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
- newLines[newLinesLen++] = this.host.createVisibleLine();
- }
- ctx.lines = newLines.concat(ctx.lines);
- }
- _removeLinesBefore(ctx, removeCount) {
- for (let i = 0; i < removeCount; i++) {
- const lineDomNode = ctx.lines[i].getDomNode();
- if (lineDomNode) {
- this.domNode.removeChild(lineDomNode);
- }
- }
- ctx.lines.splice(0, removeCount);
- }
- _insertLinesAfter(ctx, fromLineNumber, toLineNumber, deltaTop, deltaLN) {
- const newLines = [];
- let newLinesLen = 0;
- for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
- newLines[newLinesLen++] = this.host.createVisibleLine();
- }
- ctx.lines = ctx.lines.concat(newLines);
- }
- _removeLinesAfter(ctx, removeCount) {
- const removeIndex = ctx.linesLength - removeCount;
- for (let i = 0; i < removeCount; i++) {
- const lineDomNode = ctx.lines[removeIndex + i].getDomNode();
- if (lineDomNode) {
- this.domNode.removeChild(lineDomNode);
- }
- }
- ctx.lines.splice(removeIndex, removeCount);
- }
- _finishRenderingNewLines(ctx, domNodeIsEmpty, newLinesHTML, wasNew) {
- if (ViewLayerRenderer._ttPolicy) {
- newLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(newLinesHTML);
- }
- const lastChild = this.domNode.lastChild;
- if (domNodeIsEmpty || !lastChild) {
- this.domNode.innerHTML = newLinesHTML; // explains the ugly casts -> https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393;
- }
- else {
- lastChild.insertAdjacentHTML('afterend', newLinesHTML);
- }
- let currChild = this.domNode.lastChild;
- for (let i = ctx.linesLength - 1; i >= 0; i--) {
- const line = ctx.lines[i];
- if (wasNew[i]) {
- line.setDomNode(currChild);
- currChild = currChild.previousSibling;
- }
- }
- }
- _finishRenderingInvalidLines(ctx, invalidLinesHTML, wasInvalid) {
- const hugeDomNode = document.createElement('div');
- if (ViewLayerRenderer._ttPolicy) {
- invalidLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(invalidLinesHTML);
- }
- hugeDomNode.innerHTML = invalidLinesHTML;
- for (let i = 0; i < ctx.linesLength; i++) {
- const line = ctx.lines[i];
- if (wasInvalid[i]) {
- const source = hugeDomNode.firstChild;
- const lineDomNode = line.getDomNode();
- lineDomNode.parentNode.replaceChild(source, lineDomNode);
- line.setDomNode(source);
- }
- }
- }
- _finishRendering(ctx, domNodeIsEmpty, deltaTop) {
- const sb = ViewLayerRenderer._sb;
- const linesLength = ctx.linesLength;
- const lines = ctx.lines;
- const rendLineNumberStart = ctx.rendLineNumberStart;
- const wasNew = [];
- {
- sb.reset();
- let hadNewLine = false;
- for (let i = 0; i < linesLength; i++) {
- const line = lines[i];
- wasNew[i] = false;
- const lineDomNode = line.getDomNode();
- if (lineDomNode) {
- // line is not new
- continue;
- }
- const 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();
- let hadInvalidLine = false;
- const wasInvalid = [];
- for (let i = 0; i < linesLength; i++) {
- const line = lines[i];
- wasInvalid[i] = false;
- if (wasNew[i]) {
- // line was new
- continue;
- }
- const 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._ttPolicy = (_a = window.trustedTypes) === null || _a === void 0 ? void 0 : _a.createPolicy('editorViewLayer', { createHTML: value => value });
- ViewLayerRenderer._sb = createStringBuilder(100000);
|