diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ecc92f8abfd69..8dcd74a245f4a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3932,32 +3932,116 @@ namespace ts { export function createTextWriter(newLine: string): EmitTextWriter { let output: string; + const pendingCharCodes: number[] = []; + let lastChar: number; let indent: number; let lineStart: boolean; let lineCount: number; let linePos: number; + let totalChars: number = 0; let hasTrailingComment = false; - function updateLineCountAndPosFor(s: string) { - const lineStartsOfS = computeLineStarts(s); - if (lineStartsOfS.length > 1) { - lineCount = lineCount + lineStartsOfS.length - 1; - linePos = output.length - s.length + last(lineStartsOfS); - lineStart = (linePos - output.length) === 0; + const newLineCodes: number[] = []; + for (let i = 0; i < newLine.length; i++) { + newLineCodes.push(newLine.charCodeAt(i)); + } + + function appendCharCode(charCode: number) { + ++totalChars; + lastChar = charCode; + pendingCharCodes.push(charCode); + if (pendingCharCodes.length >= 1024) { + flushBuffer(); } - else { - lineStart = false; + } + + function flushBuffer() { + if (pendingCharCodes.length > 0) { + output += String.fromCharCode.apply(null, pendingCharCodes); + pendingCharCodes.length = 0; } } + function appendRawSmall(text: string) { + const len = text.length; + let pos = 0; + while (pos < len) { + const ch = text.charCodeAt(pos); + appendCharCode(ch); + pos++; + switch (ch) { + case CharacterCodes.carriageReturn: + const nextChar = text.charCodeAt(pos); + if (nextChar === CharacterCodes.lineFeed) { + appendCharCode(nextChar); + pos++; + } + // falls through + case CharacterCodes.lineFeed: + ++lineCount; + linePos = totalChars; + break; + default: + if (ch > CharacterCodes.maxAsciiCharacter && isLineBreak(ch)) { + ++lineCount; + linePos = totalChars; + } + break; + } + } + + lineStart = linePos === totalChars; + } + + function appendRawLarge(text: string) { + flushBuffer(); + + const len = text.length; + let pos = 0; + while (pos < len) { + const ch = text.charCodeAt(pos); + ++totalChars; + lastChar = ch; + pos++; + switch (ch) { + case CharacterCodes.carriageReturn: + const nextChar = text.charCodeAt(pos); + if (nextChar === CharacterCodes.lineFeed) { + ++totalChars; + lastChar = nextChar; + pos++; + } + // falls through + case CharacterCodes.lineFeed: + ++lineCount; + linePos = totalChars; + break; + default: + if (ch > CharacterCodes.maxAsciiCharacter && isLineBreak(ch)) { + ++lineCount; + linePos = totalChars; + } + break; + } + } + + lineStart = linePos === totalChars; + output += text; + } + function writeText(s: string) { if (s && s.length) { if (lineStart) { - s = getIndentString(indent) + s; - lineStart = false; + for (let i = 0, totalIndent = indent * getIndentSize(); i < totalIndent; i++) { + appendCharCode(CharacterCodes.space); + } + // lineStart will be automatically cleared by the append + } + if (s.length > 256) { + appendRawLarge(s); + } else { + appendRawSmall(s); } - output += s; - updateLineCountAndPosFor(s); } } @@ -3978,12 +4062,18 @@ namespace ts { lineCount = 0; linePos = 0; hasTrailingComment = false; + pendingCharCodes.length = 0; + lastChar = 0; + totalChars = 0; } function rawWrite(s: string) { if (s !== undefined) { - output += s; - updateLineCountAndPosFor(s); + if (s.length > 256) { + appendRawLarge(s); + } else { + appendRawSmall(s); + } hasTrailingComment = false; } } @@ -3996,16 +4086,18 @@ namespace ts { function writeLine(force?: boolean) { if (!lineStart || force) { - output += newLine; + for (let i = 0, len = newLineCodes.length; i < len; i++) { + appendCharCode(newLineCodes[i]); + } lineCount++; - linePos = output.length; + linePos = totalChars; lineStart = true; hasTrailingComment = false; } } function getTextPosWithWriteLine() { - return lineStart ? output.length : (output.length + newLine.length); + return lineStart ? totalChars : (totalChars + newLineCodes.length); } reset(); @@ -4018,13 +4110,16 @@ namespace ts { increaseIndent: () => { indent++; }, decreaseIndent: () => { indent--; }, getIndent: () => indent, - getTextPos: () => output.length, + getTextPos: () => totalChars, getLine: () => lineCount, - getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos, - getText: () => output, + getColumn: () => lineStart ? indent * getIndentSize() : totalChars - linePos, + getText: () => { + flushBuffer(); + return output; + }, isAtStartOfLine: () => lineStart, hasTrailingComment: () => hasTrailingComment, - hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)), + hasTrailingWhitespace: () => !!lastChar && isWhiteSpaceLike(lastChar), clear: reset, reportInaccessibleThisError: noop, reportPrivateInBaseOfClassExpression: noop,