diff options
Diffstat (limited to 'app/assets/javascripts/ide/lib/editorconfig/parser.js')
-rw-r--r-- | app/assets/javascripts/ide/lib/editorconfig/parser.js | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/app/assets/javascripts/ide/lib/editorconfig/parser.js b/app/assets/javascripts/ide/lib/editorconfig/parser.js new file mode 100644 index 00000000000..a30a8cb868d --- /dev/null +++ b/app/assets/javascripts/ide/lib/editorconfig/parser.js @@ -0,0 +1,55 @@ +import { parseString } from 'editorconfig/src/lib/ini'; +import minimatch from 'minimatch'; +import { getPathParents } from '../../utils'; + +const dirname = path => path.replace(/\.editorconfig$/, ''); + +function isRootConfig(config) { + return config.some(([pattern, rules]) => !pattern && rules?.root === 'true'); +} + +function getRulesForSection(path, [pattern, rules]) { + if (!pattern) { + return {}; + } + if (minimatch(path, pattern, { matchBase: true })) { + return rules; + } + + return {}; +} + +function getRulesWithConfigs(filePath, configFiles = [], rules = {}) { + if (!configFiles.length) return rules; + + const [{ content, path: configPath }, ...nextConfigs] = configFiles; + const configDir = dirname(configPath); + + if (!filePath.startsWith(configDir)) return rules; + + const parsed = parseString(content); + const isRoot = isRootConfig(parsed); + const relativeFilePath = filePath.slice(configDir.length); + + const sectionRules = parsed.reduce( + (acc, section) => Object.assign(acc, getRulesForSection(relativeFilePath, section)), + {}, + ); + + // prefer existing rules by overwriting to section rules + const result = Object.assign(sectionRules, rules); + + return isRoot ? result : getRulesWithConfigs(filePath, nextConfigs, result); +} + +// eslint-disable-next-line import/prefer-default-export +export function getRulesWithTraversal(filePath, getFileContent) { + const editorconfigPaths = [ + ...getPathParents(filePath).map(x => `${x}/.editorconfig`), + '.editorconfig', + ]; + + return Promise.all( + editorconfigPaths.map(path => getFileContent(path).then(content => ({ path, content }))), + ).then(results => getRulesWithConfigs(filePath, results.filter(x => x.content))); +} |