From 142f9da7ce2f3128e0befea4d1744bb3f63f48a7 Mon Sep 17 00:00:00 2001 From: David Michon Date: Tue, 11 May 2021 09:16:46 -0700 Subject: [PATCH 1/5] Use char code array in createTextWriter --- src/compiler/utilities.ts | 83 +++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ecc92f8abfd69..a33cd7c5c7edb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3932,32 +3932,66 @@ 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 appendRaw(text: string) { + let lastLineStart = -1; + + const len = text.length; + for (let pos = 0; pos < len; pos++) { + const ch = text.charCodeAt(pos); + appendCharCode(ch); + // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway + if (ch !== CharacterCodes.carriageReturn && isLineBreak(ch)) { + ++lineCount; + lastLineStart = totalChars; + } + } + + if (lastLineStart >= 0) { + linePos = lastLineStart; } + + lineStart = linePos === totalChars; } function writeText(s: string) { if (s && s.length) { if (lineStart) { - s = getIndentString(indent) + s; - lineStart = false; + for (let i = 0; i < indent; i++) { + appendCharCode(CharacterCodes.space); + } + // lineStart will be automatically cleared by the append } - output += s; - updateLineCountAndPosFor(s); + appendRaw(s); } } @@ -3978,12 +4012,14 @@ 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); + appendRaw(s); hasTrailingComment = false; } } @@ -3996,16 +4032,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 +4056,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, From d821808ade01dfe4e19d7ed286f261a25f17e7c0 Mon Sep 17 00:00:00 2001 From: David Michon Date: Tue, 11 May 2021 09:57:35 -0700 Subject: [PATCH 2/5] Fix indent size --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a33cd7c5c7edb..07d18fbe3f6d4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3986,7 +3986,7 @@ namespace ts { function writeText(s: string) { if (s && s.length) { if (lineStart) { - for (let i = 0; i < indent; i++) { + for (let i = 0, totalIndent = indent * getIndentSize(); i < totalIndent; i++) { appendCharCode(CharacterCodes.space); } // lineStart will be automatically cleared by the append From eb9b36bc7ddb15936cc150472e66b2dc73dded80 Mon Sep 17 00:00:00 2001 From: David Michon Date: Tue, 11 May 2021 09:57:45 -0700 Subject: [PATCH 3/5] Minor optimization --- src/compiler/utilities.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 07d18fbe3f6d4..661b3ac62dd40 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3963,8 +3963,6 @@ namespace ts { } function appendRaw(text: string) { - let lastLineStart = -1; - const len = text.length; for (let pos = 0; pos < len; pos++) { const ch = text.charCodeAt(pos); @@ -3972,14 +3970,10 @@ namespace ts { // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway if (ch !== CharacterCodes.carriageReturn && isLineBreak(ch)) { ++lineCount; - lastLineStart = totalChars; + linePos = totalChars; } } - if (lastLineStart >= 0) { - linePos = lastLineStart; - } - lineStart = linePos === totalChars; } From b07dc871c18ee2e48b7e8690bf609420ba698e6b Mon Sep 17 00:00:00 2001 From: David Michon Date: Tue, 11 May 2021 10:43:42 -0700 Subject: [PATCH 4/5] Try preserving long strings --- src/compiler/utilities.ts | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 661b3ac62dd40..3a17b3c73416c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3962,13 +3962,13 @@ namespace ts { } } - function appendRaw(text: string) { + function appendRawSmall(text: string) { const len = text.length; for (let pos = 0; pos < len; pos++) { const ch = text.charCodeAt(pos); appendCharCode(ch); // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway - if (ch !== CharacterCodes.carriageReturn && isLineBreak(ch)) { + if (isLineBreak(ch) && ch !== CharacterCodes.carriageReturn) { ++lineCount; linePos = totalChars; } @@ -3977,6 +3977,25 @@ namespace ts { lineStart = linePos === totalChars; } + function appendRawLarge(text: string) { + flushBuffer(); + + const len = text.length; + for (let pos = 0; pos < len; pos++) { + const ch = text.charCodeAt(pos); + ++totalChars; + lastChar = ch; + // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway + if (isLineBreak(ch) && ch !== CharacterCodes.carriageReturn) { + ++lineCount; + linePos = totalChars; + } + } + + lineStart = linePos === totalChars; + output += text; + } + function writeText(s: string) { if (s && s.length) { if (lineStart) { @@ -3985,7 +4004,11 @@ namespace ts { } // lineStart will be automatically cleared by the append } - appendRaw(s); + if (s.length > 256) { + appendRawLarge(s); + } else { + appendRawSmall(s); + } } } @@ -4013,7 +4036,11 @@ namespace ts { function rawWrite(s: string) { if (s !== undefined) { - appendRaw(s); + if (s.length > 256) { + appendRawLarge(s); + } else { + appendRawSmall(s); + } hasTrailingComment = false; } } From 7f98bef06320edac95df8a2ba824b0b8119a039a Mon Sep 17 00:00:00 2001 From: David Michon Date: Tue, 11 May 2021 11:20:39 -0700 Subject: [PATCH 5/5] Reconcile carriageReturn handling --- src/compiler/utilities.ts | 53 +++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3a17b3c73416c..8dcd74a245f4a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3964,13 +3964,29 @@ namespace ts { function appendRawSmall(text: string) { const len = text.length; - for (let pos = 0; pos < len; pos++) { + let pos = 0; + while (pos < len) { const ch = text.charCodeAt(pos); appendCharCode(ch); - // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway - if (isLineBreak(ch) && ch !== CharacterCodes.carriageReturn) { - ++lineCount; - linePos = totalChars; + 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; } } @@ -3981,14 +3997,31 @@ namespace ts { flushBuffer(); const len = text.length; - for (let pos = 0; pos < len; pos++) { + let pos = 0; + while (pos < len) { const ch = text.charCodeAt(pos); ++totalChars; lastChar = ch; - // Ignore carriageReturn, since we mark the following lineFeed as the newline anyway - if (isLineBreak(ch) && ch !== CharacterCodes.carriageReturn) { - ++lineCount; - linePos = totalChars; + 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; } }