1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 | const astUtils = require("../ast-utils");
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | module.exports = {
|
14 | meta: {
|
15 | docs: {
|
16 | description: "require or disallow an empty line between class members",
|
17 | category: "Stylistic Issues",
|
18 | recommended: false,
|
19 | url: "https://eslint.org/docs/rules/lines-between-class-members"
|
20 | },
|
21 |
|
22 | fixable: "whitespace",
|
23 |
|
24 | schema: [
|
25 | {
|
26 | enum: ["always", "never"]
|
27 | },
|
28 | {
|
29 | type: "object",
|
30 | properties: {
|
31 | exceptAfterSingleLine: {
|
32 | type: "boolean"
|
33 | }
|
34 | },
|
35 | additionalProperties: false
|
36 | }
|
37 | ]
|
38 | },
|
39 |
|
40 | create(context) {
|
41 |
|
42 | const options = [];
|
43 |
|
44 | options[0] = context.options[0] || "always";
|
45 | options[1] = context.options[1] || { exceptAfterSingleLine: false };
|
46 |
|
47 | const ALWAYS_MESSAGE = "Expected blank line between class members.";
|
48 | const NEVER_MESSAGE = "Unexpected blank line between class members.";
|
49 |
|
50 | const sourceCode = context.getSourceCode();
|
51 |
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | function isPaddingBetweenTokens(first, second) {
|
59 | const comments = sourceCode.getCommentsBefore(second);
|
60 | const len = comments.length;
|
61 |
|
62 |
|
63 | if (len === 0) {
|
64 | const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
|
65 |
|
66 | return linesBetweenFstAndSnd >= 1;
|
67 | }
|
68 |
|
69 |
|
70 |
|
71 | let sumOfCommentLines = 0;
|
72 | let prevCommentLineNum = -1;
|
73 |
|
74 | for (let i = 0; i < len; i++) {
|
75 | const commentLinesOfThisComment = comments[i].loc.end.line - comments[i].loc.start.line + 1;
|
76 |
|
77 | sumOfCommentLines += commentLinesOfThisComment;
|
78 |
|
79 | |
80 |
|
81 |
|
82 |
|
83 | if (prevCommentLineNum === comments[i].loc.start.line) {
|
84 | sumOfCommentLines -= 1;
|
85 | }
|
86 |
|
87 | prevCommentLineNum = comments[i].loc.end.line;
|
88 | }
|
89 |
|
90 | |
91 |
|
92 |
|
93 |
|
94 | if (first.loc.end.line === comments[0].loc.start.line) {
|
95 | sumOfCommentLines -= 1;
|
96 | }
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 | if (comments[len - 1].loc.end.line === second.loc.start.line) {
|
103 | sumOfCommentLines -= 1;
|
104 | }
|
105 |
|
106 | const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
|
107 |
|
108 | return linesBetweenFstAndSnd - sumOfCommentLines >= 1;
|
109 | }
|
110 |
|
111 | return {
|
112 | ClassBody(node) {
|
113 | const body = node.body;
|
114 |
|
115 | for (let i = 0; i < body.length - 1; i++) {
|
116 | const curFirst = sourceCode.getFirstToken(body[i]);
|
117 | const curLast = sourceCode.getLastToken(body[i]);
|
118 | const nextFirst = sourceCode.getFirstToken(body[i + 1]);
|
119 | const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
|
120 | const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
|
121 | const skip = !isMulti && options[1].exceptAfterSingleLine;
|
122 |
|
123 |
|
124 | if ((options[0] === "always" && !skip && !isPadded) ||
|
125 | (options[0] === "never" && isPadded)) {
|
126 | context.report({
|
127 | node: body[i + 1],
|
128 | message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE,
|
129 | fix(fixer) {
|
130 | return isPadded
|
131 | ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
|
132 | : fixer.insertTextAfter(curLast, "\n");
|
133 | }
|
134 | });
|
135 | }
|
136 | }
|
137 | }
|
138 | };
|
139 | }
|
140 | };
|