diff options
Diffstat (limited to 'tools/eslint/lib/rules/newline-after-var.js')
-rw-r--r-- | tools/eslint/lib/rules/newline-after-var.js | 61 |
1 files changed, 55 insertions, 6 deletions
diff --git a/tools/eslint/lib/rules/newline-after-var.js b/tools/eslint/lib/rules/newline-after-var.js index e1ade14bde..9e7d859931 100644 --- a/tools/eslint/lib/rules/newline-after-var.js +++ b/tools/eslint/lib/rules/newline-after-var.js @@ -3,6 +3,7 @@ * @author Gopal Venkatesan * @copyright 2015 Gopal Venkatesan. All rights reserved. * @copyright 2015 Casey Visco. All rights reserved. + * @copyright 2015 Ian VanSchooten. All rights reserved. */ "use strict"; @@ -20,10 +21,11 @@ module.exports = function(context) { // be treated as "always" and the only special case is "never" var mode = context.options[0] === "never" ? "never" : "always"; - // Cache line numbers of comments for faster lookup - var comments = context.getAllComments().map(function (token) { - return token.loc.start.line; - }); + // Cache starting and ending line numbers of comments for faster lookup + var commentEndLine = context.getAllComments().reduce(function(result, token) { + result[token.loc.start.line] = token.loc.end.line; + return result; + }, {}); //-------------------------------------------------------------------------- @@ -62,6 +64,42 @@ module.exports = function(context) { } /** + * Determine if provided nodeType is a function specifier + * @private + * @param {string} nodeType - nodeType to test + * @returns {boolean} True if `nodeType` is a function specifier + */ + function isFunctionSpecifier(nodeType) { + return nodeType === "FunctionDeclaration" || nodeType === "FunctionExpression" || + nodeType === "ArrowFunctionExpression"; + } + + /** + * Determine if provided node is the last of his parent + * @private + * @param {ASTNode} node - node to test + * @returns {boolean} True if `node` is last of his parent + */ + function isLastNode(node) { + return node.parent.body[node.parent.body.length - 1] === node; + } + + /** + * Determine if a token starts more than one line after a comment ends + * @param {token} token The token being checked + * @param {integer} commentStartLine The line number on which the comment starts + * @returns {boolean} True if `token` does not start immediately after a comment + */ + function hasBlankLineAfterComment(token, commentStartLine) { + var commentEnd = commentEndLine[commentStartLine]; + // If there's another comment, repeat check for blank line + if (commentEndLine[commentEnd + 1]) { + return hasBlankLineAfterComment(token, commentEnd + 1); + } + return (token.loc.start.line > commentEndLine[commentStartLine] + 1); + } + + /** * Checks that a blank line exists after a variable declaration when mode is * set to "always", or checks that there is no blank line when mode is set * to "never" @@ -97,15 +135,26 @@ module.exports = function(context) { return; } + // Ignore if it is last statement in a function + if (node.parent.parent && isFunctionSpecifier(node.parent.parent.type) && isLastNode(node)) { + return; + } + // Next statement is not a `var`... noNextLineToken = nextToken.loc.start.line > nextLineNum; - hasNextLineComment = comments.indexOf(nextLineNum) >= 0; + hasNextLineComment = (typeof commentEndLine[nextLineNum] !== "undefined"); if (mode === "never" && noNextLineToken && !hasNextLineComment) { context.report(node, NEVER_MESSAGE, { identifier: node.name }); } - if (mode === "always" && (!noNextLineToken || hasNextLineComment)) { + // Token on the next line, or comment without blank line + if ( + mode === "always" && ( + !noNextLineToken || + hasNextLineComment && !hasBlankLineAfterComment(nextToken, nextLineNum) + ) + ) { context.report(node, ALWAYS_MESSAGE, { identifier: node.name }); } } |