From b921d1fc99b0955d69026286c8fdbe9cad739c20 Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Thu, 8 Dec 2016 11:19:02 -0500 Subject: [PATCH] Update: add `indent` options for array and object literals (fixes #7473) (#7681) * Update: add `indent` options for array and object literals (fixes #7473) * Fix crash with empty arrays --- docs/rules/indent.md | 95 +++++++++++++++ lib/rules/indent.js | 60 +++++++--- tests/lib/rules/indent.js | 243 +++++++++++++++++++++++++++++++++++++- 3 files changed, 383 insertions(+), 15 deletions(-) diff --git a/docs/rules/indent.md b/docs/rules/indent.md index 40de404b5d3..95b1517849c 100644 --- a/docs/rules/indent.md +++ b/docs/rules/indent.md @@ -82,6 +82,8 @@ This rule has an object option: * `body` (default: 1) enforces indentation level for the body of a function expression. * `"CallExpression"` takes an object to define rules for function call expressions. * `arguments` (off by default) enforces indentation level for arguments in a call expression. This can either be a number indicating indentation level, or the string `"first"` indicating that all arguments of the expression must be aligned with the first argument. +* `"ArrayExpression"` (default: 1) enforces indentation level for elements in arrays. It can also be set to the string `"first"`, indicating that all the elements in the array should be aligned with the first element. +* `"ObjectExpression"` (default: 1) enforces indentation level for properties in objects. It can be set to the string `"first"`, indicating that all properties in the object should be aligned with the first property. Level of indentation denotes the multiple of the indent specified. Example: @@ -430,6 +432,99 @@ foo(bar, baz, baz, boop, beep); ``` +### ArrayExpression + +Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": 1 }` option: + +```js +/*eslint indent: ["error", 2, { "ArrayExpression": 1 }]*/ + +var foo = [ + bar, +baz, + qux +]; +``` + +Examples of **correct** code for this rule with the `2, { "ArrayExpression": 1 }` option: + +```js +/*eslint indent: ["error", 2, { "ArrayExpression": 1 }]*/ + +var foo = [ + bar, + baz, + qux +]; +``` + +Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": "first" }` option: + +```js +/*eslint indent: ["error", 2, {"ArrayExpression": "first"}]*/ + +var foo = [bar, + baz, + qux +]; +``` + +Examples of **correct** code for this rule with the `2, { "ArrayExpression": "first" }` option: + +```js +/*eslint indent: ["error", 2, {"ArrayExpression": "first"}]*/ + +var foo = [bar, + baz, + qux +]; +``` + +### ObjectExpression + +Examples of **incorrect** code for this rule with the `2, { "ObjectExpression": 1 }` option: + +```js +/*eslint indent: ["error", 2, { "ObjectExpression": 1 }]*/ + +var foo = { + bar: 1, +baz: 2, + qux: 3 +}; +``` + +Examples of **correct** code for this rule with the `2, { "ObjectExpression": 1 }` option: + +```js +/*eslint indent: ["error", 2, { "ObjectExpression": 1 }]*/ + +var foo = { + bar: 1, + baz: 2, + qux: 3 +}; +``` + +Examples of **incorrect** code for this rule with the `2, { "ObjectExpression": "first" }` option: + +```js +/*eslint indent: ["error", 2, {"ObjectExpression": "first"}]*/ + +var foo = { bar: 1, + baz: 2 }; +``` + +Examples of **correct** code for this rule with the `2, { "ObjectExpression": "first" }` option: + +```js +/*eslint indent: ["error", 2, {"ObjectExpression": "first"}]*/ + +var foo = { bar: 1, + baz: 2 }; +``` + + ## Compatibility * **JSHint**: `indent` diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 7e60a8d00fb..813528435f4 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -129,6 +129,28 @@ module.exports = { ] } } + }, + ArrayExpression: { + oneOf: [ + { + type: "integer", + minimum: 0 + }, + { + enum: ["first"] + } + ] + }, + ObjectExpression: { + oneOf: [ + { + type: "integer", + minimum: 0 + }, + { + enum: ["first"] + } + ] } }, additionalProperties: false @@ -161,7 +183,9 @@ module.exports = { }, CallExpression: { arguments: DEFAULT_PARAMETER_INDENT - } + }, + ArrayExpression: 1, + ObjectExpression: 1 }; const sourceCode = context.getSourceCode(); @@ -210,6 +234,14 @@ module.exports = { if (typeof opts.CallExpression === "object") { Object.assign(options.CallExpression, opts.CallExpression); } + + if (typeof opts.ArrayExpression === "number" || typeof opts.ArrayExpression === "string") { + options.ArrayExpression = opts.ArrayExpression; + } + + if (typeof opts.ObjectExpression === "number" || typeof opts.ObjectExpression === "string") { + options.ObjectExpression = opts.ObjectExpression; + } } } @@ -694,27 +726,27 @@ module.exports = { if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) { if (parent.type === "VariableDeclarator" && parentVarNode.loc.start.line === parent.loc.start.line) { nodeIndent = nodeIndent + (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]); - } else if ( - parent.type === "ObjectExpression" || - parent.type === "ArrayExpression" || - parent.type === "CallExpression" || - parent.type === "ArrowFunctionExpression" || - parent.type === "NewExpression" || - parent.type === "LogicalExpression" - ) { - nodeIndent = nodeIndent + indentSize; + } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") { + nodeIndent += options[parent.type] * indentSize; + } else if (parent.type === "CallExpression" || parent.type === "NewExpression") { + nodeIndent += (typeof options.CallExpression.arguments === "number" ? options.CallExpression.arguments : 1) * indentSize; + } else if (parent.type === "LogicalExpression" || parent.type === "ArrowFunctionExpression") { + nodeIndent += indentSize; } } } else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent) && parent.type !== "MemberExpression" && parent.type !== "ExpressionStatement" && parent.type !== "AssignmentExpression" && parent.type !== "Property") { nodeIndent = nodeIndent + indentSize; } - elementsIndent = nodeIndent + indentSize; - checkFirstNodeLineIndent(node, nodeIndent); } else { nodeIndent = getNodeIndent(node).goodChar; - elementsIndent = nodeIndent + indentSize; + } + + if (options[node.type] === "first") { + elementsIndent = elements.length ? elements[0].loc.start.column : 0; // If there are no elements, elementsIndent doesn't matter. + } else { + elementsIndent = nodeIndent + indentSize * options[node.type]; } /* @@ -735,7 +767,7 @@ module.exports = { } } - checkLastNodeLineIndent(node, elementsIndent - indentSize); + checkLastNodeLineIndent(node, nodeIndent + (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0)); } /** diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 0542a5d00d7..17df56c8d93 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -1726,6 +1726,109 @@ ruleTester.run("indent", rule, { " foo\n" + ")", parserOptions: {ecmaFeatures: {globalReturn: true}} + }, + { + code: + "var foo = [\n" + + " bar,\n" + + " baz\n" + + "]" + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]" + }, + { + code: + "var foo = [bar,\n" + + "baz,\n" + + "qux\n" + + "]", + options: [2, {ArrayExpression: 0}] + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + options: [2, {ArrayExpression: 8}] + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + options: [2, {ArrayExpression: "first"}] + }, + { + code: + "var foo = [bar,\n" + + " baz, qux\n" + + "]", + options: [2, {ArrayExpression: "first"}] + }, + { + code: + "var foo = [\n" + + " { bar: 1,\n" + + " baz: 2 },\n" + + " { bar: 3,\n" + + " qux: 4 }\n" + + "]", + options: [4, {ArrayExpression: 2, ObjectExpression: "first"}] + }, + { + code: + "var foo = {\n" + + "bar: 1,\n" + + "baz: 2\n" + + "};", + options: [2, {ObjectExpression: 0}] + }, + { + code: + "var foo = { foo: 1, bar: 2,\n" + + " baz: 3 }", + options: [2, {ObjectExpression: "first"}] + }, + { + code: + "var foo = [\n" + + " {\n" + + " foo: 1\n" + + " }\n" + + "]", + options: [4, {ArrayExpression: 2}] + }, + { + code: + "function foo() {\n" + + " [\n" + + " foo\n" + + " ]\n" + + "}", + options: [2, {ArrayExpression: 4}] + }, + { + code: "[\n]", + options: [2, {ArrayExpression: "first"}] + }, + { + code: "[\n]", + options: [2, {ArrayExpression: 1}] + }, + { + code: "{\n}", + options: [2, {ObjectExpression: "first"}] + }, + { + code: "{\n}", + options: [2, {ObjectExpression: 1}] } ], invalid: [ @@ -3530,6 +3633,144 @@ ruleTester.run("indent", rule, { " ok: true" + " });", errors: expectedErrors([2, 4, 8, "ObjectExpression"]) - } + }, + { + code: + "var foo = [\n" + + " bar,\n" + + " baz\n" + + " ]", + output: + "var foo = [\n" + + " bar,\n" + + " baz\n" + + "]", + errors: expectedErrors([[2, 4, 11, "Identifier"], [3, 4, 2, "Identifier"], [4, 0, 10, "ArrayExpression"]]) + }, + { + code: + "var foo = [bar,\n" + + "baz,\n" + + " qux\n" + + "]", + output: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + errors: expectedErrors([2, 4, 0, "Identifier"]) + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + output: + "var foo = [bar,\n" + + "baz,\n" + + "qux\n" + + "]", + options: [2, {ArrayExpression: 0}], + errors: expectedErrors([[2, 0, 2, "Identifier"], [3, 0, 2, "Identifier"]]) + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + output: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + options: [2, {ArrayExpression: 8}], + errors: expectedErrors([[2, 16, 2, "Identifier"], [3, 16, 2, "Identifier"]]) + }, + { + code: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + output: + "var foo = [bar,\n" + + " baz,\n" + + " qux\n" + + "]", + options: [2, {ArrayExpression: "first"}], + errors: expectedErrors([[2, 11, 4, "Identifier"], [3, 11, 4, "Identifier"]]) + }, + { + code: + "var foo = [bar,\n" + + " baz, qux\n" + + "]", + output: + "var foo = [bar,\n" + + " baz, qux\n" + + "]", + options: [2, {ArrayExpression: "first"}], + errors: expectedErrors([2, 11, 4, "Identifier"]) + }, + { + code: + "var foo = [\n" + + " { bar: 1,\n" + + " baz: 2 },\n" + + " { bar: 3,\n" + + " qux: 4 }\n" + + "]", + output: + "var foo = [\n" + + " { bar: 1,\n" + + " baz: 2 },\n" + + " { bar: 3,\n" + + " qux: 4 }\n" + + "]", + options: [4, {ArrayExpression: 2, ObjectExpression: "first"}], + errors: expectedErrors([[3, 10, 12, "Property"], [5, 10, 12, "Property"]]) + }, + { + code: + "var foo = {\n" + + " bar: 1,\n" + + " baz: 2\n" + + "};", + output: + "var foo = {\n" + + "bar: 1,\n" + + "baz: 2\n" + + "};", + options: [2, {ObjectExpression: 0}], + errors: expectedErrors([[2, 0, 2, "Property"], [3, 0, 2, "Property"]]) + }, + { + code: + "var quux = { foo: 1, bar: 2,\n" + + "baz: 3 }", + output: + "var quux = { foo: 1, bar: 2,\n" + + " baz: 3 }", + options: [2, {ObjectExpression: "first"}], + errors: expectedErrors([2, 13, 0, "Property"]) + }, + { + code: + "function foo() {\n" + + " [\n" + + " foo\n" + + " ]\n" + + "}", + output: + "function foo() {\n" + + " [\n" + + " foo\n" + + " ]\n" + + "}", + options: [2, {ArrayExpression: 4}], + errors: expectedErrors([2, 2, 4, "ExpressionStatement"]) + }, ] });