123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- 'use strict';
- // detect either spaces or tabs but not both to properly handle tabs
- // for indentation and spaces for alignment
- const INDENT_RE = /^(?:( )+|\t+)/;
- function getMostUsed(indents) {
- let result = 0;
- let maxUsed = 0;
- let maxWeight = 0;
- for (const entry of indents) {
- // TODO: use destructuring when targeting Node.js 6
- const key = entry[0];
- const val = entry[1];
- const u = val[0];
- const w = val[1];
- if (u > maxUsed || (u === maxUsed && w > maxWeight)) {
- maxUsed = u;
- maxWeight = w;
- result = Number(key);
- }
- }
- return result;
- }
- module.exports = str => {
- if (typeof str !== 'string') {
- throw new TypeError('Expected a string');
- }
- // used to see if tabs or spaces are the most used
- let tabs = 0;
- let spaces = 0;
- // remember the size of previous line's indentation
- let 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],
- // }
- const indents = new Map();
- // pointer to the array of last used indent
- let current;
- // whether the last action was an indent (opposed to an unindent)
- let isIndent;
- for (const line of str.split(/\n/g)) {
- if (!line) {
- // ignore empty lines
- continue;
- }
- let indent;
- const matches = line.match(INDENT_RE);
- if (matches) {
- indent = matches[0].length;
- if (matches[1]) {
- spaces++;
- } else {
- tabs++;
- }
- } else {
- indent = 0;
- }
- const diff = indent - prev;
- prev = indent;
- if (diff) {
- // an indent or unindent has been detected
- isIndent = diff > 0;
- current = indents.get(isIndent ? diff : -diff);
- if (current) {
- current[0]++;
- } else {
- current = [1, 0];
- indents.set(diff, current);
- }
- } else if (current) {
- // if the last action was an indent, increment the weight
- current[1] += Number(isIndent);
- }
- }
- const amount = getMostUsed(indents);
- let type;
- let indent;
- if (!amount) {
- type = null;
- indent = '';
- } else if (spaces >= tabs) {
- type = 'space';
- indent = ' '.repeat(amount);
- } else {
- type = 'tab';
- indent = '\t'.repeat(amount);
- }
- return {
- amount,
- type,
- indent
- };
- };
|