From 2b9e4aabf229ec351015489380e33a4f063d4d21 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 6 Feb 2020 09:06:56 -0800 Subject: [PATCH] Allow emitter to write multiple newlines in node lists --- src/compiler/emitter.ts | 95 ++++++++++++++++++------------------- src/compiler/types.ts | 3 +- src/compiler/utilities.ts | 8 +++- src/services/textChanges.ts | 6 +-- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a5eb13647e9..91985448b93 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2924,14 +2924,14 @@ namespace ts { return false; } - if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) - || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { + if (getLeadingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines) + || getClosingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines)) { return false; } let previousStatement: Statement | undefined; for (const statement of body.statements) { - if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { + if (getSeparatingLineTerminatorCount(previousStatement, statement, ListFormat.PreserveLines) > 0) { return false; } @@ -4002,8 +4002,9 @@ namespace ts { // Write the opening line terminator or leading whitespace. const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; - if (shouldWriteLeadingLineTerminator(parentNode, children!, format)) { // TODO: GH#18217 - writeLine(); + const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children!, format); // TODO: GH#18217 + if (leadingLineTerminatorCount) { + writeLine(leadingLineTerminatorCount); shouldEmitInterveningComments = false; } else if (format & ListFormat.SpaceBetweenBraces) { @@ -4042,7 +4043,8 @@ namespace ts { recordBundleFileInternalSectionEnd(previousSourceFileTextKind); // Write either a line terminator or whitespace to separate the elements. - if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { + const separatingLineTerminatorCount = getSeparatingLineTerminatorCount(previousSibling, child, format); + if (separatingLineTerminatorCount > 0) { // If a synthesized node in a single-line list starts on a new // line, we should increase the indent. if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { @@ -4050,7 +4052,7 @@ namespace ts { shouldDecreaseIndentAfterEmit = true; } - writeLine(); + writeLine(separatingLineTerminatorCount); shouldEmitInterveningComments = false; } else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { @@ -4105,8 +4107,9 @@ namespace ts { recordBundleFileInternalSectionEnd(previousSourceFileTextKind); // Write the closing line terminator or closing whitespace. - if (shouldWriteClosingLineTerminator(parentNode, children!, format)) { - writeLine(); + const closingLineTerminatorCount = getClosingLineTerminatorCount(parentNode, children!, format); + if (closingLineTerminatorCount) { + writeLine(closingLineTerminatorCount); } else if (format & ListFormat.SpaceBetweenBraces) { writeSpace(); @@ -4176,8 +4179,10 @@ namespace ts { writer.writeProperty(s); } - function writeLine() { - writer.writeLine(); + function writeLine(count = 1) { + for (let i = 0; i < count; i++) { + writer.writeLine(i > 0); + } } function increaseIndent() { @@ -4256,75 +4261,65 @@ namespace ts { } } - function shouldWriteLeadingLineTerminator(parentNode: TextRange, children: NodeArray, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return true; - } - - if (format & ListFormat.PreserveLines) { + function getLeadingLineTerminatorCount(parentNode: TextRange, children: NodeArray, format: ListFormat): number { + if (format & ListFormat.PreserveLines || printerOptions.preserveNewlines) { if (format & ListFormat.PreferNewLine) { - return true; + return 1; } const firstChild = children[0]; if (firstChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile!); + return rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { - return synthesizedNodeStartsOnNewLine(firstChild, format); + else if (!positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(firstChild)) { + return getLinesBetweenRangeEndAndRangeStart(parentNode, firstChild, currentSourceFile!); } - else { - return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile!); + else if (synthesizedNodeStartsOnNewLine(firstChild, format)) { + return 1; } } - else { - return false; - } + return format & ListFormat.MultiLine ? 1 : 0; } - function shouldWriteSeparatingLineTerminator(previousNode: Node | undefined, nextNode: Node, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return true; - } - else if (format & ListFormat.PreserveLines) { + function getSeparatingLineTerminatorCount(previousNode: Node | undefined, nextNode: Node, format: ListFormat): number { + if (format & ListFormat.PreserveLines || printerOptions.preserveNewlines) { if (previousNode === undefined || nextNode === undefined) { - return false; + return 0; } - else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { - return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); + else if (!nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode)) { + return getLinesBetweenRangeEndAndRangeStart(previousNode, nextNode, currentSourceFile!); } - else { - return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile!); + else if (synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format)) { + return 1; } } - else { - return getStartsOnNewLine(nextNode); + else if (getStartsOnNewLine(nextNode)) { + return 1; } + return format & ListFormat.MultiLine ? 1 : 0; } - function shouldWriteClosingLineTerminator(parentNode: TextRange, children: NodeArray, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return (format & ListFormat.NoTrailingNewLine) === 0; - } - else if (format & ListFormat.PreserveLines) { + function getClosingLineTerminatorCount(parentNode: TextRange, children: NodeArray, format: ListFormat): number { + if (format & ListFormat.PreserveLines || printerOptions.preserveNewlines) { if (format & ListFormat.PreferNewLine) { - return true; + return 1; } const lastChild = lastOrUndefined(children); if (lastChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile!); + return rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { - return synthesizedNodeStartsOnNewLine(lastChild, format); + else if (!positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild)) { + return getLinesBetweenRangeEndAndRangeStart(parentNode, lastChild, currentSourceFile!); } else { - return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile!); + return Number(synthesizedNodeStartsOnNewLine(lastChild, format)); } } - else { - return false; + if (format & ListFormat.MultiLine) { + return (format & ListFormat.NoTrailingNewLine) === 0 ? 1 : 0; } + return 0; } function synthesizedNodeStartsOnNewLine(node: Node, format: ListFormat) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2c67997f2e0..3743b89dc99 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3753,7 +3753,7 @@ namespace ts { writeParameter(text: string): void; writeProperty(text: string): void; writeSymbol(text: string, symbol: Symbol): void; - writeLine(): void; + writeLine(force?: boolean): void; increaseIndent(): void; decreaseIndent(): void; clear(): void; @@ -6242,6 +6242,7 @@ namespace ts { /*@internal*/ writeBundleFileInfo?: boolean; /*@internal*/ recordInternalSection?: boolean; /*@internal*/ stripInternal?: boolean; + /*@internal*/ preserveNewlines?: boolean; /*@internal*/ relativeToBuildInfo?: (path: string) => string; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a0a57176b0f..d338b39c829 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3569,8 +3569,8 @@ namespace ts { } } - function writeLine() { - if (!lineStart) { + function writeLine(force?: boolean) { + if (!lineStart || force) { output += newLine; lineCount++; linePos = output.length; @@ -4676,6 +4676,10 @@ namespace ts { return positionsAreOnSameLine(range1.end, getStartPositionOfRange(range2, sourceFile), sourceFile); } + export function getLinesBetweenRangeEndAndRangeStart(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { + return getLineOfLocalPosition(sourceFile, getStartPositionOfRange(range2, sourceFile)) - getLineOfLocalPosition(sourceFile, range1.end); + } + export function isNodeArrayMultiLine(list: NodeArray, sourceFile: SourceFile): boolean { return !positionsAreOnSameLine(list.pos, list.end, sourceFile); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index be978b5a334..a6301a45225 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -923,7 +923,7 @@ namespace ts.textChanges { export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } { const writer = createWriter(newLineCharacter); const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed; - createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer); + createPrinter({ newLine, neverAsciiEscape: true, preserveNewlines: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer); return { text: writer.getText(), node: assignPositionsToNode(node) }; } } @@ -1053,8 +1053,8 @@ namespace ts.textChanges { writer.writeSymbol(s, sym); setLastNonTriviaPosition(s, /*force*/ false); } - function writeLine(): void { - writer.writeLine(); + function writeLine(force?: boolean): void { + writer.writeLine(force); } function increaseIndent(): void { writer.increaseIndent();