From 680e48f507e38c3be58952b255f9a8201ea37bb3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 7 Mar 2015 12:54:12 -0800 Subject: [PATCH] Preserve newlines on either side of a binary expression. --- src/compiler/emitter.ts | 34 +++++++++++++++---- .../reference/APISample_transform.js | 3 +- tests/baselines/reference/asiArith.js | 8 ++--- ...constructorWithIncompleteTypeAnnotation.js | 3 +- .../parserGreaterThanTokenAmbiguity10.js | 4 +-- .../parserGreaterThanTokenAmbiguity15.js | 4 +-- .../parserGreaterThanTokenAmbiguity20.js | 4 +-- .../parserGreaterThanTokenAmbiguity4.js | 2 +- .../parserGreaterThanTokenAmbiguity5.js | 4 +-- .../parserGreaterThanTokenAmbiguity9.js | 2 +- ...parserRegularExpressionDivideAmbiguity1.js | 3 +- .../typeGuardsInConditionalExpression.js | 11 +++--- ...ypeGuardsInRightOperandOfAndAndOperator.js | 28 ++++++++------- .../typeGuardsInRightOperandOfOrOrOperator.js | 28 ++++++++------- 14 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a0c887414cd..9cdcfeef09d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3290,26 +3290,46 @@ module ts { else { emit(node.left); - if (node.operatorToken.kind !== SyntaxKind.CommaToken) { - write(" "); + // If there was a newline between the left side of the binary expression and the + // operator, then try to preserve that. + var indented = false; + var isSynthesied = nodeIsSynthesized(node); + if (!isSynthesied && !nodeEndIsOnSameLineAsNodeStart(node.left, node.operatorToken)) { + indented = true; + increaseIndent(); + writeLine(); + } + else { + // Otherwise just emit the operator right afterwards. For everything but + // comma, emit a space before the operator. + if (node.operatorToken.kind !== SyntaxKind.CommaToken) { + write(" "); + } } write(tokenToString(node.operatorToken.kind)); - var shouldPlaceOnNewLine = !nodeIsSynthesized(node) && !nodeEndIsOnSameLineAsNodeStart(node.operatorToken, node.right); + // If there was a newline after the operator (or this is a synthesized node that + // wants to be on a new line), then put a newline in. But only if we haven't + // already done this for the left side. + var wantsIndent = (!isSynthesied && !nodeEndIsOnSameLineAsNodeStart(node.operatorToken, node.right)) || + synthesizedNodeStartsOnNewLine(node.right); - // Check if the right expression is on a different line versus the operator itself. If so, - // we'll emit newline. - if (shouldPlaceOnNewLine || synthesizedNodeStartsOnNewLine(node.right)) { + if (wantsIndent && !indented) { + indented = true; increaseIndent(); writeLine(); emit(node.right); - decreaseIndent(); } else { write(" "); emit(node.right); } + + // If we indented the left or the right side, then dedent now. + if (indented) { + decreaseIndent(); + } } } diff --git a/tests/baselines/reference/APISample_transform.js b/tests/baselines/reference/APISample_transform.js index 4cd5c5c0983..27e04bec1a0 100644 --- a/tests/baselines/reference/APISample_transform.js +++ b/tests/baselines/reference/APISample_transform.js @@ -2052,7 +2052,8 @@ function transform(contents, compilerOptions) { return { outputs: outputs, errors: errors.map(function (e) { - return e.file.fileName + "(" + (e.file.getLineAndCharacterOfPosition(e.start).line + 1) + "): " + ts.flattenDiagnosticMessageText(e.messageText, os.EOL); + return e.file.fileName + "(" + (e.file.getLineAndCharacterOfPosition(e.start).line + 1) + "): " + + ts.flattenDiagnosticMessageText(e.messageText, os.EOL); }) }; } diff --git a/tests/baselines/reference/asiArith.js b/tests/baselines/reference/asiArith.js index 8e957aae332..b5016af3117 100644 --- a/tests/baselines/reference/asiArith.js +++ b/tests/baselines/reference/asiArith.js @@ -37,9 +37,9 @@ y //// [asiArith.js] var x = 1; var y = 1; -var z = x + - + +y; +var z = x + + + +y; var a = 1; var b = 1; -var c = x - - - -y; +var c = x + - - -y; diff --git a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js index ac77cc177b8..6adbaddb117 100644 --- a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js +++ b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js @@ -314,8 +314,7 @@ var TypeScriptAllInOne; Program.prototype.if = function (retValue) { if (retValue === void 0) { retValue = != 0; } return 1; - ^ - retValue; + ^ retValue; bfs.TYPES(); if (retValue != 0) { return 1 && diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js index a558407de43..64e142d7095 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js @@ -5,5 +5,5 @@ 2; //// [parserGreaterThanTokenAmbiguity10.js] -1 >>> - 2; +1 + >>> 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js index 83f2cda2364..7b44da35688 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js @@ -5,5 +5,5 @@ 2; //// [parserGreaterThanTokenAmbiguity15.js] -1 >>= - 2; +1 + >>= 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js index 9ac06e9644f..7a3650fdfd5 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js @@ -5,5 +5,5 @@ 2; //// [parserGreaterThanTokenAmbiguity20.js] -1 >>>= - 2; +1 + >>>= 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity4.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity4.js index c0b8fc45cc1..dcfb51575e4 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity4.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity4.js @@ -4,4 +4,4 @@ //// [parserGreaterThanTokenAmbiguity4.js] 1 > - > 2; + > 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js index cde18d5c63d..baa3af48e9d 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js @@ -5,5 +5,5 @@ 2; //// [parserGreaterThanTokenAmbiguity5.js] -1 >> - 2; +1 + >> 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity9.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity9.js index 757ddf38a7f..96be8d0749c 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity9.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity9.js @@ -4,4 +4,4 @@ //// [parserGreaterThanTokenAmbiguity9.js] 1 >> - > 2; + > 2; diff --git a/tests/baselines/reference/parserRegularExpressionDivideAmbiguity1.js b/tests/baselines/reference/parserRegularExpressionDivideAmbiguity1.js index 79e0c04d409..c3e61545c2c 100644 --- a/tests/baselines/reference/parserRegularExpressionDivideAmbiguity1.js +++ b/tests/baselines/reference/parserRegularExpressionDivideAmbiguity1.js @@ -3,4 +3,5 @@ /notregexp/a.foo(); //// [parserRegularExpressionDivideAmbiguity1.js] -1 / notregexp / a.foo(); +1 + / notregexp / a.foo(); diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js index 7c87d80d592..a1a546a193e 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.js +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -157,7 +157,8 @@ function foo10(x) { var b; return typeof x === "string" ? x // string : ((b = x) // x is number | boolean - && typeof x === "number" && x.toString()); // x is number + && typeof x === "number" + && x.toString()); // x is number } function foo11(x) { // Mixing typeguards @@ -165,8 +166,9 @@ function foo11(x) { var b; return typeof x === "string" ? x // number | boolean | string - changed in the false branch : ((b = x) // x is number | boolean | string - because the assignment changed it - && typeof x === "number" && (x = 10) // assignment to x - && x); // x is number | boolean | string + && typeof x === "number" + && (x = 10) // assignment to x + && x); // x is number | boolean | string } function foo12(x) { // Mixing typeguards @@ -174,5 +176,6 @@ function foo12(x) { var b; return typeof x === "string" ? (x = 10 && x.toString().length) // number | boolean | string - changed here : ((b = x) // x is number | boolean | string - changed in true branch - && typeof x === "number" && x); // x is number + && typeof x === "number" + && x); // x is number } diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js index ffba829c37d..23ee7b29f51 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js @@ -72,36 +72,38 @@ function foo3(x) { } function foo4(x) { return typeof x !== "string" // string | number | boolean - && typeof x !== "number" // number | boolean - && x; // boolean + && typeof x !== "number" // number | boolean + && x; // boolean } function foo5(x) { // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop var b; return typeof x !== "string" // string | number | boolean - && ((b = x) && (typeof x !== "number" // number | boolean - && x)); // boolean + && ((b = x) && (typeof x !== "number" // number | boolean + && x)); // boolean } function foo6(x) { // Mixing typeguard narrowing in if statement with conditional expression typeguard return typeof x !== "string" // string | number | boolean - && (typeof x !== "number" // number | boolean - ? x // boolean - : x === 10); // number + && (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10); // number } function foo7(x) { var y; var z; // Mixing typeguard narrowing // Assigning value to x deep inside another guard stops narrowing of type too - return typeof x !== "string" && ((z = x) // string | number | boolean - x changed deeper in conditional expression - && (typeof x === "number" ? (x = 10 && x.toString()) // number | boolean | string - : (y = x && x.toString()))); // number | boolean | string + return typeof x !== "string" + && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && (typeof x === "number" ? (x = 10 && x.toString()) // number | boolean | string + : (y = x && x.toString()))); // number | boolean | string } function foo8(x) { // Mixing typeguard // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" && (x = 10) // change x - number| string - && (typeof x === "number" ? x // number - : x.length); // string + return typeof x !== "string" + && (x = 10) // change x - number| string + && (typeof x === "number" ? x // number + : x.length); // string } diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js index 5f5880a4947..13bb090ab8c 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js @@ -72,36 +72,38 @@ function foo3(x) { } function foo4(x) { return typeof x === "string" // string | number | boolean - || typeof x === "number" // number | boolean - || x; // boolean + || typeof x === "number" // number | boolean + || x; // boolean } function foo5(x) { // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop var b; return typeof x === "string" // string | number | boolean - || ((b = x) || (typeof x === "number" // number | boolean - || x)); // boolean + || ((b = x) || (typeof x === "number" // number | boolean + || x)); // boolean } function foo6(x) { // Mixing typeguard return typeof x === "string" // string | number | boolean - || (typeof x !== "number" // number | boolean - ? x // boolean - : x === 10); // number + || (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10); // number } function foo7(x) { var y; var z; // Mixing typeguard narrowing // Assigning value to x deep inside another guard stops narrowing of type too - return typeof x === "string" || ((z = x) // string | number | boolean - x changed deeper in conditional expression - || (typeof x === "number" ? (x = 10 && x.toString()) // number | boolean | string - : (y = x && x.toString()))); // number | boolean | string + return typeof x === "string" + || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || (typeof x === "number" ? (x = 10 && x.toString()) // number | boolean | string + : (y = x && x.toString()))); // number | boolean | string } function foo8(x) { // Mixing typeguard // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" || (x = 10) // change x - number| string - || (typeof x === "number" ? x // number - : x.length); // string + return typeof x === "string" + || (x = 10) // change x - number| string + || (typeof x === "number" ? x // number + : x.length); // string }