UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

97 lines (96 loc) • 3.44 kB
export function partitionSourceFiles(sourceRoot, files, maxFiles = 12) { if (maxFiles <= 0) { throw new Error("maxFiles must be greater than zero"); } return partitionAt(sourceRoot.replace(/\\/g, "/"), normalizeFiles(files), maxFiles, 0); } function normalizeFiles(files) { return [...new Set(files.map((file) => file.replace(/\\/g, "/")))].sort((a, b) => a.localeCompare(b)); } function partitionAt(sourceRoot, files, maxFiles, depth) { if (files.length === 0) return []; if (files.length <= maxFiles) { return [{ label: commonLabel(sourceRoot, files, depth), files }]; } const directFiles = []; const buckets = new Map(); for (const file of files) { const relativePath = file.startsWith(`${sourceRoot}/`) ? file.slice(sourceRoot.length + 1) : file; const parts = relativePath.split("/"); if (parts.length <= depth + 1) { directFiles.push(file); continue; } const segment = parts[depth]; if (!segment) { directFiles.push(file); continue; } const bucket = buckets.get(segment) ?? []; bucket.push(file); buckets.set(segment, bucket); } if (buckets.size === 0) { return chunkFiles(currentLabel(sourceRoot, files, depth), files, maxFiles); } const groups = chunkFiles(currentLabel(sourceRoot, files, depth), directFiles, maxFiles); for (const [segment, bucketFiles] of [...buckets.entries()].sort(([left], [right]) => left.localeCompare(right))) { if (bucketFiles.length <= maxFiles) { groups.push({ label: `${sourceRoot}/${bucketPrefix(bucketFiles, sourceRoot, depth, segment)}`, files: bucketFiles, }); } else { groups.push(...partitionAt(sourceRoot, bucketFiles, maxFiles, depth + 1)); } } return groups; } function chunkFiles(label, files, maxFiles) { if (files.length === 0) return []; if (files.length <= maxFiles) return [{ label, files }]; const chunks = []; for (let index = 0; index < files.length; index += maxFiles) { const part = Math.floor(index / maxFiles) + 1; chunks.push({ label: `${label}#${part}`, files: files.slice(index, index + maxFiles), }); } return chunks; } function currentLabel(sourceRoot, files, depth) { if (depth === 0) return sourceRoot; const first = files[0]; if (!first) return sourceRoot; const relativePath = first.startsWith(`${sourceRoot}/`) ? first.slice(sourceRoot.length + 1) : first; const parts = relativePath.split("/").slice(0, depth); return parts.length === 0 ? sourceRoot : `${sourceRoot}/${parts.join("/")}`; } function commonLabel(sourceRoot, files, depth) { if (depth === 0) return sourceRoot; if (files.length === 1) return files[0] ?? sourceRoot; return currentLabel(sourceRoot, files, depth); } function bucketPrefix(files, sourceRoot, depth, segment) { const first = files[0]; if (!first || depth === 0) return segment; const relativePath = first.startsWith(`${sourceRoot}/`) ? first.slice(sourceRoot.length + 1) : first; const parts = relativePath.split("/").slice(0, depth); return [...parts, segment].join("/"); }