Repository: lesshint/lesshint (js)

Issue:
Multiple warnings of the same error in selector
For the following selector in one of my less-files (excuse the messy selector):

```
.contextmenu-wrapper:not(.backward) ui-contextmenu-item:first-of-type .contextmenu-item, .contextmenu-wrapper.backward ui-contextmenu-item:last-of-type .contextmenu-item
```

This one produces **14** warnings:

```
line 72, col 1, singleLinePerSelector: Each selector should be on its own line.
line 72, col 21, singleLinePerSelector: Each selector should be on its own line.
line 72, col 36, singleLinePerSelector: Each selector should be on its own line.
line 72, col 37, singleLinePerSelector: Each selector should be on its own line.
line 72, col 56, singleLinePerSelector: Each selector should be on its own line.
line 72, col 70, singleLinePerSelector: Each selector should be on its own line.
line 72, col 71, singleLinePerSelector: Each selector should be on its own line.
line 72, col 90, singleLinePerSelector: Each selector should be on its own line.
line 72, col 110, singleLinePerSelector: Each selector should be on its own line.
line 72, col 119, singleLinePerSelector: Each selector should be on its own line.
line 72, col 120, singleLinePerSelector: Each selector should be on its own line.
line 72, col 139, singleLinePerSelector: Each selector should be on its own line.
line 72, col 152, singleLinePerSelector: Each selector should be on its own line.
line 72, col 153, singleLinePerSelector: Each selector should be on its own line.
```

It seems as the correct warning should be only the one at col 90, since this is where the second selector starts.

Pull request description:
Ensure that singleLinePerSelector only reports each selector once
Closes #239.

Patch:
diff --git a/lib/linters/newline_after_block.js b/lib/linters/newline_after_block.js
index 58f33a5..85e7101 100644
--- a/lib/linters/newline_after_block.js
+++ b/lib/linters/newline_after_block.js
@@ -35,12 +35,12 @@ module.exports = {
          * unless it's the first comment of a block.
          */
         if (prev.type === 'comment') {
-            if (Object.is(prev, parent.first) || prev.raws.before === '\n\n') {
+            if (Object.is(prev, parent.first) || prev.raws.before.indexOf('\n\n') !== -1) {
                 return;
             }
         }
 
-        if (node.raws.before !== '\n\n') {
+        if (node.raws.before.indexOf('\n\n') === -1) {
             return [{
                 message: this.message
             }];
diff --git a/lib/linters/single_line_per_selector.js b/lib/linters/single_line_per_selector.js
index 1cf9dfc..3289dcc 100644
--- a/lib/linters/single_line_per_selector.js
+++ b/lib/linters/single_line_per_selector.js
@@ -19,9 +19,16 @@ module.exports = {
         }).process(node.selector);
 
         tree.each(function (selector) {
+            var reported = false;
+
             selector.each(function (thing) {
                 var value = thing.toString().trim();
 
+                // This selector is already reported, bail
+                if (reported) {
+                    return;
+                }
+
                 switch (config.style) {
                     case '18f':
                         if (value && value.length >= 5) {
@@ -35,6 +42,8 @@ module.exports = {
                 }
 
                 if (!valid && tree.nodes.length > 1 && node.selector.indexOf('\n') === -1) {
+                    reported = true;
+
                     results.push({
                         column: node.source.start.column + thing.source.start.column - 1,
                         line: node.source.start.line,


Test patch:
diff --git a/test/specs/linters/newline_after_block.js b/test/specs/linters/newline_after_block.js
index 9a2b171..ba6337c 100644
--- a/test/specs/linters/newline_after_block.js
+++ b/test/specs/linters/newline_after_block.js
@@ -116,5 +116,23 @@ describe('lesshint', function () {
                 expect(result).to.be.undefined;
             });
         });
+
+        it('should not report nested blocks with a preceding new line', function () {
+            var source = '';
+
+            source += '.foo {';
+            source += '    color: red;';
+            source += '\n\n';
+            source += '    .bar {';
+            source += '        color: red;';
+            source += '    }';
+            source += '}';
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint({}, ast.root.last);
+
+                expect(result).to.be.undefined;
+            });
+        });
     });
 });
diff --git a/test/specs/linters/single_line_per_selector.js b/test/specs/linters/single_line_per_selector.js
index 3739015..2d6b99b 100644
--- a/test/specs/linters/single_line_per_selector.js
+++ b/test/specs/linters/single_line_per_selector.js
@@ -96,5 +96,27 @@ describe('lesshint', function () {
                 expect(result).to.be.undefined;
             });
         });
+
+        it('should not report the same selector multiple times. #239', function () {
+            var source = '.foo .bar, .bar .foo {}';
+            var expected = [
+                {
+                    column: 1,
+                    line: 1,
+                    message: 'Each selector should be on its own line.'
+                },
+                {
+                    column: 12,
+                    line: 1,
+                    message: 'Each selector should be on its own line.'
+                }
+            ];
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint({}, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
     });
 });