Repository: eslint/eslint (js)

Issue:
Indent should ignore indentation of variable assigment in multiline variable declaration
**Tell us about your environment**
* **ESLint Version:**
master
* **Node Version:**
6.10
* **npm Version:**
3.10


**What parser (default, Babel-ESLint, etc.) are you using?**
typescript
**Please show your full configuration:**

```js
module.exports = {
//    "extends": ["eslint:all"],
    "parser": "typescript-eslint-parser",
    "parserOptions": {
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "rules": {
        "indent": "error"
    }
};
```

**What did you do? Please include the actual source code causing the issue.**

Invalid
```js
const fooLongName
        = 1,
    barLongName
        = 2,
    bazLongName
        = 3;
```

**What did you expect to happen?**

I expected this to be a valid indentation style. The indent-legacy rule allows for this style. Also if equal operators are on the previous line it is considered a valid indentation.

Valid
```js
const fooLongName =
        1,
    barLongName =
        2,
    bazLongName =
        3;
```



**What actually happened? Please include the actual, raw output from ESLint.**
It is expected to be indented like so:

```js
const fooLongName
    = 1,
    barLongName
    = 2,
    bazLongName
    = 3;
```

```
/mnt/c/Users/reyad.attiyat/Workspace/eslint-test/test-var-indent.ts
  2:1  error  Expected indentation of 4 spaces but found 8  indent
  4:1  error  Expected indentation of 4 spaces but found 8  indent
  6:1  error  Expected indentation of 4 spaces but found 8  indent
```

I can a create a PR to fix this. Just want to confirm this is not intended behavior. When the equal operator is on the line with the variable identifier the assignment indentation is ignored completely. We should probably do the same when the equal operator is on the same line as the assignment.

I have tested the changes made in https://github.com/eslint/eslint/pull/8498 and it does not fix this case.

Pull request description:
Fix: Indent Ignore Variable Declaration init operator (fixes #8546)
**What is the purpose of this pull request? (put an "X" next to item)**

[X ] Bug fix (fixes #8546 )

**What changes did you make? (Give an overview)**
Allow the VariableDeclarator node listener to ignore the operator as well as the initial value.


**Is there anything you'd like reviewers to focus on?**
I tried to match the behavior of variable assignment but might have missed something. The following are now all valid:
```js
const aLongName 
    = 1,
  bLongName
    = 2;
```
```js
const aLongName 
    =
  1,
  bLongName
     =
     2;
```





Patch:
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 0e6154d55..30d9401dc 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -1039,6 +1039,9 @@ module.exports = {
 
             VariableDeclarator(node) {
                 if (node.init) {
+                    const equalOperator = sourceCode.getTokenBefore(node.init, astUtils.isNotOpeningParenToken);
+
+                    offsets.ignoreToken(equalOperator);
                     offsets.ignoreToken(sourceCode.getFirstToken(node.init));
                 }
             },
diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js
index a867a9390..43a8bb77d 100644
--- a/lib/rules/prefer-arrow-callback.js
+++ b/lib/rules/prefer-arrow-callback.js
@@ -277,15 +277,23 @@ module.exports = {
                             const paramsRightParen = sourceCode.getTokenBefore(node.body);
                             const asyncKeyword = node.async ? "async " : "";
                             const paramsFullText = sourceCode.text.slice(paramsLeftParen.range[0], paramsRightParen.range[1]);
-
-                            if (callbackInfo.isLexicalThis) {
-
-                                // If the callback function has `.bind(this)`, replace it with an arrow function and remove the binding.
-                                return fixer.replaceText(node.parent.parent, `${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`);
-                            }
-
-                            // Otherwise, only replace the `function` keyword and parameters with the arrow function parameters.
-                            return fixer.replaceTextRange([node.start, node.body.start], `${asyncKeyword}${paramsFullText} => `);
+                            const arrowFunctionText = `${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`;
+
+                            /*
+                             * If the callback function has `.bind(this)`, replace it with an arrow function and remove the binding.
+                             * Otherwise, just replace the arrow function itself.
+                             */
+                            const replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node;
+
+                            /*
+                             * If the replaced node is part of a BinaryExpression, LogicalExpression, or MemberExpression, then
+                             * the arrow function needs to be parenthesized, because `foo || () => {}` is invalid syntax even
+                             * though `foo || function() {}` is valid.
+                             */
+                            const needsParens = replacedNode.parent.type !== "CallExpression" && replacedNode.parent.type !== "ConditionalExpression";
+                            const replacementText = needsParens ? `(${arrowFunctionText})` : arrowFunctionText;
+
+                            return fixer.replaceText(replacedNode, replacementText);
                         }
                     });
                 }


