Skip to content

Commit

Permalink
New: line-comment-position rule (fixes #6077) (#6953)
Browse files Browse the repository at this point in the history
  • Loading branch information
alberto authored and nzakas committed Aug 30, 2016
1 parent c1f0d76 commit 8277357
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Expand Up @@ -171,6 +171,7 @@
"key-spacing": "off",
"keyword-spacing": "off",
"linebreak-style": "off",
"line-comment-position": "off",
"lines-around-comment": "off",
"lines-around-directive": "off",
"max-depth": "off",
Expand Down
104 changes: 104 additions & 0 deletions docs/rules/line-comment-position.md
@@ -0,0 +1,104 @@
# enforce position of line comments (line-comment-position)

Line comments can be positioned above or beside code. This rule helps teams maintain a consistent style.

```js
// above comment
var foo = "bar"; // beside comment
```

## Rule Details

This rule enforces consistent position of line comments. Block comments are not affected by this rule. By default, this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`, `falls through`.


## Options

This rule takes one argument, which can be a string or an object. The string settings are the same as those of the `position` property (explained below). The object option has the following properties:

### position

The `position` option has two settings:

* `above` (default) enforces line comments only above code, in its own line.
* `beside` enforces line comments only at the end of code lines.

#### position: above

Examples of **correct** code for the `{ "position": "above" }` option:

```js
/*eslint line-comment-position: ["error", { "position": "above" }]*/
// valid comment
1 + 1;
```


Examples of **incorrect** code for the `{ "position": "above" }` option:

```js
/*eslint line-comment-position: ["error", { "position": "above" }]*/
1 + 1; // invalid comment
```

#### position: beside

Examples of **correct** code for the `{ "position": "beside" }` option:

```js
/*eslint line-comment-position: ["error", { "position": "beside" }]*/
1 + 1; // valid comment
```


Examples of **incorrect** code for the `{ "position": "above" }` option:

```js
/*eslint line-comment-position: ["error", { "position": "above" }]*/
// invalid comment
1 + 1;
```

### ignorePattern

By default this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`, `falls through`. An alternative regular expression can be provided.

Examples of **correct** code for the `ignorePattern` option:

```js
/*eslint line-comment-position: ["error", { "ignorePattern": "pragma" }]*/
1 + 1; // pragma valid comment
```

Examples of **incorrect** code for the `ignorePattern` option:

```js
/*eslint line-comment-position: ["error", { "ignorePattern": "pragma" }]*/
1 + 1; // invalid comment
```

### applyDefaultPatterns

Default ignore patterns are applied even when `ignorePattern` is provided. If you want to omit default patterns, set this option to `false`.

Examples of **correct** code for the `{ "applyDefaultPatterns": false }` option:

```js
/*eslint line-comment-position: ["error", { "ignorePattern": "pragma", "applyDefaultPatterns": false }]*/
1 + 1; // pragma valid comment
```

Examples of **incorrect** code for the `{ "applyDefaultPatterns": false }` option:

```js
/*eslint line-comment-position: ["error", { "ignorePattern": "pragma", "applyDefaultPatterns": false }]*/
1 + 1; // falls through
```

## When Not To Use It

If you aren't concerned about having different line comment styles, then you can turn off this rule.

## Compatibility

**JSCS**: [validateCommentPosition](http://jscs.info/rule/validateCommentPosition)
101 changes: 101 additions & 0 deletions lib/rules/line-comment-position.js
@@ -0,0 +1,101 @@
/**
* @fileoverview Rule to enforce the position of line comments
* @author Alberto Rodríguez
*/
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "enforce position of line comments",
category: "Stylistic Issues",
recommended: false
},

schema: [
{
oneOf: [
{
enum: ["above", "beside"]
},
{
type: "object",
properties: {
position: {
enum: ["above", "beside"]
},
ignorePattern: {
type: "string"
},
applyDefaultPatterns: {
type: "boolean"
}
},
additionalProperties: false
}
]
}
]
},

