Repository: lesshint/lesshint (js)

Issue:
SpaceBeforeBrace New Line
I think there is a bug in the reg ex here: https://github.com/lesshint/lesshint/blob/77a502cb25dd8e8368b9a35aaabd183352d6c03e/lib/linters/space_before_brace.js#L15

It does not allow for whitespace between the selector and the brace so this is allowed

```css
ul
{
//Some Styles
li
{
//Some Styles
}
}
```

But this is not because between li and { there are 4 spaces.
```css
ul
{
    //Some Styles

    li
    {
        //Some Styles
    }
}
```

I think the fix would be to change the regex to /^\n *\t*$/ which would require a return directly after the selector but also allow for tabs or a spaces used to indent the code.

I do not think this is intended behaviour and I am happy to send a pull request but but fixing it would be a breaking change.

Pull request description:
Space before brace new line and whitespace
This fixes #244 where setting "space before brace" to "new_line" did not allow you to indent the code.

It also fixes another issue in the "one_space" reg ex which used \s instead of space literal. \s does not mean space it means all whitespace characters so it would also pick up new lines (providing you use Unix style of just LF and not windows which uses two characters) and tabs.

Test have been provided and all pass.

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,
diff --git a/lib/linters/space_before_brace.js b/lib/linters/space_before_brace.js
index 9abde89..b445ea7 100644
--- a/lib/linters/space_before_brace.js
+++ b/lib/linters/space_before_brace.js
@@ -12,9 +12,9 @@ module.exports = {
     lint: function spaceBeforeBraceLinter (config, node) {
         var column;
         var styles = {
-            'new_line': /^\n$/,
+            'new_line': /^\n *\t*$/,
             'no_space': /^$/,
-            'one_space': /^\s$/
+            'one_space': /^ $/
         };
 
         if (config.style && !styles[config.style]) {


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);
+            });
+        });
     });
 });
diff --git a/test/specs/linters/space_before_brace.js b/test/specs/linters/space_before_brace.js
index da648fb..21e049d 100644
--- a/test/specs/linters/space_before_brace.js
+++ b/test/specs/linters/space_before_brace.js
@@ -185,6 +185,63 @@ describe('lesshint', function () {
             });
         });
 
+        it('should not allow multiple spaces when "style" is "one_space"', function () {
+            var source = '.foo  {}';
+            var expected = [{
+                column: 5,
+                line: 1,
+                message: 'Opening curly brace should be preceded by one space.'
+            }];
+
+            var options = {
+                style: 'one_space'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should not allow new line when "style" is "one_space"', function () {
+            var source = '.foo, .bar\n{}';
+            var expected = [{
+                column: 11,
+                line: 1,
+                message: 'Opening curly brace should be preceded by one space.'
+            }];
+
+            var options = {
+                style: 'one_space'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should not allow tab when "style" is "one_space"', function () {
+            var source = '.foo, .bar\t{}';
+            var expected = [{
+                column: 11,
+                line: 1,
+                message: 'Opening curly brace should be preceded by one space.'
+            }];
+
+            var options = {
+                style: 'one_space'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
         it('should not allow multiple spaces when multiple simple selectors are used and "style" is "one_space"', function () {
             var source = '.foo, .bar  {}';
             var expected = [{
@@ -230,6 +287,32 @@ describe('lesshint', function () {
             });
         });
 
+        it('should allow one new line followed by spaces when "style" is "new_line"', function () {
+            var source = '.foo\n       {}';
+            var options = {
+                style: 'new_line'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+
+        it('should allow one new line followed by tabs when "style" is "new_line"', function () {
+            var source = '.foo\n\t\t{}';
+            var options = {
+                style: 'new_line'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+
         it('should not allow multiple new lines when "style" is "new_line"', function () {
             var source = '.foo\n\n{}';
             var expected = [{