Repository: eslint/eslint (js)

Issue:
Proposal: Add "always-multiline" option to "multiline-ternary" rule
**What rule do you want to change?**

[multiline-ternary](http://eslint.org/docs/rules/multiline-ternary)

**Does this change cause the rule to produce more or fewer warnings?**

n/a

**How will the change be implemented? (New option, new default behavior, etc.)?**

new option (`always-multiline`)

**Please provide some example code that this change will affect:**

```js
// 1
var foo = bar > baz ? value1 : value2;

// 2
var foo = bar > baz ?
    value1 : value2;
```

**What does the rule currently do for this code?**

##### `never`

1 - nothing (already correct)
2 - requires the second line to be moved onto the end of the first line

##### `always`

1 - requires `value1` and `value2` to each be moved onto their own line
2 - requires `value2` to be moved onto the next line

**What will the rule do after it's changed?**

##### `always-multiline`

1 - nothing (already correct)
2 - requires `value2` to be moved onto the next line

`always-multiline` is the same as `always` except it allows ternary expressions to be on a single line
(so the `always` functionality is enforced only if there is a newline somewhere within the ternary expression).

Pull request:
Update: Add always-multiline option to multiline-ternary (fixes #8770)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Changes an existing rule

See #8770 

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

This adds an `always-multiline` option to [`multiline-ternary`](http://eslint.org/docs/rules/multiline-ternary). This option enforces newlines between the operands of a ternary expression only if the expression spans multiple lines.

**Is there anything you'd like reviewers to focus on?**

Nope.

However, I would like out point out that with the addition of this option, the name of this rule could be changed to `ternary-newline` to follow the naming convention of similar ESLint rules.


Patch:
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index 67b5e7952..0c35b19fb 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -361,6 +361,8 @@ In the preceding example, the error level is assumed to be the first argument. I
 
 To learn more about JSON Schema, we recommend looking at some [examples](http://json-schema.org/examples.html) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook).
 
+**Note:** Currently you need to use full JSON Schema object rather than array in case your schema has references ($ref), because in case of array format eslint transforms this array into a single schema without updating references that makes them incorrect (they are ignored).
+
 ### Getting the Source
 
 If your rule needs to get the actual JavaScript source to work with, then use the `sourceCode.getText()` method. This method works as follows:
diff --git a/docs/rules/multiline-ternary.md b/docs/rules/multiline-ternary.md
index e331078f3..4faaad79c 100644
--- a/docs/rules/multiline-ternary.md
+++ b/docs/rules/multiline-ternary.md
@@ -26,6 +26,7 @@ Note: The location of the operators is not enforced by this rule. Please see the
 This rule has a string option:
 
 * `"always"` (default) enforces newlines between the operands of a ternary expression.
+* `"always-multiline"` enforces newlines between the operands of a ternary expression if the expression spans multiple lines.
 * `"never"` disallows newlines between the operands of a ternary expression (enforcing that the entire ternary expression is on one line).
 
 ### always
@@ -62,6 +63,50 @@ foo > bar ?
     value3;
 ```
 
+### always-multiline
+
+Examples of **incorrect** code for this rule with the `"always-multiline"` option:
+
+```js
+/*eslint multiline-ternary: ["error", "always"]*/
+
+foo > bar ? value1 :
+    value2;
+
+foo > bar ?
+    value1 : value2;
+
+foo > bar &&
+    bar > baz ? value1 : value2;
+```
+
+Examples of **correct** code for this rule with the `"always-multiline"` option:
+
+```js
+/*eslint multiline-ternary: ["error", "always"]*/
+
+foo > bar ? value1 : value2;
+
+foo > bar ?
+    value1 :
+    value2;
+
+foo > bar ?
+    (baz > qux ? value1 : value2) :
+    value3;
+
+foo > bar ?
+    (baz > qux ?
+        value1 :
+        value2) :
+    value3;
+
+foo > bar &&
+    bar > baz ?
+        value1 :
+        value2;
+```
+
 ### never
 
 Examples of **incorrect** code for this rule with the `"never"` option:
diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js
index 17066d947..ddcc0cd22 100644
--- a/lib/rules/comma-dangle.js
+++ b/lib/rules/comma-dangle.js
@@ -81,49 +81,49 @@ module.exports = {
             category: "Stylistic Issues",
             recommended: false
         },
-
         fixable: "code",
-
-        schema: [
-            {
-                defs: {
-                    value: {
-                        enum: [
-                            "always",
-                            "always-multiline",
-                            "only-multiline",
-                            "never"
-                        ]
-                    },
-                    valueWithIgnore: {
-                        anyOf: [
-                            {
-                                $ref: "#/defs/value"
-                            },
-                            {
-                                enum: ["ignore"]
-                            }
-                        ]
-                    }
+        schema: {
+            definitions: {
+                value: {
+                    enum: [
+                        "always-multiline",
+                        "always",
+                        "never",
+                        "only-multiline"
+                    ]
                 },
-                anyOf: [
-                    {
-                        $ref: "#/defs/value"
-                    },
-                    {
-                        type: "object",
-                        properties: {
-                            arrays: { $refs: "#/defs/valueWithIgnore" },
-                            objects: { $refs: "#/defs/valueWithIgnore" },
-                            imports: { $refs: "#/defs/valueWithIgnore" },
-                            exports: { $refs: "#/defs/valueWithIgnore" },
-                            functions: { $refs: "#/defs/valueWithIgnore" }
+                valueWithIgnore: {
+                    enum: [
+                        "always-multiline",
+                        "always",
+                        "ignore",
+                        "never",
+                        "only-multiline"
+                    ]
+                }
+            },
+            type: "array",
+            items: [
+                {
+                    oneOf: [
+                        {
+                            $ref: "#/definitions/value"
                         },
-                        additionalProperties: false
-                    }
-                ]
-            }
-        ]
+                        {
+                            type: "object",
+                            properties: {
+                                arrays: { $ref: "#/definitions/valueWithIgnore" },
+                                objects: { $ref: "#/definitions/valueWithIgnore" },
+                                imports: { $ref: "#/definitions/valueWithIgnore" },
+                                exports: { $ref: "#/definitions/valueWithIgnore" },
+                                functions: { $ref: "#/definitions/valueWithIgnore" }
+                            },
+                            additionalProperties: false
+                        }
+                    ]
+                }
+            ]
+        }
     },
 
     create(context) {
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 6babe29c9..78c8bb9af 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -1375,13 +1375,14 @@ module.exports = {
 
             JSXExpressionContainer(node) {
                 const openingCurly = sourceCode.getFirstToken(node);
-                const firstExpressionToken = sourceCode.getFirstToken(node.expression);
+                const closingCurly = sourceCode.getLastToken(node);
 
-                if (firstExpressionToken) {
-                    offsets.setDesiredOffset(firstExpressionToken, openingCurly, 1);
-                }
-
-                offsets.matchIndentOf(openingCurly, sourceCode.getLastToken(node));
+                offsets.setDesiredOffsets(
+                    sourceCode.getTokensBetween(openingCurly, closingCurly, { includeComments: true }),
+                    openingCurly,
+                    1
+                );
+                offsets.matchIndentOf(openingCurly, closingCurly);
             },
 
             "Program:exit"() {
diff --git a/lib/rules/multiline-ternary.js b/lib/rules/multiline-ternary.js
index de19bd43f..a74f241d8 100644
--- a/lib/rules/multiline-ternary.js
+++ b/lib/rules/multiline-ternary.js
@@ -20,13 +20,15 @@ module.exports = {
         },
         schema: [
             {
-                enum: ["always", "never"]
+                enum: ["always", "always-multiline", "never"]
             }
         ]
     },
 
     create(context) {
-        const multiline = context.options[0] !== "never";
+        const option = context.options[0];
+        const multiline = option !== "never";
+        const allowSingleLine = option === "always-multiline";
 
         //--------------------------------------------------------------------------
         // Helpers
@@ -69,6 +71,10 @@ module.exports = {
                         reportError(node.consequent, node, false);
                     }
                 } else {
+                    if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
+                        return;
+                    }
+
                     if (areTestAndConsequentOnSameLine) {
                         reportError(node.test, node, true);
                     }


Test patch:
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index dc65f57d4..f5a57e1ee 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -4649,6 +4649,44 @@ ruleTester.run("indent", rule, {
                     </span>
                 )
             `
+        },
+        {
+            code: unIndent`
+                <div>
+                    {
+                        /* foo */
+                    }
+                </div>
+            `
+        },
+
+        // https://github.com/eslint/eslint/issues/8832
+        {
+            code: unIndent`
+                <div>
+                    {
+                        (
+                            1
+                        )
+                    }
+                </div>
+            `
+        },
+        {
+            code: unIndent`
+                function A() {
+                    return (
+                        <div>
+                            {
+                                b && (
+                                    <div>
+                                    </div>
+                                )
+                            }
+                        </div>
+                    );
+                }
+            `
         }
     ],
 
@@ -8769,6 +8807,44 @@ ruleTester.run("indent", rule, {
                 )
             `,
             errors: expectedErrors([[5, 4, 8, "Punctuator"], [6, 4, 8, "Punctuator"], [7, 0, 4, "Punctuator"]])
+        },
+        {
+            code: unIndent`
+                <div>
+                    {
+                    (
+                        1
+                    )
+                    }
+                </div>
+            `,
+            output: unIndent`
+                <div>
+                    {
+                        (
+                            1
+                        )
+                    }
+                </div>
+            `,
+            errors: expectedErrors([[3, 8, 4, "Punctuator"], [4, 12, 8, "Numeric"], [5, 8, 4, "Punctuator"]])
+        },
+        {
+            code: unIndent`
+                <div>
+                    {
+                      /* foo */
+                    }
+                </div>
+            `,
+            output: unIndent`
+                <div>
+                    {
+                        /* foo */
+                    }
+                </div>
+            `,
+            errors: expectedErrors([3, 8, 6, "Block"])
         }
     ]
 });
diff --git a/tests/lib/rules/multiline-ternary.js b/tests/lib/rules/multiline-ternary.js
index 74221b6c0..30750246c 100644
--- a/tests/lib/rules/multiline-ternary.js
+++ b/tests/lib/rules/multiline-ternary.js
@@ -37,6 +37,17 @@ ruleTester.run("multiline-ternary", rule, {
         { code: "a\n? b\n? c\n: d\n: e", options: ["always"] },
         { code: "a\n? (b\n? c\n: d)\n: e", options: ["always"] },
 
+        // "always-multiline"
+        { code: "a\n? b\n: c", options: ["always-multiline"] },
+        { code: "a ?\nb :\nc", options: ["always-multiline"] },
+        { code: "a\n? b\n? c\n: d\n: e", options: ["always-multiline"] },
+        { code: "a\n? (b\n? c\n: d)\n: e", options: ["always-multiline"] },
+        { code: "a ? b : c", options: ["always-multiline"] },
+        { code: "a ? b ? c : d : e", options: ["always-multiline"] },
+        { code: "a ? (b ? c : d) : e", options: ["always-multiline"] },
+        { code: "a\n? (b ? c : d)\n: e", options: ["always-multiline"] },
+        { code: "a ?\n(b ? c : d) :\ne", options: ["always-multiline"] },
+
         // "never"
         { code: "a ? b : c", options: ["never"] },
         { code: "a ? b ? c : d : e", options: ["never"] },
@@ -282,6 +293,137 @@ ruleTester.run("multiline-ternary", rule, {
             }]
         },
 
+        // "always-multiline"
+        {
+            code: "a\n? b : c",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 3
+            }]
+        },
+        {
+            code: "a ? b\n: c",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            }]
+        },
+        {
+            code: "a &&\nb ? c : d",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            },
+            {
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 5
+            }]
+        },
+        {
+            code: "a ? b +\nc : d",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            },
+            {
+                message: expectedConsAltMsg,
+                line: 1,
+                column: 5
+            }]
+        },
+        {
+            code: "a ? b : c +\nd",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            },
+            {
+                message: expectedConsAltMsg,
+                line: 1,
+                column: 5
+            }]
+        },
+        {
+            code: "a ?\n(b ? c : d) : e",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 2
+            }]
+        },
+        {
+            code: "a ? (b ? c : d) :\ne",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            }]
+        },
+        {
+            code: "a ? (b\n? c\n: d) : e",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedTestConsMsg,
+                line: 1,
+                column: 1
+            },
+            {
+                message: expectedConsAltMsg,
+                line: 1,
+                column: 6
+            }]
+        },
+        {
+            code: "a ?\n(b ? c\n: d) : e",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 2
+            },
+            {
+                message: expectedTestConsMsg,
+                line: 2,
+                column: 2
+            }]
+        },
+        {
+            code: "a ?\n(b\n? c : d) : e",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 2
+            },
+            {
+                message: expectedConsAltMsg,
+                line: 3,
+                column: 3
+            }]
+        },
+        {
+            code: "a ?\n(b\n? c\n : d) : e",
+            options: ["always-multiline"],
+            errors: [{
+                message: expectedConsAltMsg,
+                line: 2,
+                column: 2
+            }]
+        },
+
         // "never"
         {
             code: "a\n? b : c",