create(context) {
const DEFAULT_IGNORE_PATTERN = "^\\s*(?:eslint|jshint\\s+|jslint\\s+|istanbul\\s+|globals?\\s+|exported\\s+|jscs|falls?\\s?through)";
const options = context.options[0];

let above,
ignorePattern,
applyDefaultPatterns = true;

if (!options || typeof option === "string") {
above = !options || options === "above";

} else {
above = options.position === "above";
ignorePattern = options.ignorePattern;
applyDefaultPatterns = options.applyDefaultPatterns !== false;
}

const defaultIgnoreRegExp = new RegExp(DEFAULT_IGNORE_PATTERN);
const customIgnoreRegExp = new RegExp(ignorePattern);
const sourceCode = context.getSourceCode();

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------

return {
LineComment(node) {
if (applyDefaultPatterns && defaultIgnoreRegExp.test(node.value)) {
return;
}

if (ignorePattern && customIgnoreRegExp.test(node.value)) {
return;
}

const previous = sourceCode.getTokenOrCommentBefore(node);
const isOnSameLine = previous && previous.loc.end.line === node.loc.start.line;

if (above) {
if (isOnSameLine) {
context.report({
node,
message: "Expected comment to be above code."
});
}
} else {
if (!isOnSameLine) {
context.report({
node,
message: "Expected comment to be beside code."
});
}
}
}
};
}
};
147 changes: 147 additions & 0 deletions tests/lib/rules/line-comment-position.js
@@ -0,0 +1,147 @@
/**
* @fileoverview enforce position of line comments
* @author Alberto Rodríguez
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/line-comment-position"),
RuleTester = require("../../../lib/testers/rule-tester");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();

ruleTester.run("line-comment-position", rule, {

valid: [
"// valid comment\n1 + 1;",
"/* block comments are skipped */\n1 + 1;",
"1 + 1; /* block comments are skipped */",
"1 + 1; /* eslint eqeqeq: 'error' */",
"1 + 1; /* eslint-disable */",
"1 + 1; /* eslint-enable */",
"1 + 1; // eslint-disable-line",
"// eslint-disable-next-line\n1 + 1;",
"1 + 1; // global MY_GLOBAL, ANOTHER",
"1 + 1; // globals MY_GLOBAL: true",
"1 + 1; // exported MY_GLOBAL, ANOTHER",
"1 + 1; // fallthrough",
"1 + 1; // fall through",
"1 + 1; // falls through",
"1 + 1; // jslint vars: true",
"1 + 1; // jshint ignore:line",
"1 + 1; // istanbul ignore next",
{
code: "1 + 1; // linter excepted comment",
options: [{position: "above", ignorePattern: "linter" } ]
},
{
code: "1 + 1; // valid comment",
options: ["beside"]
},
{
code: "// jscs: disable\n1 + 1;",
options: ["beside"]
},
{
code: "// jscs: enable\n1 + 1;",
options: ["beside"]
},
{
code: "/* block comments are skipped */\n1 + 1;",
options: ["beside"]
},
{
code: "/*block comment*/\n/*block comment*/\n1 + 1;",
options: ["beside"]
},
{
code: "1 + 1; /* block comment are skipped */",
options: ["beside"]
},
{
code: "1 + 1; // jshint strict: true",
options: ["beside"]
},
{
code: "// pragma valid comment\n1 + 1;",
options: [{position: "beside", ignorePattern: "pragma|linter" } ]
}
],

invalid: [
{
code: "1 + 1; // invalid comment",
errors: [{
message: "Expected comment to be above code.",
type: "Line",
line: 1,
column: 8
}]
},
{
code: "1 + 1; // globalization is a word",
errors: [{
message: "Expected comment to be above code.",
type: "Line",
line: 1,
column: 8
}]
},
{
code: "// jscs: disable\n1 + 1;",
options: [{ position: "beside", applyDefaultPatterns: false }],
errors: [{
message: "Expected comment to be beside code.",
type: "Line",
line: 1,
column: 1
}]
},
{
code: "1 + 1; // mentioning falls through",
errors: [{
message: "Expected comment to be above code.",
type: "Line",
line: 1,
column: 8
}]
},
{
code: "// invalid comment\n1 + 1;",
options: ["beside"],
errors: [{
message: "Expected comment to be beside code.",
type: "Line",
line: 1,
column: 1
}]
},
{
code: "// pragma\n// invalid\n1 + 1;",
options: [{ position: "beside", ignorePattern: "pragma" }],
errors: [{
message: "Expected comment to be beside code.",
type: "Line",
line: 2,
column: 1
}]
},
{
code: "1 + 1; // linter\n2 + 2; // invalid comment",
options: [{position: "above", ignorePattern: "linter" } ],
errors: [{
message: "Expected comment to be above code.",
type: "Line",
line: 2,
column: 8
}]
}
]
});

0 comments on commit 8277357

Please sign in to comment.