diff --git a/substore/rename.js b/substore/rename.js index eca38c9..c463b6f 100644 --- a/substore/rename.js +++ b/substore/rename.js @@ -9,6 +9,10 @@ function operator(proxies) { const globalCount = Object.create(null); const currentCount = Object.create(null); const baseNames = new Array(proxies.length); + const normalizedCoreNames = new Array(proxies.length); + const subNames = new Array(proxies.length); + const sequenceInfos = new Array(proxies.length); + const sequenceGroups = Object.create(null); const suffixRegexCache = Object.create(null); const regionAliases = [ @@ -689,6 +693,42 @@ function operator(proxies) { .replace(/\s{2,}/g, ' ') .trim(); + const regionSequenceOf = name => { + const sequenceMatch = name.match(/^((?:[\u{1F1E6}-\u{1F1FF}]{2}|🏴‍☠️)\s*)?([A-Z]{2}(?:\s+[A-Z]{3})?)(?:\s+(\d{2}))?(\s.*)?$/u); + if (!sequenceMatch) return null; + + const prefix = sequenceMatch[1] || ''; + const regionCode = sequenceMatch[2]; + const sequenceNumber = sequenceMatch[3] || ''; + const rest = sequenceMatch[4] || ''; + + return { + prefix, + regionCode, + sequenceNumber, + rest, + key: `${prefix}${regionCode}\u0000${rest.trim()}` + }; + }; + + const withRegionSequence = (sequenceInfo, sequenceNumber) => { + return `${sequenceInfo.prefix}${sequenceInfo.regionCode} ${sequenceNumber}${sequenceInfo.rest}` + .replace(/\s{2,}/g, ' ') + .trim(); + }; + + const nextRegionSequence = sequenceGroup => { + while (sequenceGroup.used[sequenceGroup.next.toString().padStart(2, '0')]) { + sequenceGroup.next += 1; + } + + const sequenceNumber = sequenceGroup.next.toString().padStart(2, '0'); + sequenceGroup.used[sequenceNumber] = true; + sequenceGroup.next += 1; + + return sequenceNumber; + }; + const normalizeBilingualName = name => { const bilingualMatch = name.match(/^(.+?)\s*|\s*(.+)$/); if (!bilingualMatch) return name; @@ -763,7 +803,36 @@ function operator(proxies) { const subName = typeof proxy._subName === 'string' ? proxy._subName.trim() : ''; const coreName = stripExistingSuffix(proxyName, subName); const normalizedCoreName = normalizeCoreName(coreName); - const baseName = subName ? `${normalizedCoreName} - ${subName}` : normalizedCoreName; + const sequenceInfo = regionSequenceOf(normalizedCoreName); + + normalizedCoreNames[proxyIndex] = normalizedCoreName; + subNames[proxyIndex] = subName; + sequenceInfos[proxyIndex] = sequenceInfo; + + if (sequenceInfo) { + const sequenceGroupKey = `${subName}\u0000${sequenceInfo.key}`; + const sequenceGroup = sequenceGroups[sequenceGroupKey] || ( + sequenceGroups[sequenceGroupKey] = { used: Object.create(null), next: 1 } + ); + + if (sequenceInfo.sequenceNumber) { + sequenceGroup.used[sequenceInfo.sequenceNumber] = true; + } + } + }); + + normalizedCoreNames.forEach((normalizedCoreName, proxyIndex) => { + const subName = subNames[proxyIndex]; + const sequenceInfo = sequenceInfos[proxyIndex]; + const needsLeadingSequence = sequenceInfo && !sequenceInfo.sequenceNumber; + const sequenceGroupKey = sequenceInfo ? `${subName}\u0000${sequenceInfo.key}` : ''; + const sequenceNumber = needsLeadingSequence + ? nextRegionSequence(sequenceGroups[sequenceGroupKey]) + : ''; + const coreName = needsLeadingSequence + ? withRegionSequence(sequenceInfo, sequenceNumber) + : normalizedCoreName; + const baseName = subName ? `${coreName} - ${subName}` : coreName; baseNames[proxyIndex] = baseName; globalCount[baseName] = (globalCount[baseName] || 0) + 1;