/*--------------------------------------------------------------------------------------------- * 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 { createStringBuilder } from '../../common/core/stringBuilder.js'; import * as strings from '../../../base/common/strings.js'; import { Configuration } from '../config/configuration.js'; import { LineInjectedText } from '../../common/model/textModelEvents.js'; import { ModelLineProjectionData } from '../../common/viewModel/modelLineProjectionData.js'; const ttPolicy = (_a = window.trustedTypes) === null || _a === void 0 ? void 0 : _a.createPolicy('domLineBreaksComputer', { createHTML: value => value }); export class DOMLineBreaksComputerFactory { static create() { return new DOMLineBreaksComputerFactory(); } constructor() { } createLineBreaksComputer(fontInfo, tabSize, wrappingColumn, wrappingIndent) { let requests = []; let injectedTexts = []; return { addRequest: (lineText, injectedText, previousLineBreakData) => { requests.push(lineText); injectedTexts.push(injectedText); }, finalize: () => { return createLineBreaks(requests, fontInfo, tabSize, wrappingColumn, wrappingIndent, injectedTexts); } }; } } function createLineBreaks(requests, fontInfo, tabSize, firstLineBreakColumn, wrappingIndent, injectedTextsPerLine) { var _a; function createEmptyLineBreakWithPossiblyInjectedText(requestIdx) { const injectedTexts = injectedTextsPerLine[requestIdx]; if (injectedTexts) { const lineText = LineInjectedText.applyInjectedText(requests[requestIdx], injectedTexts); const injectionOptions = injectedTexts.map(t => t.options); const injectionOffsets = injectedTexts.map(text => text.column - 1); // creating a `LineBreakData` with an invalid `breakOffsetsVisibleColumn` is OK // because `breakOffsetsVisibleColumn` will never be used because it contains injected text return new ModelLineProjectionData(injectionOffsets, injectionOptions, [lineText.length], [], 0); } else { return null; } } if (firstLineBreakColumn === -1) { const result = []; for (let i = 0, len = requests.length; i < len; i++) { result[i] = createEmptyLineBreakWithPossiblyInjectedText(i); } return result; } const overallWidth = Math.round(firstLineBreakColumn * fontInfo.typicalHalfwidthCharacterWidth); const additionalIndent = (wrappingIndent === 3 /* DeepIndent */ ? 2 : wrappingIndent === 2 /* Indent */ ? 1 : 0); const additionalIndentSize = Math.round(tabSize * additionalIndent); const additionalIndentLength = Math.ceil(fontInfo.spaceWidth * additionalIndentSize); const containerDomNode = document.createElement('div'); Configuration.applyFontInfoSlow(containerDomNode, fontInfo); const sb = createStringBuilder(10000); const firstNonWhitespaceIndices = []; const wrappedTextIndentLengths = []; const renderLineContents = []; const allCharOffsets = []; const allVisibleColumns = []; for (let i = 0; i < requests.length; i++) { const lineContent = LineInjectedText.applyInjectedText(requests[i], injectedTextsPerLine[i]); let firstNonWhitespaceIndex = 0; let wrappedTextIndentLength = 0; let width = overallWidth; if (wrappingIndent !== 0 /* None */) { firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); if (firstNonWhitespaceIndex === -1) { // all whitespace line firstNonWhitespaceIndex = 0; } else { // Track existing indent for (let i = 0; i < firstNonWhitespaceIndex; i++) { const charWidth = (lineContent.charCodeAt(i) === 9 /* Tab */ ? (tabSize - (wrappedTextIndentLength % tabSize)) : 1); wrappedTextIndentLength += charWidth; } const indentWidth = Math.ceil(fontInfo.spaceWidth * wrappedTextIndentLength); // Force sticking to beginning of line if no character would fit except for the indentation if (indentWidth + fontInfo.typicalFullwidthCharacterWidth > overallWidth) { firstNonWhitespaceIndex = 0; wrappedTextIndentLength = 0; } else { width = overallWidth - indentWidth; } } } const renderLineContent = lineContent.substr(firstNonWhitespaceIndex); const tmp = renderLine(renderLineContent, wrappedTextIndentLength, tabSize, width, sb, additionalIndentLength); firstNonWhitespaceIndices[i] = firstNonWhitespaceIndex; wrappedTextIndentLengths[i] = wrappedTextIndentLength; renderLineContents[i] = renderLineContent; allCharOffsets[i] = tmp[0]; allVisibleColumns[i] = tmp[1]; } const html = sb.build(); const trustedhtml = (_a = ttPolicy === null || ttPolicy === void 0 ? void 0 : ttPolicy.createHTML(html)) !== null && _a !== void 0 ? _a : html; containerDomNode.innerHTML = trustedhtml; containerDomNode.style.position = 'absolute'; containerDomNode.style.top = '10000'; containerDomNode.style.wordWrap = 'break-word'; document.body.appendChild(containerDomNode); let range = document.createRange(); const lineDomNodes = Array.prototype.slice.call(containerDomNode.children, 0); let result = []; for (let i = 0; i < requests.length; i++) { const lineDomNode = lineDomNodes[i]; const breakOffsets = readLineBreaks(range, lineDomNode, renderLineContents[i], allCharOffsets[i]); if (breakOffsets === null) { result[i] = createEmptyLineBreakWithPossiblyInjectedText(i); continue; } const firstNonWhitespaceIndex = firstNonWhitespaceIndices[i]; const wrappedTextIndentLength = wrappedTextIndentLengths[i] + additionalIndentSize; const visibleColumns = allVisibleColumns[i]; const breakOffsetsVisibleColumn = []; for (let j = 0, len = breakOffsets.length; j < len; j++) { breakOffsetsVisibleColumn[j] = visibleColumns[breakOffsets[j]]; } if (firstNonWhitespaceIndex !== 0) { // All break offsets are relative to the renderLineContent, make them absolute again for (let j = 0, len = breakOffsets.length; j < len; j++) { breakOffsets[j] += firstNonWhitespaceIndex; } } let injectionOptions; let injectionOffsets; const curInjectedTexts = injectedTextsPerLine[i]; if (curInjectedTexts) { injectionOptions = curInjectedTexts.map(t => t.options); injectionOffsets = curInjectedTexts.map(text => text.column - 1); } else { injectionOptions = null; injectionOffsets = null; } result[i] = new ModelLineProjectionData(injectionOffsets, injectionOptions, breakOffsets, breakOffsetsVisibleColumn, wrappedTextIndentLength); } document.body.removeChild(containerDomNode); return result; } function renderLine(lineContent, initialVisibleColumn, tabSize, width, sb, wrappingIndentLength) { if (wrappingIndentLength !== 0) { let hangingOffset = String(wrappingIndentLength); sb.appendASCIIString('