You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
6.2 KiB
151 lines
6.2 KiB
/** |
|
* @fileoverview Disallows multiple blank lines. |
|
* implementation adapted from the no-trailing-spaces rule. |
|
* @author Greg Cochard |
|
*/ |
|
"use strict"; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Rule Definition |
|
//------------------------------------------------------------------------------ |
|
|
|
/** @type {import('../shared/types').Rule} */ |
|
module.exports = { |
|
meta: { |
|
type: "layout", |
|
|
|
docs: { |
|
description: "Disallow multiple empty lines", |
|
recommended: false, |
|
url: "https://eslint.org/docs/rules/no-multiple-empty-lines" |
|
}, |
|
|
|
fixable: "whitespace", |
|
|
|
schema: [ |
|
{ |
|
type: "object", |
|
properties: { |
|
max: { |
|
type: "integer", |
|
minimum: 0 |
|
}, |
|
maxEOF: { |
|
type: "integer", |
|
minimum: 0 |
|
}, |
|
maxBOF: { |
|
type: "integer", |
|
minimum: 0 |
|
} |
|
}, |
|
required: ["max"], |
|
additionalProperties: false |
|
} |
|
], |
|
|
|
messages: { |
|
blankBeginningOfFile: "Too many blank lines at the beginning of file. Max of {{max}} allowed.", |
|
blankEndOfFile: "Too many blank lines at the end of file. Max of {{max}} allowed.", |
|
consecutiveBlank: "More than {{max}} blank {{pluralizedLines}} not allowed." |
|
} |
|
}, |
|
|
|
create(context) { |
|
|
|
// Use options.max or 2 as default |
|
let max = 2, |
|
maxEOF = max, |
|
maxBOF = max; |
|
|
|
if (context.options.length) { |
|
max = context.options[0].max; |
|
maxEOF = typeof context.options[0].maxEOF !== "undefined" ? context.options[0].maxEOF : max; |
|
maxBOF = typeof context.options[0].maxBOF !== "undefined" ? context.options[0].maxBOF : max; |
|
} |
|
|
|
const sourceCode = context.getSourceCode(); |
|
|
|
// Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue |
|
const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines; |
|
const templateLiteralLines = new Set(); |
|
|
|
//-------------------------------------------------------------------------- |
|
// Public |
|
//-------------------------------------------------------------------------- |
|
|
|
return { |
|
TemplateLiteral(node) { |
|
node.quasis.forEach(literalPart => { |
|
|
|
// Empty lines have a semantic meaning if they're inside template literals. Don't count these as empty lines. |
|
for (let ignoredLine = literalPart.loc.start.line; ignoredLine < literalPart.loc.end.line; ignoredLine++) { |
|
templateLiteralLines.add(ignoredLine); |
|
} |
|
}); |
|
}, |
|
"Program:exit"(node) { |
|
return allLines |
|
|
|
// Given a list of lines, first get a list of line numbers that are non-empty. |
|
.reduce((nonEmptyLineNumbers, line, index) => { |
|
if (line.trim() || templateLiteralLines.has(index + 1)) { |
|
nonEmptyLineNumbers.push(index + 1); |
|
} |
|
return nonEmptyLineNumbers; |
|
}, []) |
|
|
|
// Add a value at the end to allow trailing empty lines to be checked. |
|
.concat(allLines.length + 1) |
|
|
|
// Given two line numbers of non-empty lines, report the lines between if the difference is too large. |
|
.reduce((lastLineNumber, lineNumber) => { |
|
let messageId, maxAllowed; |
|
|
|
if (lastLineNumber === 0) { |
|
messageId = "blankBeginningOfFile"; |
|
maxAllowed = maxBOF; |
|
} else if (lineNumber === allLines.length + 1) { |
|
messageId = "blankEndOfFile"; |
|
maxAllowed = maxEOF; |
|
} else { |
|
messageId = "consecutiveBlank"; |
|
maxAllowed = max; |
|
} |
|
|
|
if (lineNumber - lastLineNumber - 1 > maxAllowed) { |
|
context.report({ |
|
node, |
|
loc: { |
|
start: { line: lastLineNumber + maxAllowed + 1, column: 0 }, |
|
end: { line: lineNumber, column: 0 } |
|
}, |
|
messageId, |
|
data: { |
|
max: maxAllowed, |
|
pluralizedLines: maxAllowed === 1 ? "line" : "lines" |
|
}, |
|
fix(fixer) { |
|
const rangeStart = sourceCode.getIndexFromLoc({ line: lastLineNumber + 1, column: 0 }); |
|
|
|
/* |
|
* The end of the removal range is usually the start index of the next line. |
|
* However, at the end of the file there is no next line, so the end of the |
|
* range is just the length of the text. |
|
*/ |
|
const lineNumberAfterRemovedLines = lineNumber - maxAllowed; |
|
const rangeEnd = lineNumberAfterRemovedLines <= allLines.length |
|
? sourceCode.getIndexFromLoc({ line: lineNumberAfterRemovedLines, column: 0 }) |
|
: sourceCode.text.length; |
|
|
|
return fixer.removeRange([rangeStart, rangeEnd]); |
|
} |
|
}); |
|
} |
|
|
|
return lineNumber; |
|
}, 0); |
|
} |
|
}; |
|
} |
|
};
|
|
|