|
'use strict'; |
|
|
|
|
|
const INDENT_REGEX = /^(?:( )+|\t+)/; |
|
|
|
const INDENT_TYPE_SPACE = 'space'; |
|
const INDENT_TYPE_TAB = 'tab'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function makeIndentsMap(string, ignoreSingleSpaces) { |
|
const indents = new Map(); |
|
|
|
|
|
let previousSize = 0; |
|
let previousIndentType; |
|
|
|
|
|
let key; |
|
|
|
for (const line of string.split(/\n/g)) { |
|
if (!line) { |
|
|
|
continue; |
|
} |
|
|
|
let indent; |
|
let indentType; |
|
let weight; |
|
let entry; |
|
const matches = line.match(INDENT_REGEX); |
|
|
|
if (matches === null) { |
|
previousSize = 0; |
|
previousIndentType = ''; |
|
} else { |
|
indent = matches[0].length; |
|
|
|
if (matches[1]) { |
|
indentType = INDENT_TYPE_SPACE; |
|
} else { |
|
indentType = INDENT_TYPE_TAB; |
|
} |
|
|
|
|
|
if (ignoreSingleSpaces && indentType === INDENT_TYPE_SPACE && indent === 1) { |
|
continue; |
|
} |
|
|
|
if (indentType !== previousIndentType) { |
|
previousSize = 0; |
|
} |
|
|
|
previousIndentType = indentType; |
|
|
|
weight = 0; |
|
|
|
const indentDifference = indent - previousSize; |
|
previousSize = indent; |
|
|
|
|
|
if (indentDifference === 0) { |
|
weight++; |
|
|
|
} else { |
|
const absoluteIndentDifference = indentDifference > 0 ? indentDifference : -indentDifference; |
|
key = encodeIndentsKey(indentType, absoluteIndentDifference); |
|
} |
|
|
|
|
|
entry = indents.get(key); |
|
|
|
if (entry === undefined) { |
|
entry = [1, 0]; |
|
} else { |
|
entry = [++entry[0], entry[1] + weight]; |
|
} |
|
|
|
indents.set(key, entry); |
|
} |
|
} |
|
|
|
return indents; |
|
} |
|
|
|
|
|
function encodeIndentsKey(indentType, indentAmount) { |
|
const typeCharacter = indentType === INDENT_TYPE_SPACE ? 's' : 't'; |
|
return typeCharacter + String(indentAmount); |
|
} |
|
|
|
|
|
function decodeIndentsKey(indentsKey) { |
|
const keyHasTypeSpace = indentsKey[0] === 's'; |
|
const type = keyHasTypeSpace ? INDENT_TYPE_SPACE : INDENT_TYPE_TAB; |
|
|
|
const amount = Number(indentsKey.slice(1)); |
|
|
|
return {type, amount}; |
|
} |
|
|
|
|
|
|
|
function getMostUsedKey(indents) { |
|
let result; |
|
let maxUsed = 0; |
|
let maxWeight = 0; |
|
|
|
for (const [key, [usedCount, weight]] of indents) { |
|
if (usedCount > maxUsed || (usedCount === maxUsed && weight > maxWeight)) { |
|
maxUsed = usedCount; |
|
maxWeight = weight; |
|
result = key; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
function makeIndentString(type, amount) { |
|
const indentCharacter = type === INDENT_TYPE_SPACE ? ' ' : '\t'; |
|
return indentCharacter.repeat(amount); |
|
} |
|
|
|
module.exports = string => { |
|
if (typeof string !== 'string') { |
|
throw new TypeError('Expected a string'); |
|
} |
|
|
|
|
|
|
|
let indents = makeIndentsMap(string, true); |
|
if (indents.size === 0) { |
|
indents = makeIndentsMap(string, false); |
|
} |
|
|
|
const keyOfMostUsedIndent = getMostUsedKey(indents); |
|
|
|
let type; |
|
let amount = 0; |
|
let indent = ''; |
|
|
|
if (keyOfMostUsedIndent !== undefined) { |
|
({type, amount} = decodeIndentsKey(keyOfMostUsedIndent)); |
|
indent = makeIndentString(type, amount); |
|
} |
|
|
|
return { |
|
amount, |
|
type, |
|
indent |
|
}; |
|
}; |
|
|