index.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /* eslint-disable guard-for-in */
  2. 'use strict';
  3. var repeating = require('repeating');
  4. // detect either spaces or tabs but not both to properly handle tabs
  5. // for indentation and spaces for alignment
  6. var INDENT_RE = /^(?:( )+|\t+)/;
  7. function getMostUsed(indents) {
  8. var result = 0;
  9. var maxUsed = 0;
  10. var maxWeight = 0;
  11. for (var n in indents) {
  12. var indent = indents[n];
  13. var u = indent[0];
  14. var w = indent[1];
  15. if (u > maxUsed || u === maxUsed && w > maxWeight) {
  16. maxUsed = u;
  17. maxWeight = w;
  18. result = Number(n);
  19. }
  20. }
  21. return result;
  22. }
  23. module.exports = function (str) {
  24. if (typeof str !== 'string') {
  25. throw new TypeError('Expected a string');
  26. }
  27. // used to see if tabs or spaces are the most used
  28. var tabs = 0;
  29. var spaces = 0;
  30. // remember the size of previous line's indentation
  31. var prev = 0;
  32. // remember how many indents/unindents as occurred for a given size
  33. // and how much lines follow a given indentation
  34. //
  35. // indents = {
  36. // 3: [1, 0],
  37. // 4: [1, 5],
  38. // 5: [1, 0],
  39. // 12: [1, 0],
  40. // }
  41. var indents = {};
  42. // pointer to the array of last used indent
  43. var current;
  44. // whether the last action was an indent (opposed to an unindent)
  45. var isIndent;
  46. str.split(/\n/g).forEach(function (line) {
  47. if (!line) {
  48. // ignore empty lines
  49. return;
  50. }
  51. var indent;
  52. var matches = line.match(INDENT_RE);
  53. if (!matches) {
  54. indent = 0;
  55. } else {
  56. indent = matches[0].length;
  57. if (matches[1]) {
  58. spaces++;
  59. } else {
  60. tabs++;
  61. }
  62. }
  63. var diff = indent - prev;
  64. prev = indent;
  65. if (diff) {
  66. // an indent or unindent has been detected
  67. isIndent = diff > 0;
  68. current = indents[isIndent ? diff : -diff];
  69. if (current) {
  70. current[0]++;
  71. } else {
  72. current = indents[diff] = [1, 0];
  73. }
  74. } else if (current) {
  75. // if the last action was an indent, increment the weight
  76. current[1] += Number(isIndent);
  77. }
  78. });
  79. var amount = getMostUsed(indents);
  80. var type;
  81. var actual;
  82. if (!amount) {
  83. type = null;
  84. actual = '';
  85. } else if (spaces >= tabs) {
  86. type = 'space';
  87. actual = repeating(' ', amount);
  88. } else {
  89. type = 'tab';
  90. actual = repeating('\t', amount);
  91. }
  92. return {
  93. amount: amount,
  94. type: type,
  95. indent: actual
  96. };
  97. };