/* eslint-disable guard-for-in */ 'use strict'; var repeating = require('repeating'); // detect either spaces or tabs but not both to properly handle tabs // for indentation and spaces for alignment var INDENT_RE = /^(?:( )+|\t+)/; function getMostUsed(indents) { var result = 0; var maxUsed = 0; var maxWeight = 0; for (var n in indents) { var indent = indents[n]; var u = indent[0]; var w = indent[1]; if (u > maxUsed || u === maxUsed && w > maxWeight) { maxUsed = u; maxWeight = w; result = Number(n); } } return result; } module.exports = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } // used to see if tabs or spaces are the most used var tabs = 0; var spaces = 0; // remember the size of previous line's indentation var prev = 0; // remember how many indents/unindents as occurred for a given size // and how much lines follow a given indentation // // indents = { // 3: [1, 0], // 4: [1, 5], // 5: [1, 0], // 12: [1, 0], // } var indents = {}; // pointer to the array of last used indent var current; // whether the last action was an indent (opposed to an unindent) var isIndent; str.split(/\n/g).forEach(function (line) { if (!line) { // ignore empty lines return; } var indent; var matches = line.match(INDENT_RE); if (!matches) { indent = 0; } else { indent = matches[0].length; if (matches[1]) { spaces++; } else { tabs++; } } var diff = indent - prev; prev = indent; if (diff) { // an indent or unindent has been detected isIndent = diff > 0; current = indents[isIndent ? diff : -diff]; if (current) { current[0]++; } else { current = indents[diff] = [1, 0]; } } else if (current) { // if the last action was an indent, increment the weight current[1] += Number(isIndent); } }); var amount = getMostUsed(indents); var type; var actual; if (!amount) { type = null; actual = ''; } else if (spaces >= tabs) { type = 'space'; actual = repeating(' ', amount); } else { type = 'tab'; actual = repeating('\t', amount); } return { amount: amount, type: type, indent: actual }; };