in patched-vscode/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts [104:356]
function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: ModelLineProjectionData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ModelLineProjectionData | null {
if (firstLineBreakColumn === -1) {
return null;
}
const len = lineText.length;
if (len <= 1) {
return null;
}
const isKeepAll = (wordBreak === 'keepAll');
const prevBreakingOffsets = previousBreakingData.breakOffsets;
const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakOffsetsVisibleColumn;
const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent);
const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength;
const breakingOffsets: number[] = arrPool1;
const breakingOffsetsVisibleColumn: number[] = arrPool2;
let breakingOffsetsCount = 0;
let lastBreakingOffset = 0;
let lastBreakingOffsetVisibleColumn = 0;
let breakingColumn = firstLineBreakColumn;
const prevLen = prevBreakingOffsets.length;
let prevIndex = 0;
if (prevIndex >= 0) {
let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn);
while (prevIndex + 1 < prevLen) {
const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn);
if (distance >= bestDistance) {
break;
}
bestDistance = distance;
prevIndex++;
}
}
while (prevIndex < prevLen) {
// Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break)
let prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex];
let prevBreakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];
if (lastBreakingOffset > prevBreakOffset) {
prevBreakOffset = lastBreakingOffset;
prevBreakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn;
}
let breakOffset = 0;
let breakOffsetVisibleColumn = 0;
let forcedBreakOffset = 0;
let forcedBreakOffsetVisibleColumn = 0;
// initially, we search as much as possible to the right (if it fits)
if (prevBreakOffsetVisibleColumn <= breakingColumn) {
let visibleColumn = prevBreakOffsetVisibleColumn;
let prevCharCode = prevBreakOffset === 0 ? CharCode.Null : lineText.charCodeAt(prevBreakOffset - 1);
let prevCharCodeClass = prevBreakOffset === 0 ? CharacterClass.NONE : classifier.get(prevCharCode);
let entireLineFits = true;
for (let i = prevBreakOffset; i < len; i++) {
const charStartOffset = i;
const charCode = lineText.charCodeAt(i);
let charCodeClass: number;
let charWidth: number;
if (strings.isHighSurrogate(charCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
i++;
charCodeClass = CharacterClass.NONE;
charWidth = 2;
} else {
charCodeClass = classifier.get(charCode);
charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
}
if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) {
breakOffset = charStartOffset;
breakOffsetVisibleColumn = visibleColumn;
}
visibleColumn += charWidth;
// check if adding character at `i` will go over the breaking column
if (visibleColumn > breakingColumn) {
// We need to break at least before character at `i`:
if (charStartOffset > lastBreakingOffset) {
forcedBreakOffset = charStartOffset;
forcedBreakOffsetVisibleColumn = visibleColumn - charWidth;
} else {
// we need to advance at least by one character
forcedBreakOffset = i + 1;
forcedBreakOffsetVisibleColumn = visibleColumn;
}
if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) {
// Cannot break at `breakOffset` => reset it if it was set
breakOffset = 0;
}
entireLineFits = false;
break;
}
prevCharCode = charCode;
prevCharCodeClass = charCodeClass;
}
if (entireLineFits) {
// there is no more need to break => stop the outer loop!
if (breakingOffsetsCount > 0) {
// Add last segment, no need to assign to `lastBreakingOffset` and `lastBreakingOffsetVisibleColumn`
breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1];
breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1];
breakingOffsetsCount++;
}
break;
}
}
if (breakOffset === 0) {
// must search left
let visibleColumn = prevBreakOffsetVisibleColumn;
let charCode = lineText.charCodeAt(prevBreakOffset);
let charCodeClass = classifier.get(charCode);
let hitATabCharacter = false;
for (let i = prevBreakOffset - 1; i >= lastBreakingOffset; i--) {
const charStartOffset = i + 1;
const prevCharCode = lineText.charCodeAt(i);
if (prevCharCode === CharCode.Tab) {
// cannot determine the width of a tab when going backwards, so we must go forwards
hitATabCharacter = true;
break;
}
let prevCharCodeClass: number;
let prevCharWidth: number;
if (strings.isLowSurrogate(prevCharCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
i--;
prevCharCodeClass = CharacterClass.NONE;
prevCharWidth = 2;
} else {
prevCharCodeClass = classifier.get(prevCharCode);
prevCharWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1);
}
if (visibleColumn <= breakingColumn) {
if (forcedBreakOffset === 0) {
forcedBreakOffset = charStartOffset;
forcedBreakOffsetVisibleColumn = visibleColumn;
}
if (visibleColumn <= breakingColumn - wrappedLineBreakColumn) {
// went too far!
break;
}
if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) {
breakOffset = charStartOffset;
breakOffsetVisibleColumn = visibleColumn;
break;
}
}
visibleColumn -= prevCharWidth;
charCode = prevCharCode;
charCodeClass = prevCharCodeClass;
}
if (breakOffset !== 0) {
const remainingWidthOfNextLine = wrappedLineBreakColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn);
if (remainingWidthOfNextLine <= tabSize) {
const charCodeAtForcedBreakOffset = lineText.charCodeAt(forcedBreakOffset);
let charWidth: number;
if (strings.isHighSurrogate(charCodeAtForcedBreakOffset)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
charWidth = 2;
} else {
charWidth = computeCharWidth(charCodeAtForcedBreakOffset, forcedBreakOffsetVisibleColumn, tabSize, columnsForFullWidthChar);
}
if (remainingWidthOfNextLine - charWidth < 0) {
// it is not worth it to break at breakOffset, it just introduces an extra needless line!
breakOffset = 0;
}
}
}
if (hitATabCharacter) {
// cannot determine the width of a tab when going backwards, so we must go forwards from the previous break
prevIndex--;
continue;
}
}
if (breakOffset === 0) {
// Could not find a good breaking point
breakOffset = forcedBreakOffset;
breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn;
}
if (breakOffset <= lastBreakingOffset) {
// Make sure that we are advancing (at least one character)
const charCode = lineText.charCodeAt(lastBreakingOffset);
if (strings.isHighSurrogate(charCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
breakOffset = lastBreakingOffset + 2;
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + 2;
} else {
breakOffset = lastBreakingOffset + 1;
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + computeCharWidth(charCode, lastBreakingOffsetVisibleColumn, tabSize, columnsForFullWidthChar);
}
}
lastBreakingOffset = breakOffset;
breakingOffsets[breakingOffsetsCount] = breakOffset;
lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn;
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
breakingOffsetsCount++;
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn;
while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) {
prevIndex++;
}
let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn);
while (prevIndex + 1 < prevLen) {
const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn);
if (distance >= bestDistance) {
break;
}
bestDistance = distance;
prevIndex++;
}
}
if (breakingOffsetsCount === 0) {
return null;
}
// Doing here some object reuse which ends up helping a huge deal with GC pauses!
breakingOffsets.length = breakingOffsetsCount;
breakingOffsetsVisibleColumn.length = breakingOffsetsCount;
arrPool1 = previousBreakingData.breakOffsets;
arrPool2 = previousBreakingData.breakOffsetsVisibleColumn;
previousBreakingData.breakOffsets = breakingOffsets;
previousBreakingData.breakOffsetsVisibleColumn = breakingOffsetsVisibleColumn;
previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength;
return previousBreakingData;
}