in patched-vscode/src/vs/editor/common/viewLayout/viewLineRenderer.ts [686:848]
function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len: number, tokens: LinePart[]): LinePart[] {
const continuesWithWrappedLine = input.continuesWithWrappedLine;
const fauxIndentLength = input.fauxIndentLength;
const tabSize = input.tabSize;
const startVisibleColumn = input.startVisibleColumn;
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const selections = input.selectionsOnLine;
const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary);
const onlyTrailing = (input.renderWhitespace === RenderWhitespace.Trailing);
const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth);
const result: LinePart[] = [];
let resultLen = 0;
let tokenIndex = 0;
let tokenType = tokens[tokenIndex].type;
let tokenContainsRTL = tokens[tokenIndex].containsRTL;
let tokenEndIndex = tokens[tokenIndex].endIndex;
const tokensLength = tokens.length;
let lineIsEmptyOrWhitespace = false;
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
let lastNonWhitespaceIndex: number;
if (firstNonWhitespaceIndex === -1) {
lineIsEmptyOrWhitespace = true;
firstNonWhitespaceIndex = len;
lastNonWhitespaceIndex = len;
} else {
lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent);
}
let wasInWhitespace = false;
let currentSelectionIndex = 0;
let currentSelection = selections && selections[currentSelectionIndex];
let tmpIndent = startVisibleColumn % tabSize;
for (let charIndex = fauxIndentLength; charIndex < len; charIndex++) {
const chCode = lineContent.charCodeAt(charIndex);
if (currentSelection && charIndex >= currentSelection.endOffset) {
currentSelectionIndex++;
currentSelection = selections && selections[currentSelectionIndex];
}
let isInWhitespace: boolean;
if (charIndex < firstNonWhitespaceIndex || charIndex > lastNonWhitespaceIndex) {
// in leading or trailing whitespace
isInWhitespace = true;
} else if (chCode === CharCode.Tab) {
// a tab character is rendered both in all and boundary cases
isInWhitespace = true;
} else if (chCode === CharCode.Space) {
// hit a space character
if (onlyBoundary) {
// rendering only boundary whitespace
if (wasInWhitespace) {
isInWhitespace = true;
} else {
const nextChCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : CharCode.Null);
isInWhitespace = (nextChCode === CharCode.Space || nextChCode === CharCode.Tab);
}
} else {
isInWhitespace = true;
}
} else {
isInWhitespace = false;
}
// If rendering whitespace on selection, check that the charIndex falls within a selection
if (isInWhitespace && selections) {
isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex;
}
// If rendering only trailing whitespace, check that the charIndex points to trailing whitespace.
if (isInWhitespace && onlyTrailing) {
isInWhitespace = lineIsEmptyOrWhitespace || charIndex > lastNonWhitespaceIndex;
}
if (isInWhitespace && tokenContainsRTL) {
// If the token contains RTL text, breaking it up into multiple line parts
// to render whitespace might affect the browser's bidi layout.
//
// We render whitespace in such tokens only if the whitespace
// is the leading or the trailing whitespace of the line,
// which doesn't affect the browser's bidi layout.
if (charIndex >= firstNonWhitespaceIndex && charIndex <= lastNonWhitespaceIndex) {
isInWhitespace = false;
}
}
if (wasInWhitespace) {
// was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
// leaving whitespace token or entering a new indent
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= charIndex; i++) {
result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE, false);
}
} else {
result[resultLen++] = new LinePart(charIndex, 'mtkw', LinePartMetadata.IS_WHITESPACE, false);
}
tmpIndent = tmpIndent % tabSize;
}
} else {
// was in regular token
if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) {
result[resultLen++] = new LinePart(charIndex, tokenType, 0, tokenContainsRTL);
tmpIndent = tmpIndent % tabSize;
}
}
if (chCode === CharCode.Tab) {
tmpIndent = tabSize;
} else if (strings.isFullWidthCharacter(chCode)) {
tmpIndent += 2;
} else {
tmpIndent++;
}
wasInWhitespace = isInWhitespace;
while (charIndex === tokenEndIndex) {
tokenIndex++;
if (tokenIndex < tokensLength) {
tokenType = tokens[tokenIndex].type;
tokenContainsRTL = tokens[tokenIndex].containsRTL;
tokenEndIndex = tokens[tokenIndex].endIndex;
} else {
break;
}
}
}
let generateWhitespace = false;
if (wasInWhitespace) {
// was in whitespace token
if (continuesWithWrappedLine && onlyBoundary) {
const lastCharCode = (len > 0 ? lineContent.charCodeAt(len - 1) : CharCode.Null);
const prevCharCode = (len > 1 ? lineContent.charCodeAt(len - 2) : CharCode.Null);
const isSingleTrailingSpace = (lastCharCode === CharCode.Space && (prevCharCode !== CharCode.Space && prevCharCode !== CharCode.Tab));
if (!isSingleTrailingSpace) {
generateWhitespace = true;
}
} else {
generateWhitespace = true;
}
}
if (generateWhitespace) {
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= len; i++) {
result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE, false);
}
} else {
result[resultLen++] = new LinePart(len, 'mtkw', LinePartMetadata.IS_WHITESPACE, false);
}
} else {
result[resultLen++] = new LinePart(len, tokenType, 0, tokenContainsRTL);
}
return result;
}