Repository: lesshint/lesshint
Pull request #87 (2015-10-21 13:07:53)

Ensure that mixins are on seperate lines
I extended the singleLinePerProperty linter to also include the 'include' and 'extend' types of gonzales-pe as those seem to be matched by ```.mixin();```. If you would like to include more or less types, please let me know.

jwilsson: I actually thought about this earlier today and I think we should add `variable` to the list.

I'm also thinking whether we should add `selector` and `atrule` too or if that's overkill. @kokarn what do you think and can you come up with anything else?
kokarn: This is everything that should be on a separate line, correct?

Reading through the docs, the ones i find are 
functions
```less
.function {
    //some stuf
}
```
Detatched rulesets
```less
// declare detached ruleset
@detached-ruleset: { background: red; };

// use detached ruleset
.top {
    @detached-ruleset(); 
}
```
and loops. 
But those should probably all be covered by this already, right?

Patch:
diff --git a/lib/linters/single_line_per_property.js b/lib/linters/single_line_per_property.js
index 626b81d..f84e38f 100644
--- a/lib/linters/single_line_per_property.js
+++ b/lib/linters/single_line_per_property.js
@@ -9,18 +9,20 @@ module.exports = {
         var results = [];
         var self = this;
 
-        node.forEach('declaration', function (element, index) {
-            var previousValid = self.isPreviousValid(node, element, index);
-            var nextValid = self.isNextValid(node, element, index);
+        ['declaration', 'include', 'extend', 'atrule'].forEach(function (type) {
+            node.forEach(type, function (element, index) {
+                var previousValid = self.isPreviousValid(node, element, index);
+                var nextValid = self.isNextValid(node, element, index);
 
-            // BOTH previous and next must be valid
-            if (!(previousValid && nextValid)) {
-                results.push({
-                    column: element.start.column,
-                    line: element.start.line,
-                    message: self.message
-                });
-            }
+                // BOTH previous and next must be valid
+                if (!(previousValid && nextValid)) {
+                    results.push({
+                        column: element.start.column,
+                        line: element.start.line,
+                        message: self.message
+                    });
+                }
+            });
         });
 
         if (results.length) {


Test patch:
diff --git a/test/specs/linters/single_line_per_property.js b/test/specs/linters/single_line_per_property.js
index 13d816f..71be001 100644
--- a/test/specs/linters/single_line_per_property.js
+++ b/test/specs/linters/single_line_per_property.js
@@ -109,6 +109,132 @@ describe('lesshint', function () {
             assert.deepEqual(actual, expected);
         });
 
+        it('should allow mixins on separate block', function () {
+            var source = '.foo {\n.mixin(); \n.mixin2(); \n}';
+            var ast;
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            assert.strictEqual(undefined, lint(options, ast));
+        });
+
+        it('should not allow single mixin in single-line block', function () {
+            var source = '.foo {.mixin();}';
+            var actual;
+            var ast;
+
+            var expected = [{
+                column: 7,
+                line: 1,
+                linter: 'singleLinePerProperty',
+                message: errorMessage
+            }];
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            actual = lint(options, ast);
+
+            assert.deepEqual(actual, expected);
+        });
+
+        it('should allow variables on separate block', function () {
+            var source = '.foo {\n@var1: 10px; \n@var2: 20px; \n}';
+            var ast;
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            assert.strictEqual(undefined, lint(options, ast));
+        });
+
+        it('should not allow single variable in single-line block', function () {
+            var source = '.foo {@var1: 10px;}';
+            var actual;
+            var ast;
+
+            var expected = [{
+                column: 7,
+                line: 1,
+                linter: 'singleLinePerProperty',
+                message: errorMessage
+            }];
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            actual = lint(options, ast);
+
+            assert.deepEqual(actual, expected);
+        });
+
+        it('should allow detached rulessets on separate block', function () {
+            var source = '.foo {\n@ruleset1(); \n@ruleset2(); \n}';
+            var ast;
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            assert.strictEqual(undefined, lint(options, ast));
+        });
+
+        it('should not allow single detached ruleset in single-line block', function () {
+            var source = '.foo {@ruleset();}';
+            var actual;
+            var ast;
+
+            var expected = [{
+                column: 7,
+                line: 1,
+                linter: 'singleLinePerProperty',
+                message: errorMessage
+            }];
+
+            var options = {
+                singleLinePerProperty: {
+                    enabled: true
+                }
+            };
+
+            ast = parseAST(source);
+            ast = ast.first('ruleset').first('block');
+
+            actual = lint(options, ast);
+
+            assert.deepEqual(actual, expected);
+        });
+
         it('should not allow opening brace on the same line as a property', function () {
             var source = '.foo { color: red; \n margin-right: 10px; \n}';
             var actual;