From dd005d4fe220e24435e1fdfb4565edbaa8492b78 Mon Sep 17 00:00:00 2001 From: Alexander Inozemtsev Date: Mon, 10 Mar 2014 19:18:51 +0400 Subject: [PATCH 1/3] New option: space-before-selector-delimiter --- config/csscomb.json | 1 + lib/csscomb.js | 1 + .../space-before-selector-delimiter.js | 54 ++++++++++++ .../space-before-selector-delimiter.js | 83 +++++++++++++++++++ .../test-2.expected.css | 6 ++ .../test-3.expected.css | 12 +++ .../space-before-selector-delimiter/test.css | 6 ++ .../test.expected.css | 6 ++ 8 files changed, 169 insertions(+) create mode 100644 lib/options/space-before-selector-delimiter.js create mode 100644 test/options/space-before-selector-delimiter.js create mode 100644 test/options/space-before-selector-delimiter/test-2.expected.css create mode 100644 test/options/space-before-selector-delimiter/test-3.expected.css create mode 100644 test/options/space-before-selector-delimiter/test.css create mode 100644 test/options/space-before-selector-delimiter/test.expected.css diff --git a/config/csscomb.json b/config/csscomb.json index 767205cc..8cfaa28a 100644 --- a/config/csscomb.json +++ b/config/csscomb.json @@ -18,6 +18,7 @@ "space-before-colon": "", "space-before-combinator": " ", "space-before-opening-brace": "\n", + "space-before-selector-delimiter": "", "strip-spaces": true, "unitless-zero": true, "vendor-prefix-align": true, diff --git a/lib/csscomb.js b/lib/csscomb.js index 038bc4b0..dcb97bd8 100644 --- a/lib/csscomb.js +++ b/lib/csscomb.js @@ -21,6 +21,7 @@ var OPTIONS = [ 'space-after-colon', 'space-before-opening-brace', 'space-after-opening-brace', + 'space-before-selector-delimiter', 'sort-order', 'block-indent', 'tab-size', diff --git a/lib/options/space-before-selector-delimiter.js b/lib/options/space-before-selector-delimiter.js new file mode 100644 index 00000000..6d0309a9 --- /dev/null +++ b/lib/options/space-before-selector-delimiter.js @@ -0,0 +1,54 @@ +module.exports = { + name: 'space-before-selector-delimiter', + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {String} nodeType + * @param {node} node + */ + process: function(nodeType, node) { + if (nodeType !== 'selector') return; + + var value = this.getValue('space-before-selector-delimiter'); + + for (var i = node.length; i--;) { + if (node[i][0] === 'delim') { + if (node[i - 1][node[i - 1].length - 1][0] === 's') { + node[i - 1][node[i - 1].length - 1][1] = value; + } else { + node[i - 1].push(['s', value]); + } + } + } + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {String} nodeType + * @param {node} node + */ + detect: function(nodeType, node) { + if (nodeType !== 'selector') return; + + var variants = []; + + for (var i = node.length; i--;) { + if (node[i][0] !== 'delim') continue; + + if (node[i - 1][node[i - 1].length - 1][0] === 's') { + variants.push(node[i - 1][node[i - 1].length - 1][1]); + } else { + variants.push(''); + } + } + + return variants; + } +}; diff --git a/test/options/space-before-selector-delimiter.js b/test/options/space-before-selector-delimiter.js new file mode 100644 index 00000000..3770c75b --- /dev/null +++ b/test/options/space-before-selector-delimiter.js @@ -0,0 +1,83 @@ +describe('options/space-before-selector-delimiter:', function() { + beforeEach(function() { + this.filename = __filename; + }); + + it('Array value => should not change anything', function() { + this.comb.configure({ 'space-before-selector-delimiter': ['', ' '] }); + this.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + this.comb.configure({ 'space-before-selector-delimiter': ' nani ' }); + this.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + this.comb.configure({ 'space-before-selector-delimiter': 3.5 }); + this.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before selector delimiter', function() { + this.comb.configure({ 'space-before-selector-delimiter': 0 }); + this.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before selector delimiter', function() { + this.comb.configure({ 'space-before-selector-delimiter': ' ' }); + this.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before selector delimiter', function() { + this.comb.configure({ 'space-before-selector-delimiter': '\n ' }); + this.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + + it('Should detect no whitespace', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0}', + { 'space-before-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a \n ,b {top:0}', + { 'space-before-selector-delimiter': ' \n ' } + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0} a ,b{left:0}', + { 'space-before-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a ,b {top:0} b,a{left:0}', + { 'space-before-selector-delimiter': ' ' } + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a ,b{top:0} b,c{left:0} c,d{right:0}', + { 'space-before-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + this.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0} b ,c{left:0} c ,d{right:0}', + { 'space-before-selector-delimiter': ' ' } + ); + }); +}); diff --git a/test/options/space-before-selector-delimiter/test-2.expected.css b/test/options/space-before-selector-delimiter/test-2.expected.css new file mode 100644 index 00000000..90b232e1 --- /dev/null +++ b/test/options/space-before-selector-delimiter/test-2.expected.css @@ -0,0 +1,6 @@ +a ,b { color: red } +a , b { color: red } +a ,b { color: red } +a , +b { color: red } +a+b ,c>d ,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/test-3.expected.css b/test/options/space-before-selector-delimiter/test-3.expected.css new file mode 100644 index 00000000..1b454c84 --- /dev/null +++ b/test/options/space-before-selector-delimiter/test-3.expected.css @@ -0,0 +1,12 @@ +a + ,b { color: red } +a + , b { color: red } +a + ,b { color: red } +a + , +b { color: red } +a+b + ,c>d + ,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/test.css b/test/options/space-before-selector-delimiter/test.css new file mode 100644 index 00000000..cd8e7d77 --- /dev/null +++ b/test/options/space-before-selector-delimiter/test.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a ,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/test.expected.css b/test/options/space-before-selector-delimiter/test.expected.css new file mode 100644 index 00000000..3c566c55 --- /dev/null +++ b/test/options/space-before-selector-delimiter/test.expected.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file From 7d8f53d1b2d569ee08667f96df9381c82b7760df Mon Sep 17 00:00:00 2001 From: Alexander Inozemtsev Date: Mon, 10 Mar 2014 19:33:33 +0400 Subject: [PATCH 2/3] New option: space-after-selector-delimiter --- config/csscomb.json | 1 + doc/options.md | 76 +++++++++++++++++ lib/csscomb.js | 1 + lib/options/space-after-selector-delimiter.js | 54 ++++++++++++ .../options/space-after-selector-delimiter.js | 83 +++++++++++++++++++ .../test-2.expected.css | 5 ++ .../test-3.expected.css | 11 +++ .../space-after-selector-delimiter/test.css | 6 ++ .../test.expected.css | 5 ++ 9 files changed, 242 insertions(+) create mode 100644 lib/options/space-after-selector-delimiter.js create mode 100644 test/options/space-after-selector-delimiter.js create mode 100644 test/options/space-after-selector-delimiter/test-2.expected.css create mode 100644 test/options/space-after-selector-delimiter/test-3.expected.css create mode 100644 test/options/space-after-selector-delimiter/test.css create mode 100644 test/options/space-after-selector-delimiter/test.expected.css diff --git a/config/csscomb.json b/config/csscomb.json index 8cfaa28a..497d29fe 100644 --- a/config/csscomb.json +++ b/config/csscomb.json @@ -15,6 +15,7 @@ "space-after-colon": " ", "space-after-combinator": " ", "space-after-opening-brace": "\n", + "space-after-selector-delimiter": "\n", "space-before-colon": "", "space-before-combinator": " ", "space-before-opening-brace": "\n", diff --git a/doc/options.md b/doc/options.md index ccdf2a9d..3356a1ca 100644 --- a/doc/options.md +++ b/doc/options.md @@ -427,6 +427,44 @@ a{ color: panda;} ``` +## space-after-selector-delimiter + +Set space after selector delimiter. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ 'space-after-selector-delimiter': 1 }` + +```scss +// Before: +a,b{ + color: panda; + } + +// After: +a, b { + color: panda; + } +``` + +Example: `{ 'space-before-selector-delimiter': '\n' }` + +```scss +// Before: +a, b{ + color: panda; + } + +// After: +a, +b{ + color: panda; + } +``` + ## space-before-colon Set space before `:` in declarations. @@ -536,6 +574,44 @@ a } ``` +## space-before-selector-delimiter + +Set space before selector delimiter. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ 'space-before-selector-delimiter': 0 }` + +```scss +// Before: +a , b{ + color: panda; + } + +// After: +a, b { + color: panda; + } +``` + +Example: `{ 'space-before-selector-delimiter': '\n' }` + +```scss +// Before: +a, b{ + color: panda; + } + +// After: +a +,b{ + color: panda; + } +``` + ## strip-spaces Whether to trim trailing spaces. diff --git a/lib/csscomb.js b/lib/csscomb.js index dcb97bd8..be58e9a3 100644 --- a/lib/csscomb.js +++ b/lib/csscomb.js @@ -22,6 +22,7 @@ var OPTIONS = [ 'space-before-opening-brace', 'space-after-opening-brace', 'space-before-selector-delimiter', + 'space-after-selector-delimiter', 'sort-order', 'block-indent', 'tab-size', diff --git a/lib/options/space-after-selector-delimiter.js b/lib/options/space-after-selector-delimiter.js new file mode 100644 index 00000000..7485dbf7 --- /dev/null +++ b/lib/options/space-after-selector-delimiter.js @@ -0,0 +1,54 @@ +module.exports = { + name: 'space-after-selector-delimiter', + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {String} nodeType + * @param {node} node + */ + process: function(nodeType, node) { + if (nodeType !== 'selector') return; + + var value = this.getValue('space-after-selector-delimiter'); + + for (var i = node.length; i--;) { + if (node[i][0] === 'delim') { + if (node[i + 1][1][0] === 's') { + node[i + 1][1][1] = value; + } else { + node[i + 1].splice(1, 0, ['s', value]); + } + } + } + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {String} nodeType + * @param {node} node + */ + detect: function(nodeType, node) { + if (nodeType !== 'selector') return; + + var variants = []; + + for (var i = node.length; i--;) { + if (node[i][0] !== 'delim') continue; + + if (node[i + 1][1][0] === 's') { + variants.push(node[i + 1][1][1]); + } else { + variants.push(''); + } + } + + return variants; + } +}; diff --git a/test/options/space-after-selector-delimiter.js b/test/options/space-after-selector-delimiter.js new file mode 100644 index 00000000..8d82ae1f --- /dev/null +++ b/test/options/space-after-selector-delimiter.js @@ -0,0 +1,83 @@ +describe('options/space-after-selector-delimiter:', function() { + beforeEach(function() { + this.filename = __filename; + }); + + it('Array value => should not change anything', function() { + this.comb.configure({ 'space-after-selector-delimiter': ['', ' '] }); + this.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + this.comb.configure({ 'space-after-selector-delimiter': ' nani ' }); + this.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + this.comb.configure({ 'space-after-selector-delimiter': 3.5 }); + this.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after selector delimiter', function() { + this.comb.configure({ 'space-after-selector-delimiter': 0 }); + this.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space after selector delimiter', function() { + this.comb.configure({ 'space-after-selector-delimiter': ' ' }); + this.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space after selector delimiter', function() { + this.comb.configure({ 'space-after-selector-delimiter': '\n ' }); + this.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + + it('Should detect no whitespace', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0}', + { 'space-after-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a, \n b {top:0}', + { 'space-after-selector-delimiter': ' \n ' } + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0} a, b{left:0}', + { 'space-after-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a, b {top:0} b,a{left:0}', + { 'space-after-selector-delimiter': ' ' } + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a, b{top:0} b,c{left:0} c,d{right:0}', + { 'space-after-selector-delimiter': '' } + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + this.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0} b, c{left:0} c, sd{right:0}', + { 'space-after-selector-delimiter': ' ' } + ); + }); +}); diff --git a/test/options/space-after-selector-delimiter/test-2.expected.css b/test/options/space-after-selector-delimiter/test-2.expected.css new file mode 100644 index 00000000..92d1ece8 --- /dev/null +++ b/test/options/space-after-selector-delimiter/test-2.expected.css @@ -0,0 +1,5 @@ +a, b { color: red } +a, b { color: red } +a , b { color: red } +a, b { color: red } +a+b, c>d, e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/test-3.expected.css b/test/options/space-after-selector-delimiter/test-3.expected.css new file mode 100644 index 00000000..6a198487 --- /dev/null +++ b/test/options/space-after-selector-delimiter/test-3.expected.css @@ -0,0 +1,11 @@ +a, + b { color: red } +a, + b { color: red } +a , + b { color: red } +a, + b { color: red } +a+b, + c>d, + e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/test.css b/test/options/space-after-selector-delimiter/test.css new file mode 100644 index 00000000..cd8e7d77 --- /dev/null +++ b/test/options/space-after-selector-delimiter/test.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a ,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/test.expected.css b/test/options/space-after-selector-delimiter/test.expected.css new file mode 100644 index 00000000..b0d4fd20 --- /dev/null +++ b/test/options/space-after-selector-delimiter/test.expected.css @@ -0,0 +1,5 @@ +a,b { color: red } +a,b { color: red } +a ,b { color: red } +a,b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file From 7edc9a7c70f813cfbade8ff999ff2ef5efdffe4e Mon Sep 17 00:00:00 2001 From: Alexander Inozemtsev Date: Tue, 11 Mar 2014 01:32:37 +0400 Subject: [PATCH 3/3] review fixes for spaces around selector delimiter --- doc/options.md | 2 +- lib/options/space-after-selector-delimiter.js | 12 ++++++------ lib/options/space-before-selector-delimiter.js | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/doc/options.md b/doc/options.md index 3356a1ca..09533324 100644 --- a/doc/options.md +++ b/doc/options.md @@ -450,7 +450,7 @@ a, b { } ``` -Example: `{ 'space-before-selector-delimiter': '\n' }` +Example: `{ 'space-after-selector-delimiter': '\n' }` ```scss // Before: diff --git a/lib/options/space-after-selector-delimiter.js b/lib/options/space-after-selector-delimiter.js index 7485dbf7..1de68870 100644 --- a/lib/options/space-after-selector-delimiter.js +++ b/lib/options/space-after-selector-delimiter.js @@ -18,12 +18,12 @@ module.exports = { var value = this.getValue('space-after-selector-delimiter'); for (var i = node.length; i--;) { - if (node[i][0] === 'delim') { - if (node[i + 1][1][0] === 's') { - node[i + 1][1][1] = value; - } else { - node[i + 1].splice(1, 0, ['s', value]); - } + if (node[i][0] !== 'delim') continue; + + if (node[i + 1][1][0] === 's') { + node[i + 1][1][1] = value; + } else { + node[i + 1].splice(1, 0, ['s', value]); } } }, diff --git a/lib/options/space-before-selector-delimiter.js b/lib/options/space-before-selector-delimiter.js index 6d0309a9..3e720d05 100644 --- a/lib/options/space-before-selector-delimiter.js +++ b/lib/options/space-before-selector-delimiter.js @@ -18,12 +18,13 @@ module.exports = { var value = this.getValue('space-before-selector-delimiter'); for (var i = node.length; i--;) { - if (node[i][0] === 'delim') { - if (node[i - 1][node[i - 1].length - 1][0] === 's') { - node[i - 1][node[i - 1].length - 1][1] = value; - } else { - node[i - 1].push(['s', value]); - } + if (node[i][0] !== 'delim') continue; + + var previousNode = node[i - 1]; + if (previousNode[previousNode.length - 1][0] === 's') { + previousNode[previousNode.length - 1][1] = value; + } else { + previousNode.push(['s', value]); } } }, @@ -42,8 +43,9 @@ module.exports = { for (var i = node.length; i--;) { if (node[i][0] !== 'delim') continue; - if (node[i - 1][node[i - 1].length - 1][0] === 's') { - variants.push(node[i - 1][node[i - 1].length - 1][1]); + var previousNode = node[i - 1]; + if (previousNode[previousNode.length - 1][0] === 's') { + variants.push(previousNode[previousNode.length - 1][1]); } else { variants.push(''); }