| /** |
| * @fileoverview Rule to check empty newline between class members |
| * @author 薛定谔的猫<hh_2013@foxmail.com> |
| */ |
| "use strict"; |
| |
| const astUtils = require("./utils/ast-utils"); |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| type: "layout", |
| |
| docs: { |
| description: "require or disallow an empty line between class members", |
| category: "Stylistic Issues", |
| recommended: false, |
| url: "https://eslint.org/docs/rules/lines-between-class-members" |
| }, |
| |
| fixable: "whitespace", |
| |
| schema: [ |
| { |
| enum: ["always", "never"] |
| }, |
| { |
| type: "object", |
| properties: { |
| exceptAfterSingleLine: { |
| type: "boolean", |
| default: false |
| } |
| }, |
| additionalProperties: false |
| } |
| ], |
| messages: { |
| never: "Unexpected blank line between class members.", |
| always: "Expected blank line between class members." |
| } |
| }, |
| |
| create(context) { |
| |
| const options = []; |
| |
| options[0] = context.options[0] || "always"; |
| options[1] = context.options[1] || { exceptAfterSingleLine: false }; |
| |
| const sourceCode = context.getSourceCode(); |
| |
| /** |
| * Checks if there is padding between two tokens |
| * @param {Token} first The first token |
| * @param {Token} second The second token |
| * @returns {boolean} True if there is at least a line between the tokens |
| */ |
| function isPaddingBetweenTokens(first, second) { |
| const comments = sourceCode.getCommentsBefore(second); |
| const len = comments.length; |
| |
| // If there is no comments |
| if (len === 0) { |
| const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1; |
| |
| return linesBetweenFstAndSnd >= 1; |
| } |
| |
| |
| // If there are comments |
| let sumOfCommentLines = 0; // the numbers of lines of comments |
| let prevCommentLineNum = -1; // line number of the end of the previous comment |
| |
| for (let i = 0; i < len; i++) { |
| const commentLinesOfThisComment = comments[i].loc.end.line - comments[i].loc.start.line + 1; |
| |
| sumOfCommentLines += commentLinesOfThisComment; |
| |
| /* |
| * If this comment and the previous comment are in the same line, |
| * the count of comment lines is duplicated. So decrement sumOfCommentLines. |
| */ |
| if (prevCommentLineNum === comments[i].loc.start.line) { |
| sumOfCommentLines -= 1; |
| } |
| |
| prevCommentLineNum = comments[i].loc.end.line; |
| } |
| |
| /* |
| * If the first block and the first comment are in the same line, |
| * the count of comment lines is duplicated. So decrement sumOfCommentLines. |
| */ |
| if (first.loc.end.line === comments[0].loc.start.line) { |
| sumOfCommentLines -= 1; |
| } |
| |
| /* |
| * If the last comment and the second block are in the same line, |
| * the count of comment lines is duplicated. So decrement sumOfCommentLines. |
| */ |
| if (comments[len - 1].loc.end.line === second.loc.start.line) { |
| sumOfCommentLines -= 1; |
| } |
| |
| const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1; |
| |
| return linesBetweenFstAndSnd - sumOfCommentLines >= 1; |
| } |
| |
| return { |
| ClassBody(node) { |
| const body = node.body; |
| |
| for (let i = 0; i < body.length - 1; i++) { |
| const curFirst = sourceCode.getFirstToken(body[i]); |
| const curLast = sourceCode.getLastToken(body[i]); |
| const nextFirst = 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], |
| messageId: isPadded ? "never" : "always", |
| fix(fixer) { |
| return isPadded |
| ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n") |
| : fixer.insertTextAfter(curLast, "\n"); |
| } |
| }); |
| } |
| } |
| } |
| }; |
| } |
| }; |