diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js index a37ee4862ba..5d7f4d33c00 100755 --- a/conf/eslint-recommended.js +++ b/conf/eslint-recommended.js @@ -63,6 +63,7 @@ module.exports = { "linebreak-style": "off", "lines-around-comment": "off", "lines-around-directive": "off", + "lines-between-class-members": "off", "max-depth": "off", "max-len": "off", "max-lines": "off", @@ -221,7 +222,6 @@ module.exports = { "operator-assignment": "off", "operator-linebreak": "off", "padded-blocks": "off", - "padding-line-after-class-members": "off", "padding-line-between-statements": "off", "prefer-arrow-callback": "off", "prefer-const": "off", diff --git a/docs/rules/lines-between-class-members.md b/docs/rules/lines-between-class-members.md index fecd30be085..8acdcbbb7e4 100644 --- a/docs/rules/lines-between-class-members.md +++ b/docs/rules/lines-between-class-members.md @@ -1,6 +1,6 @@ -# require or disallow an empty line after after class members (lines-between-class-members) +# require or disallow an empty line between class members (lines-between-class-members) -This rule improves readability by enforcing lines between class members. it will not check empty lines before the first member and after the last member, since that is already taken care of by padded-blocks. +This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member, since that is already taken care of by padded-blocks. ## Rule Details @@ -35,7 +35,7 @@ class MyClass { ### Options -This rule has one option, which can be a string option or an object option. +This rule has a string option and an object option. String option: @@ -44,10 +44,8 @@ String option: Object option: -* `"multiline": "always"` require an empty line after after multiline class members -* `"multiline": "never"` disallows an empty line after after multiline class members -* `"singleline": "always"` require an empty line after after singleline class members -* `"singleline": "never"` disallows an empty line after after singleline class members +* `"exceptAfterSingleLine": "false"`(default) **do not** skip checking empty lines after singleline class members +* `"exceptAfterSingleLine": "true"` skip checking empty lines after singleline class members Examples of **incorrect** code for this rule with the string option: @@ -83,70 +81,23 @@ class Foo{ } ``` -Examples of **incorrect** code for this rule with the object option: +Examples of **correct** code for this rule with the object option: ```js -/* eslint lines-between-class-members: ["error", { multiline: "always" }]*/ -class Foo{ - bar(){ - bar(); - } - baz(){} -} - -/* eslint lines-between-class-members: ["error", { multiline: "never" }]*/ -class Foo{ - bar(){ - bar(); - } - - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "always" }]*/ -class Foo{ - bar(){} - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "never" }]*/ +/* eslint lines-between-class-members: ["error", "always", { exceptAfterSingleLine: true }]*/ class Foo{ bar(){} - baz(){} } ``` -Examples of **correct** code for this rule with the object option: - -```js -/* eslint lines-between-class-members: ["error", { multiline: "always" }]*/ -class Foo{ - bar(){ - bar(); - } - - baz(){} -} - -/* eslint lines-between-class-members: ["error", { multiline: "never" }]*/ -class Foo{ - bar(){ - bar(); - } - baz(){} -} +## When Not To Use It -/* eslint lines-between-class-members: ["error", { singleline: "always" }]*/ -class Foo{ - bar(){} +If you don't want enforce empty lines between class members, you can disable this rule. - baz(){} -} +## Related Rules -/* eslint lines-between-class-members: ["error", { singleline: "never" }]*/ -class Foo{ - bar(){} - baz(){} -} -``` +* [padded-blocks](padded-blocks.md) +* [padding-line-between-statement](padding-line-between-statement.md) +* [requirePaddingNewLinesAfterBlocks](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) +* [disallowPaddingNewLinesAfterBlocks](http://jscs.info/rule/disallowPaddingNewLinesAfterBlocks) diff --git a/docs/rules/padded-blocks.md b/docs/rules/padded-blocks.md index 1ea3b11990a..dd2c68ccb22 100644 --- a/docs/rules/padded-blocks.md +++ b/docs/rules/padded-blocks.md @@ -354,3 +354,8 @@ if (a) { ## When Not To Use It You can turn this rule off if you are not concerned with the consistency of padding within blocks. + +## Related Rules + +* [lines-between-class-members](lines-between-class-members.md) +* [padding-line-between-statement](padding-line-between-statement.md) diff --git a/lib/rules/lines-between-class-members.js b/lib/rules/lines-between-class-members.js index ba95ba5ba3f..48d16395bf8 100644 --- a/lib/rules/lines-between-class-members.js +++ b/lib/rules/lines-between-class-members.js @@ -28,27 +28,20 @@ module.exports = { type: "object", properties: { exceptAfterSingleLine: { - enum: [true, false] + type: "boolean" } }, - additionalProperties: false, - minProperties: 0 + additionalProperties: false } ] }, create(context) { - const options = {}; - const config = context.options[0] || "always"; + const options = []; - if (typeof config === "string") { - options.multiline = options.singleline = config; - } - - if (context.options[1].exceptAfterSingleLine) { - options.singleline = "never"; - } + options[0] = context.options[0] || "always"; + options[1] = context.options[1] || { exceptAfterSingleLine: false }; const ALWAYS_MESSAGE = "Expected blank line between class members."; const NEVER_MESSAGE = "Unexpected blank line between class members."; @@ -71,34 +64,34 @@ module.exports = { * @returns {void} undefined. */ function checkPadding(node) { - const body = node.body; for (let i = 0; i < body.length - 1; i++) { - // only check padding lines after methods definition. - if (body[i] && body[i].type !== "MethodDefinition") { - continue; - } - - const curFirst = sourceCode.getFirstToken(body[i]); - const curLast = sourceCode.getLastToken(body[i]); - const comments = sourceCode.getCommentsBefore(body[i + 1]); - const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]); - const isPadded = isPaddingBetweenTokens(curLast, nextFirst); - const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast); - - if ((isMulti && options.multiline && (options.multiline === "always") !== isPadded) || - !isMulti && options.singleline && (options.singleline === "always") !== isPadded) { - context.report({ - node: body[i + 1], - message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE, - fix(fixer) { - return isPadded - ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n") - : fixer.insertTextAfter(curLast, "\n"); - } - }); + // only check padding lines after class members(skip empty). + if (body[i]) { + + const curFirst = sourceCode.getFirstToken(body[i]); + const curLast = sourceCode.getLastToken(body[i]); + const comments = sourceCode.getCommentsBefore(body[i + 1]); + const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]); + const isPadded = isPaddingBetweenTokens(curLast, nextFirst); + const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast); + const skip = !isMulti && options[1].exceptAfterSingleLine; + + + if ((options[0] === "always" && !skip && !isPadded) || + (options[0] === "never" && isPadded)) { + context.report({ + node: body[i + 1], + message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE, + fix(fixer) { + return isPadded + ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n") + : fixer.insertTextAfter(curLast, "\n"); + } + }); + } } } } diff --git a/tests/lib/rules/lines-between-class-members.js b/tests/lib/rules/lines-between-class-members.js index 7e51c09f5a2..dfa17670b64 100644 --- a/tests/lib/rules/lines-between-class-members.js +++ b/tests/lib/rules/lines-between-class-members.js @@ -25,7 +25,7 @@ const NEVER_MESSAGE = "Unexpected blank line between class members."; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); -ruleTester.run("lines-between-class-methods", rule, { +ruleTester.run("lines-between-class-members", rule, { valid: [ "class foo{}", "class foo{\n\n}", @@ -44,124 +44,25 @@ ruleTester.run("lines-between-class-methods", rule, { { code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", options: ["always"] }, { code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", options: ["always"] }, - { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ multiline: "always" }] }, - - { code: "class foo{ bar(){\n}\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){\n}\n/*comments*/baz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){\n}\n//comments\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ multiline: "never" }] }, - - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ singleline: "always" }] }, - { code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", options: [{ singleline: "always" }] }, - { code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", options: [{ singleline: "always" }] }, - - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ singleline: "never" }] }, - { code: "class foo{ bar(){}\n/*comments*/baz(){}}", options: [{ singleline: "never" }] }, - { code: "class foo{ bar(){}\n//comments\nbaz(){}}", options: [{ singleline: "never" }] } + { code: "class foo{ bar(){}\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] }, + { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] } ], invalid: [ { code: "class foo{ bar(){}\nbaz(){}}", output: "class foo{ bar(){}\n\nbaz(){}}", - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n\n//comments\nbaz(){}}", + options: ["always"], errors: [{ message: ALWAYS_MESSAGE }] }, { code: "class foo{ bar(){}\n\nbaz(){}}", output: "class foo{ bar(){}\nbaz(){}}", options: ["never"], errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n/*comments*/baz(){}}", - options: ["never"], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n//comments\nbaz(){}}", - options: ["never"], - errors: [{ message: NEVER_MESSAGE }] }, { code: "class foo{ bar(){\n}\nbaz(){}}", output: "class foo{ bar(){\n}\n\nbaz(){}}", - options: [{ multiline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n/*comments*/baz(){}}", - output: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", - options: [{ multiline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n//comments\nbaz(){}}", - output: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", - options: [{ multiline: "always" }], + options: ["always", { exceptAfterSingleLine: true }], errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\nbaz(){}}", - output: "class foo{ bar(){\n}\nbaz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){\n}\n/*comments*/baz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){\n}\n//comments\nbaz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\nbaz(){}}", - output: "class foo{ bar(){}\n\nbaz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\nbaz(){}}", - output: "class foo{ bar(){}\nbaz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n/*comments*/baz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n//comments\nbaz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\nbaz(){}\n\nfn(){}}", - output: "class foo{ bar(){\n}\n\nbaz(){}\nfn(){}}", - options: [{ multiline: "always", singleline: "never" }], - errors: [{ message: ALWAYS_MESSAGE }, { message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\nbaz(){}\nfn(){}}", - output: "class foo{ bar(){\n}\nbaz(){}\n\nfn(){}}", - options: [{ multiline: "never", singleline: "always" }], - errors: [{ message: NEVER_MESSAGE }, { message: ALWAYS_MESSAGE }] } ] });