Test patch:
diff --git a/tests/fixtures/config-hierarchy/overwrite-ecmaFeatures/.eslintrc b/tests/fixtures/config-hierarchy/overwrite-ecmaFeatures/.eslintrc
index 4ac0a584e..f61c28002 100644
--- a/tests/fixtures/config-hierarchy/overwrite-ecmaFeatures/.eslintrc
+++ b/tests/fixtures/config-hierarchy/overwrite-ecmaFeatures/.eslintrc
@@ -1,5 +1,7 @@
 {
-    "ecmaFeatures": {
-        "globalReturn": false
+    "parserOptions": {
+        "ecmaFeatures": {
+            "globalReturn": false
+        }
     }
 }
diff --git a/tests/lib/config.js b/tests/lib/config.js
index 1ebd20c2f..4e2ac050e 100644
--- a/tests/lib/config.js
+++ b/tests/lib/config.js
@@ -773,13 +773,13 @@ describe("Config", () => {
         });
 
         describe("with env in a child configuration file", () => {
-            xit("should overwrite parserOptions of the parent with env of the child", () => {
+            it("should not overwrite parserOptions of the parent with env of the child", () => {
                 const config = new Config({ cwd: process.cwd() });
                 const targetPath = getFixturePath("overwrite-ecmaFeatures", "child", "foo.js");
                 const expected = {
                     rules: {},
                     env: { commonjs: true },
-                    parserOptions: { ecmaFeatures: { globalReturn: true } }
+                    parserOptions: { ecmaFeatures: { globalReturn: false } }
                 };
                 const actual = config.getConfig(targetPath);
 
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index 2d56b7297..dbe140a44 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -529,6 +529,43 @@ ruleTester.run("indent", rule, {
             `,
             options: [2, { VariableDeclarator: 1 }]
         },
+        {
+            code: unIndent`
+                var foo = 1,
+                  bar
+                    = 2
+            `,
+            options: [2, { VariableDeclarator: 1 }]
+        },
+        {
+            code: unIndent`
+                var foo
+                  = 1,
+                  bar
+                    = 2
+            `,
+            options: [2, { VariableDeclarator: 1 }]
+        },
+        {
+            code: unIndent`
+                var foo
+                  =
+                  1,
+                  bar
+                    =
+                    2
+            `,
+            options: [2, { VariableDeclarator: 1 }]
+        },
+        {
+            code: unIndent`
+                var foo
+                  = (1),
+                  bar
+                    = (2)
+            `,
+            options: [2, { VariableDeclarator: 1 }]
+        },
         {
             code: unIndent`
                 var foo = 1,
diff --git a/tests/lib/rules/prefer-arrow-callback.js b/tests/lib/rules/prefer-arrow-callback.js
index c73cb825c..3480849a8 100644
--- a/tests/lib/rules/prefer-arrow-callback.js
+++ b/tests/lib/rules/prefer-arrow-callback.js
@@ -70,7 +70,7 @@ ruleTester.run("prefer-arrow-callback", rule, {
         {
             code: "foo(nativeCb || function() {});",
             errors,
-            output: "foo(nativeCb || () => {});"
+            output: "foo(nativeCb || (() => {}));"
         },
         {
             code: "foo(bar ? function() {} : function() {});",
@@ -87,6 +87,11 @@ ruleTester.run("prefer-arrow-callback", rule, {
             errors,
             output: "foo(() => { this; });"
         },
+        {
+            code: "foo(bar || function() { this; }.bind(this));",
+            errors,
+            output: "foo(bar || (() => { this; }));"
+        },
         {
             code: "foo(function() { (() => this); }.bind(this));",
             errors,
@@ -134,6 +139,11 @@ ruleTester.run("prefer-arrow-callback", rule, {
             errors,
             output: "qux((foo, bar, baz) => { return foo * this.qux; })"
         },
+        {
+            code: "foo(function() {}.bind(this, somethingElse))",
+            errors,
+            output: "foo((() => {}).bind(this, somethingElse))"
+        },
         {
             code: "qux(function(foo = 1, [bar = 2] = [], {qux: baz = 3} = {foo: 'bar'}) { return foo + bar; });",
             errors,