UNPKG

@sap/cds-dk

Version:

Command line client and development toolkit for the SAP Cloud Application Programming Model

58 lines (52 loc) 2.37 kB
const indentRegEx = new RegExp(/^(\s*)/) const leadingSpacesRegEx = new RegExp(/^( +)/) const leadingTabsRegEx = new RegExp(/^(\t+)/) /** * Greatest Common Divisor Euclidean algorithm * @param {number[]} xs - list of number to find the gcd of. * @returns {number} - greatest common divisor of all numbers in xs. */ function gcd (xs) { const euclid = (a, b) => b === 0 ? a : euclid(b, a % b) const [a, b, ...tail] = xs return tail.length ? gcd([euclid(a, b)].concat(tail)) : euclid(a, b ?? 0) // coalesce in case xs consists of exactly one element } /** * Detects indent used for a file. * The algorithm inspects the indentation of every line to find whether tabs or spaces are more prevalent. * Once that has been determined, it derives the spacing by calculating the gcd of all relevant lines. * I.e. if there are lines indented with 2, 4, or 8 spaces, it will find ' ' as indent. * If lines are indented using 1 or 2 tabs, '\t' will be returned. * TODO: filter out outliers, i.e. if the user used three spaces in one line, should ' ' space be used for spacing? Or should that be ignored? * @param {string[]} file - lines of a file. * @returns {string | null} - the string used to space (e.g. 2 spaces, 4 spaces, 1 tab, etc.) or null if the file was empty. */ function detectIndent (file) { if (file.length === 0) return null if (file.length === 1) return indentRegEx.exec(file[0])[0] const indents = file .map(line => indentRegEx.exec(line)[0]) .filter(Boolean) .reduce((bins, indent) => { const type = leadingSpacesRegEx.test(indent) ? 'spaces' : leadingTabsRegEx.test(indent) ? 'tabs' : undefined if (type) { const length = indent.split('').length const bin = bins[type] bin.total++ bin.indents[length] = (bin.indents[length] ?? 0) + 1 } return bins }, { tabs: { indents: {}, total: 0, character: '\t' }, spaces: { indents: {}, total: 0, character: ' ' } }) const preferred = indents.tabs.total > indents.spaces.total ? indents.tabs : indents.spaces const width = gcd(Object.keys(preferred.indents)) ?? 2 return preferred.character.repeat(width) } module.exports = { detectIndent }