index.js 2.1 KB

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