monaco-editor
Version:
A browser based code editor
671 lines (670 loc) • 31 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 * as strings from '../../../base/common/strings.js';
import { createStringBuilder } from '../core/stringBuilder.js';
import { LineDecoration, LineDecorationsNormalizer } from './lineDecorations.js';
var LinePart = /** @class */ (function () {
function LinePart(endIndex, type) {
this.endIndex = endIndex;
this.type = type;
}
return LinePart;
}());
var RenderLineInput = /** @class */ (function () {
function RenderLineInput(useMonospaceOptimizations, canUseHalfwidthRightwardsArrow, lineContent, continuesWithWrappedLine, isBasicASCII, containsRTL, fauxIndentLength, lineTokens, lineDecorations, tabSize, spaceWidth, stopRenderingLineAfter, renderWhitespace, renderControlCharacters, fontLigatures) {
this.useMonospaceOptimizations = useMonospaceOptimizations;
this.canUseHalfwidthRightwardsArrow = canUseHalfwidthRightwardsArrow;
this.lineContent = lineContent;
this.continuesWithWrappedLine = continuesWithWrappedLine;
this.isBasicASCII = isBasicASCII;
this.containsRTL = containsRTL;
this.fauxIndentLength = fauxIndentLength;
this.lineTokens = lineTokens;
this.lineDecorations = lineDecorations;
this.tabSize = tabSize;
this.spaceWidth = spaceWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
this.renderWhitespace = (renderWhitespace === 'all'
? 2 /* All */
: renderWhitespace === 'boundary'
? 1 /* Boundary */
: 0 /* None */);
this.renderControlCharacters = renderControlCharacters;
this.fontLigatures = fontLigatures;
}
RenderLineInput.prototype.equals = function (other) {
return (this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.lineContent === other.lineContent
&& this.continuesWithWrappedLine === other.continuesWithWrappedLine
&& this.isBasicASCII === other.isBasicASCII
&& this.containsRTL === other.containsRTL
&& this.fauxIndentLength === other.fauxIndentLength
&& this.tabSize === other.tabSize
&& this.spaceWidth === other.spaceWidth
&& this.stopRenderingLineAfter === other.stopRenderingLineAfter
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
&& this.fontLigatures === other.fontLigatures
&& LineDecoration.equalsArr(this.lineDecorations, other.lineDecorations)
&& this.lineTokens.equals(other.lineTokens));
};
return RenderLineInput;
}());
export { RenderLineInput };
/**
* Provides a both direction mapping between a line's character and its rendered position.
*/
var CharacterMapping = /** @class */ (function () {
function CharacterMapping(length, partCount) {
this.length = length;
this._data = new Uint32Array(this.length);
this._absoluteOffsets = new Uint32Array(this.length);
}
CharacterMapping.getPartIndex = function (partData) {
return (partData & 4294901760 /* PART_INDEX_MASK */) >>> 16 /* PART_INDEX_OFFSET */;
};
CharacterMapping.getCharIndex = function (partData) {
return (partData & 65535 /* CHAR_INDEX_MASK */) >>> 0 /* CHAR_INDEX_OFFSET */;
};
CharacterMapping.prototype.setPartData = function (charOffset, partIndex, charIndex, partAbsoluteOffset) {
var partData = ((partIndex << 16 /* PART_INDEX_OFFSET */)
| (charIndex << 0 /* CHAR_INDEX_OFFSET */)) >>> 0;
this._data[charOffset] = partData;
this._absoluteOffsets[charOffset] = partAbsoluteOffset + charIndex;
};
CharacterMapping.prototype.getAbsoluteOffsets = function () {
return this._absoluteOffsets;
};
CharacterMapping.prototype.charOffsetToPartData = function (charOffset) {
if (this.length === 0) {
return 0;
}
if (charOffset < 0) {
return this._data[0];
}
if (charOffset >= this.length) {
return this._data[this.length - 1];
}
return this._data[charOffset];
};
CharacterMapping.prototype.partDataToCharOffset = function (partIndex, partLength, charIndex) {
if (this.length === 0) {
return 0;
}
var searchEntry = ((partIndex << 16 /* PART_INDEX_OFFSET */)
| (charIndex << 0 /* CHAR_INDEX_OFFSET */)) >>> 0;
var min = 0;
var max = this.length - 1;
while (min + 1 < max) {
var mid = ((min + max) >>> 1);
var midEntry = this._data[mid];
if (midEntry === searchEntry) {
return mid;
}
else if (midEntry > searchEntry) {
max = mid;
}
else {
min = mid;
}
}
if (min === max) {
return min;
}
var minEntry = this._data[min];
var maxEntry = this._data[max];
if (minEntry === searchEntry) {
return min;
}
if (maxEntry === searchEntry) {
return max;
}
var minPartIndex = CharacterMapping.getPartIndex(minEntry);
var minCharIndex = CharacterMapping.getCharIndex(minEntry);
var maxPartIndex = CharacterMapping.getPartIndex(maxEntry);
var maxCharIndex;
if (minPartIndex !== maxPartIndex) {
// sitting between parts
maxCharIndex = partLength;
}
else {
maxCharIndex = CharacterMapping.getCharIndex(maxEntry);
}
var minEntryDistance = charIndex - minCharIndex;
var maxEntryDistance = maxCharIndex - charIndex;
if (minEntryDistance <= maxEntryDistance) {
return min;
}
return max;
};
return CharacterMapping;
}());
export { CharacterMapping };
var RenderLineOutput = /** @class */ (function () {
function RenderLineOutput(characterMapping, containsRTL, containsForeignElements) {
this.characterMapping = characterMapping;
this.containsRTL = containsRTL;
this.containsForeignElements = containsForeignElements;
}
return RenderLineOutput;
}());
export { RenderLineOutput };
export function renderViewLine(input, sb) {
if (input.lineContent.length === 0) {
var containsForeignElements = 0 /* None */;
// This is basically for IE's hit test to work
var content = '<span><span>\u00a0</span></span>';
if (input.lineDecorations.length > 0) {
// This line is empty, but it contains inline decorations
var classNames = [];
for (var i = 0, len = input.lineDecorations.length; i < len; i++) {
var lineDecoration = input.lineDecorations[i];
if (lineDecoration.type === 1 /* Before */) {
classNames.push(input.lineDecorations[i].className);
containsForeignElements |= 1 /* Before */;
}
if (lineDecoration.type === 2 /* After */) {
classNames.push(input.lineDecorations[i].className);
containsForeignElements |= 2 /* After */;
}
}
if (containsForeignElements !== 0 /* None */) {
content = "<span><span class=\"" + classNames.join(' ') + "\"></span></span>";
}
}
sb.appendASCIIString(content);
return new RenderLineOutput(new CharacterMapping(0, 0), false, containsForeignElements);
}
return _renderLine(resolveRenderLineInput(input), sb);
}
var RenderLineOutput2 = /** @class */ (function () {
function RenderLineOutput2(characterMapping, html, containsRTL, containsForeignElements) {
this.characterMapping = characterMapping;
this.html = html;
this.containsRTL = containsRTL;
this.containsForeignElements = containsForeignElements;
}
return RenderLineOutput2;
}());
export { RenderLineOutput2 };
export function renderViewLine2(input) {
var sb = createStringBuilder(10000);
var out = renderViewLine(input, sb);
return new RenderLineOutput2(out.characterMapping, sb.build(), out.containsRTL, out.containsForeignElements);
}
var ResolvedRenderLineInput = /** @class */ (function () {
function ResolvedRenderLineInput(fontIsMonospace, canUseHalfwidthRightwardsArrow, lineContent, len, isOverflowing, parts, containsForeignElements, tabSize, containsRTL, spaceWidth, renderWhitespace, renderControlCharacters) {
this.fontIsMonospace = fontIsMonospace;
this.canUseHalfwidthRightwardsArrow = canUseHalfwidthRightwardsArrow;
this.lineContent = lineContent;
this.len = len;
this.isOverflowing = isOverflowing;
this.parts = parts;
this.containsForeignElements = containsForeignElements;
this.tabSize = tabSize;
this.containsRTL = containsRTL;
this.spaceWidth = spaceWidth;
this.renderWhitespace = renderWhitespace;
this.renderControlCharacters = renderControlCharacters;
//
}
return ResolvedRenderLineInput;
}());
function resolveRenderLineInput(input) {
var useMonospaceOptimizations = input.useMonospaceOptimizations;
var lineContent = input.lineContent;
var isOverflowing;
var len;
if (input.stopRenderingLineAfter !== -1 && input.stopRenderingLineAfter < lineContent.length) {
isOverflowing = true;
len = input.stopRenderingLineAfter;
}
else {
isOverflowing = false;
len = lineContent.length;
}
var tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === 2 /* All */ || input.renderWhitespace === 1 /* Boundary */) {
tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.renderWhitespace === 1 /* Boundary */);
}
var containsForeignElements = 0 /* None */;
if (input.lineDecorations.length > 0) {
for (var i = 0, len_1 = input.lineDecorations.length; i < len_1; i++) {
var lineDecoration = input.lineDecorations[i];
if (lineDecoration.type === 3 /* RegularAffectingLetterSpacing */) {
// Pretend there are foreign elements... although not 100% accurate.
containsForeignElements |= 1 /* Before */;
}
else if (lineDecoration.type === 1 /* Before */) {
containsForeignElements |= 1 /* Before */;
}
else if (lineDecoration.type === 2 /* After */) {
containsForeignElements |= 2 /* After */;
}
}
tokens = _applyInlineDecorations(lineContent, len, tokens, input.lineDecorations);
}
if (!input.containsRTL) {
// We can never split RTL text, as it ruins the rendering
tokens = splitLargeTokens(lineContent, tokens, !input.isBasicASCII || input.fontLigatures);
}
return new ResolvedRenderLineInput(useMonospaceOptimizations, input.canUseHalfwidthRightwardsArrow, lineContent, len, isOverflowing, tokens, containsForeignElements, input.tabSize, input.containsRTL, input.spaceWidth, input.renderWhitespace, input.renderControlCharacters);
}
/**
* In the rendering phase, characters are always looped until token.endIndex.
* Ensure that all tokens end before `len` and the last one ends precisely at `len`.
*/
function transformAndRemoveOverflowing(tokens, fauxIndentLength, len) {
var result = [], resultLen = 0;
// The faux indent part of the line should have no token type
if (fauxIndentLength > 0) {
result[resultLen++] = new LinePart(fauxIndentLength, '');
}
for (var tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) {
var endIndex = tokens.getEndOffset(tokenIndex);
if (endIndex <= fauxIndentLength) {
// The faux indent part of the line should have no token type
continue;
}
var type = tokens.getClassName(tokenIndex);
if (endIndex >= len) {
result[resultLen++] = new LinePart(len, type);
break;
}
result[resultLen++] = new LinePart(endIndex, type);
}
return result;
}
/**
* See https://github.com/Microsoft/vscode/issues/6885.
* It appears that having very large spans causes very slow reading of character positions.
* So here we try to avoid that.
*/
function splitLargeTokens(lineContent, tokens, onlyAtSpaces) {
var lastTokenEndIndex = 0;
var result = [], resultLen = 0;
if (onlyAtSpaces) {
// Split only at spaces => we need to walk each character
for (var i = 0, len = tokens.length; i < len; i++) {
var token = tokens[i];
var tokenEndIndex = token.endIndex;
if (lastTokenEndIndex + 50 /* LongToken */ < tokenEndIndex) {
var tokenType = token.type;
var lastSpaceOffset = -1;
var currTokenStart = lastTokenEndIndex;
for (var j = lastTokenEndIndex; j < tokenEndIndex; j++) {
if (lineContent.charCodeAt(j) === 32 /* Space */) {
lastSpaceOffset = j;
}
if (lastSpaceOffset !== -1 && j - currTokenStart >= 50 /* LongToken */) {
// Split at `lastSpaceOffset` + 1
result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType);
currTokenStart = lastSpaceOffset + 1;
lastSpaceOffset = -1;
}
}
if (currTokenStart !== tokenEndIndex) {
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
}
}
else {
result[resultLen++] = token;
}
lastTokenEndIndex = tokenEndIndex;
}
}
else {
// Split anywhere => we don't need to walk each character
for (var i = 0, len = tokens.length; i < len; i++) {
var token = tokens[i];
var tokenEndIndex = token.endIndex;
var diff = (tokenEndIndex - lastTokenEndIndex);
if (diff > 50 /* LongToken */) {
var tokenType = token.type;
var piecesCount = Math.ceil(diff / 50 /* LongToken */);
for (var j = 1; j < piecesCount; j++) {
var pieceEndIndex = lastTokenEndIndex + (j * 50 /* LongToken */);
result[resultLen++] = new LinePart(pieceEndIndex, tokenType);
}
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
}
else {
result[resultLen++] = token;
}
lastTokenEndIndex = tokenEndIndex;
}
}
return result;
}
/**
* Whitespace is rendered by "replacing" tokens with a special-purpose `vs-whitespace` type that is later recognized in the rendering phase.
* Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (→ or ·) do not have the same width as .
* The rendering phase will generate `style="width:..."` for these tokens.
*/
function _applyRenderWhitespace(lineContent, len, continuesWithWrappedLine, tokens, fauxIndentLength, tabSize, useMonospaceOptimizations, onlyBoundary) {
var result = [], resultLen = 0;
var tokenIndex = 0;
var tokenType = tokens[tokenIndex].type;
var tokenEndIndex = tokens[tokenIndex].endIndex;
var tokensLength = tokens.length;
var firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
var lastNonWhitespaceIndex;
if (firstNonWhitespaceIndex === -1) {
// The entire line is whitespace
firstNonWhitespaceIndex = len;
lastNonWhitespaceIndex = len;
}
else {
lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent);
}
var tmpIndent = 0;
for (var charIndex = 0; charIndex < fauxIndentLength; charIndex++) {
var chCode = lineContent.charCodeAt(charIndex);
if (chCode === 9 /* Tab */) {
tmpIndent = tabSize;
}
else if (strings.isFullWidthCharacter(chCode)) {
tmpIndent += 2;
}
else {
tmpIndent++;
}
}
tmpIndent = tmpIndent % tabSize;
var wasInWhitespace = false;
for (var charIndex = fauxIndentLength; charIndex < len; charIndex++) {
var chCode = lineContent.charCodeAt(charIndex);
var isInWhitespace = void 0;
if (charIndex < firstNonWhitespaceIndex || charIndex > lastNonWhitespaceIndex) {
// in leading or trailing whitespace
isInWhitespace = true;
}
else if (chCode === 9 /* Tab */) {
// a tab character is rendered both in all and boundary cases
isInWhitespace = true;
}
else if (chCode === 32 /* Space */) {
// hit a space character
if (onlyBoundary) {
// rendering only boundary whitespace
if (wasInWhitespace) {
isInWhitespace = true;
}
else {
var nextChCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : 0 /* Null */);
isInWhitespace = (nextChCode === 32 /* Space */ || nextChCode === 9 /* Tab */);
}
}
else {
isInWhitespace = true;
}
}
else {
isInWhitespace = false;
}
if (wasInWhitespace) {
// was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
// leaving whitespace token or entering a new indent
result[resultLen++] = new LinePart(charIndex, 'vs-whitespace');
tmpIndent = tmpIndent % tabSize;
}
}
else {
// was in regular token
if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) {
result[resultLen++] = new LinePart(charIndex, tokenType);
tmpIndent = tmpIndent % tabSize;
}
}
if (chCode === 9 /* Tab */) {
tmpIndent = tabSize;
}
else if (strings.isFullWidthCharacter(chCode)) {
tmpIndent += 2;
}
else {
tmpIndent++;
}
wasInWhitespace = isInWhitespace;
if (charIndex === tokenEndIndex) {
tokenIndex++;
if (tokenIndex < tokensLength) {
tokenType = tokens[tokenIndex].type;
tokenEndIndex = tokens[tokenIndex].endIndex;
}
}
}
var generateWhitespace = false;
if (wasInWhitespace) {
// was in whitespace token
if (continuesWithWrappedLine && onlyBoundary) {
var lastCharCode = (len > 0 ? lineContent.charCodeAt(len - 1) : 0 /* Null */);
var prevCharCode = (len > 1 ? lineContent.charCodeAt(len - 2) : 0 /* Null */);
var isSingleTrailingSpace = (lastCharCode === 32 /* Space */ && (prevCharCode !== 32 /* Space */ && prevCharCode !== 9 /* Tab */));
if (!isSingleTrailingSpace) {
generateWhitespace = true;
}
}
else {
generateWhitespace = true;
}
}
result[resultLen++] = new LinePart(len, generateWhitespace ? 'vs-whitespace' : tokenType);
return result;
}
/**
* Inline decorations are "merged" on top of tokens.
* Special care must be taken when multiple inline decorations are at play and they overlap.
*/
function _applyInlineDecorations(lineContent, len, tokens, _lineDecorations) {
_lineDecorations.sort(LineDecoration.compare);
var lineDecorations = LineDecorationsNormalizer.normalize(lineContent, _lineDecorations);
var lineDecorationsLen = lineDecorations.length;
var lineDecorationIndex = 0;
var result = [], resultLen = 0, lastResultEndIndex = 0;
for (var tokenIndex = 0, len_2 = tokens.length; tokenIndex < len_2; tokenIndex++) {
var token = tokens[tokenIndex];
var tokenEndIndex = token.endIndex;
var tokenType = token.type;
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) {
var lineDecoration = lineDecorations[lineDecorationIndex];
if (lineDecoration.startOffset > lastResultEndIndex) {
lastResultEndIndex = lineDecoration.startOffset;
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
}
if (lineDecoration.endOffset + 1 <= tokenEndIndex) {
// This line decoration ends before this token ends
lastResultEndIndex = lineDecoration.endOffset + 1;
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
lineDecorationIndex++;
}
else {
// This line decoration continues on to the next token
lastResultEndIndex = tokenEndIndex;
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
break;
}
}
if (tokenEndIndex > lastResultEndIndex) {
lastResultEndIndex = tokenEndIndex;
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
}
}
var lastTokenEndIndex = tokens[tokens.length - 1].endIndex;
if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
var classNames = [];
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
classNames.push(lineDecorations[lineDecorationIndex].className);
lineDecorationIndex++;
}
result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '));
}
return result;
}
/**
* This function is on purpose not split up into multiple functions to allow runtime type inference (i.e. performance reasons).
* Notice how all the needed data is fully resolved and passed in (i.e. no other calls).
*/
function _renderLine(input, sb) {
var fontIsMonospace = input.fontIsMonospace;
var canUseHalfwidthRightwardsArrow = input.canUseHalfwidthRightwardsArrow;
var containsForeignElements = input.containsForeignElements;
var lineContent = input.lineContent;
var len = input.len;
var isOverflowing = input.isOverflowing;
var parts = input.parts;
var tabSize = input.tabSize;
var containsRTL = input.containsRTL;
var spaceWidth = input.spaceWidth;
var renderWhitespace = input.renderWhitespace;
var renderControlCharacters = input.renderControlCharacters;
var characterMapping = new CharacterMapping(len + 1, parts.length);
var charIndex = 0;
var tabsCharDelta = 0;
var charOffsetInPart = 0;
var prevPartContentCnt = 0;
var partAbsoluteOffset = 0;
sb.appendASCIIString('<span>');
for (var partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
partAbsoluteOffset += prevPartContentCnt;
var part = parts[partIndex];
var partEndIndex = part.endIndex;
var partType = part.type;
var partRendersWhitespace = (renderWhitespace !== 0 /* None */ && (partType.indexOf('vs-whitespace') >= 0));
charOffsetInPart = 0;
sb.appendASCIIString('<span class="');
sb.appendASCIIString(partType);
sb.appendASCII(34 /* DoubleQuote */);
if (partRendersWhitespace) {
var partContentCnt = 0;
{
var _charIndex = charIndex;
var _tabsCharDelta = tabsCharDelta;
for (; _charIndex < partEndIndex; _charIndex++) {
var charCode = lineContent.charCodeAt(_charIndex);
if (charCode === 9 /* Tab */) {
var insertSpacesCount = tabSize - (_charIndex + _tabsCharDelta) % tabSize;
_tabsCharDelta += insertSpacesCount - 1;
partContentCnt += insertSpacesCount;
}
else {
// must be CharCode.Space
partContentCnt++;
}
}
}
if (!fontIsMonospace) {
var partIsOnlyWhitespace = (partType === 'vs-whitespace');
if (partIsOnlyWhitespace || !containsForeignElements) {
sb.appendASCIIString(' style="width:');
sb.appendASCIIString(String(spaceWidth * partContentCnt));
sb.appendASCIIString('px"');
}
}
sb.appendASCII(62 /* GreaterThan */);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset);
var charCode = lineContent.charCodeAt(charIndex);
if (charCode === 9 /* Tab */) {
var insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
if (insertSpacesCount > 0) {
if (!canUseHalfwidthRightwardsArrow || insertSpacesCount > 1) {
sb.write1(0x2192); // RIGHTWARDS ARROW
}
else {
sb.write1(0xffeb); // HALFWIDTH RIGHTWARDS ARROW
}
insertSpacesCount--;
}
while (insertSpacesCount > 0) {
sb.write1(0xA0); //
insertSpacesCount--;
}
}
else {
// must be CharCode.Space
sb.write1(0xb7); // ·
}
charOffsetInPart++;
}
prevPartContentCnt = partContentCnt;
}
else {
var partContentCnt = 0;
if (containsRTL) {
sb.appendASCIIString(' dir="ltr"');
}
sb.appendASCII(62 /* GreaterThan */);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset);
var charCode = lineContent.charCodeAt(charIndex);
switch (charCode) {
case 9 /* Tab */:
var insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
while (insertSpacesCount > 0) {
sb.write1(0xA0); //
partContentCnt++;
insertSpacesCount--;
}
break;
case 32 /* Space */:
sb.write1(0xA0); //
partContentCnt++;
break;
case 60 /* LessThan */:
sb.appendASCIIString('<');
partContentCnt++;
break;
case 62 /* GreaterThan */:
sb.appendASCIIString('>');
partContentCnt++;
break;
case 38 /* Ampersand */:
sb.appendASCIIString('&');
partContentCnt++;
break;
case 0 /* Null */:
sb.appendASCIIString('�');
partContentCnt++;
break;
case 65279 /* UTF8_BOM */:
case 8232 /* LINE_SEPARATOR_2028 */:
sb.write1(0xfffd);
partContentCnt++;
break;
default:
if (strings.isFullWidthCharacter(charCode)) {
tabsCharDelta++;
}
if (renderControlCharacters && charCode < 32) {
sb.write1(9216 + charCode);
partContentCnt++;
}
else {
sb.write1(charCode);
partContentCnt++;
}
}
charOffsetInPart++;
}
prevPartContentCnt = partContentCnt;
}
sb.appendASCIIString('</span>');
}
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart, partAbsoluteOffset);
if (isOverflowing) {
sb.appendASCIIString('<span>…</span>');
}
sb.appendASCIIString('</span>');
return new RenderLineOutput(characterMapping, containsRTL, containsForeignElements);
}