in patched-vscode/src/vs/editor/common/viewLayout/viewLineRenderer.ts [913:1138]
function _renderLine(input: ResolvedRenderLineInput, sb: StringBuilder): RenderLineOutput {
const fontIsMonospace = input.fontIsMonospace;
const canUseHalfwidthRightwardsArrow = input.canUseHalfwidthRightwardsArrow;
const containsForeignElements = input.containsForeignElements;
const lineContent = input.lineContent;
const len = input.len;
const isOverflowing = input.isOverflowing;
const overflowingCharCount = input.overflowingCharCount;
const parts = input.parts;
const fauxIndentLength = input.fauxIndentLength;
const tabSize = input.tabSize;
const startVisibleColumn = input.startVisibleColumn;
const containsRTL = input.containsRTL;
const spaceWidth = input.spaceWidth;
const renderSpaceCharCode = input.renderSpaceCharCode;
const renderWhitespace = input.renderWhitespace;
const renderControlCharacters = input.renderControlCharacters;
const characterMapping = new CharacterMapping(len + 1, parts.length);
let lastCharacterMappingDefined = false;
let charIndex = 0;
let visibleColumn = startVisibleColumn;
let charOffsetInPart = 0; // the character offset in the current part
let charHorizontalOffset = 0; // the character horizontal position in terms of chars relative to line start
let partDisplacement = 0;
if (containsRTL) {
sb.appendString('<span dir="ltr">');
} else {
sb.appendString('<span>');
}
for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
const part = parts[partIndex];
const partEndIndex = part.endIndex;
const partType = part.type;
const partContainsRTL = part.containsRTL;
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace());
const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements);
const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.isPseudoAfter());
charOffsetInPart = 0;
sb.appendString('<span ');
if (partContainsRTL) {
sb.appendString('style="unicode-bidi:isolate" ');
}
sb.appendString('class="');
sb.appendString(partRendersWhitespaceWithWidth ? 'mtkz' : partType);
sb.appendASCIICharCode(CharCode.DoubleQuote);
if (partRendersWhitespace) {
let partWidth = 0;
{
let _charIndex = charIndex;
let _visibleColumn = visibleColumn;
for (; _charIndex < partEndIndex; _charIndex++) {
const charCode = lineContent.charCodeAt(_charIndex);
const charWidth = (charCode === CharCode.Tab ? (tabSize - (_visibleColumn % tabSize)) : 1) | 0;
partWidth += charWidth;
if (_charIndex >= fauxIndentLength) {
_visibleColumn += charWidth;
}
}
}
if (partRendersWhitespaceWithWidth) {
sb.appendString(' style="width:');
sb.appendString(String(spaceWidth * partWidth));
sb.appendString('px"');
}
sb.appendASCIICharCode(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, charHorizontalOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
let producedCharacters: number;
let charWidth: number;
if (charCode === CharCode.Tab) {
producedCharacters = (tabSize - (visibleColumn % tabSize)) | 0;
charWidth = producedCharacters;
if (!canUseHalfwidthRightwardsArrow || charWidth > 1) {
sb.appendCharCode(0x2192); // RIGHTWARDS ARROW
} else {
sb.appendCharCode(0xFFEB); // HALFWIDTH RIGHTWARDS ARROW
}
for (let space = 2; space <= charWidth; space++) {
sb.appendCharCode(0xA0); //
}
} else { // must be CharCode.Space
producedCharacters = 2;
charWidth = 1;
sb.appendCharCode(renderSpaceCharCode); // · or word separator middle dot
sb.appendCharCode(0x200C); // ZERO WIDTH NON-JOINER
}
charOffsetInPart += producedCharacters;
charHorizontalOffset += charWidth;
if (charIndex >= fauxIndentLength) {
visibleColumn += charWidth;
}
}
} else {
sb.appendASCIICharCode(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, charHorizontalOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
let producedCharacters = 1;
let charWidth = 1;
switch (charCode) {
case CharCode.Tab:
producedCharacters = (tabSize - (visibleColumn % tabSize));
charWidth = producedCharacters;
for (let space = 1; space <= producedCharacters; space++) {
sb.appendCharCode(0xA0); //
}
break;
case CharCode.Space:
sb.appendCharCode(0xA0); //
break;
case CharCode.LessThan:
sb.appendString('<');
break;
case CharCode.GreaterThan:
sb.appendString('>');
break;
case CharCode.Ampersand:
sb.appendString('&');
break;
case CharCode.Null:
if (renderControlCharacters) {
// See https://unicode-table.com/en/blocks/control-pictures/
sb.appendCharCode(9216);
} else {
sb.appendString('�');
}
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR:
case CharCode.PARAGRAPH_SEPARATOR:
case CharCode.NEXT_LINE:
sb.appendCharCode(0xFFFD);
break;
default:
if (strings.isFullWidthCharacter(charCode)) {
charWidth++;
}
// See https://unicode-table.com/en/blocks/control-pictures/
if (renderControlCharacters && charCode < 32) {
sb.appendCharCode(9216 + charCode);
} else if (renderControlCharacters && charCode === 127) {
// DEL
sb.appendCharCode(9249);
} else if (renderControlCharacters && isControlCharacter(charCode)) {
sb.appendString('[U+');
sb.appendString(to4CharHex(charCode));
sb.appendString(']');
producedCharacters = 8;
charWidth = producedCharacters;
} else {
sb.appendCharCode(charCode);
}
}
charOffsetInPart += producedCharacters;
charHorizontalOffset += charWidth;
if (charIndex >= fauxIndentLength) {
visibleColumn += charWidth;
}
}
}
if (partIsEmptyAndHasPseudoAfter) {
partDisplacement++;
} else {
partDisplacement = 0;
}
if (charIndex >= len && !lastCharacterMappingDefined && part.isPseudoAfter()) {
lastCharacterMappingDefined = true;
characterMapping.setColumnInfo(charIndex + 1, partIndex, charOffsetInPart, charHorizontalOffset);
}
sb.appendString('</span>');
}
if (!lastCharacterMappingDefined) {
// 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.setColumnInfo(len + 1, parts.length - 1, charOffsetInPart, charHorizontalOffset);
}
if (isOverflowing) {
sb.appendString('<span class="mtkoverflow">');
sb.appendString(nls.localize('showMore', "Show more ({0})", renderOverflowingCharCount(overflowingCharCount)));
sb.appendString('</span>');
}
sb.appendString('</span>');
return new RenderLineOutput(characterMapping, containsRTL, containsForeignElements);
}