diff --git a/src/options/tab-size.js b/src/options/tab-size.js index d9a4b753..c372f337 100644 --- a/src/options/tab-size.js +++ b/src/options/tab-size.js @@ -22,5 +22,55 @@ module.exports = { ast.traverseByType('space', function(space) { space.content = space.content.replace(/\t/, value); }); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array?} List of detected values + */ + detect(ast) { + var detected = []; + + ast.forEach(node => { + if (node.is('ruleset') || node.is('atrule')) { + var spaces = 0; + var tabs = 0; + var tabsize; + + node.forEach(block => { + if (block && block.is('block')) { + block.forEach(blockContent => { + // The indent before the very first property declaration in the + // block usually has a length of one tab or the number of + // whitespaces set for a tab. Because there might be nested + // blocks having incremented indent, we continue to the next root + // block in the each ruleset where tabsize is not detected yet. + if (tabsize !== 'detected') { + if (blockContent.is('space')) { + spaces = blockContent.content.replace(/\n/g, ''); + tabs = spaces.match(/\t/g); + spaces = spaces.match(/( )/g); + if (tabs) { + // It is a very rare case when tabs are used in a file + // instead of whitespaces and it requires a lot of efforts + // to find the tab size in a particular environment. So we + // imply that tab size = 2. + tabsize = 2; + } else if (spaces) { + tabsize = spaces.length; + } + } else if (tabsize && blockContent.is('declaration')) { + detected.push(tabsize); + tabsize = 'detected'; + } + } + }); + } + }); + } + }); + + return detected; } }; diff --git a/test/options/integral/detect/test.js b/test/options/integral/detect/test.js index e4f40a28..cb6132a4 100644 --- a/test/options/integral/detect/test.js +++ b/test/options/integral/detect/test.js @@ -9,6 +9,8 @@ describe('Option `integral`, detect', function() { let expected = JSON.parse(JSON.stringify(test.Comb.getConfig('csscomb'))); delete expected['sort-order']; delete expected.exclude; + // The default csscomb.json has no tab-size option. + expected['tab-size'] = 4; test.shouldDetect(undefined, input, expected); }); });