( uris: vs.Uri[], operationProgress: OperationProgress, runSingleOperation: (uri: vs.Uri, operationProgress: OperationProgress) => Promise<unknown>, )
| 6 | import { config } from "../config"; |
| 7 | |
| 8 | export async function runBatchFolderOperation( |
| 9 | uris: vs.Uri[], |
| 10 | operationProgress: OperationProgress, |
| 11 | runSingleOperation: (uri: vs.Uri, operationProgress: OperationProgress) => Promise<unknown>, |
| 12 | ): Promise<void> { |
| 13 | const hiddenProgress: vs.Progress<{ message?: string; increment?: number }> = { report: (_) => undefined }; |
| 14 | const childOperationProgress: OperationProgress = { |
| 15 | cancellationToken: operationProgress.cancellationToken, |
| 16 | progressReporter: hiddenProgress, |
| 17 | }; |
| 18 | |
| 19 | const maxConcurrent = config.runPubConcurrently |
| 20 | ? Math.min(uris.length, maxConcurrentProcesses) |
| 21 | : 1; |
| 22 | const activePackages = new Map<string, string>(); |
| 23 | let numCompleted = 0; |
| 24 | |
| 25 | function updateProgress(increment?: boolean) { |
| 26 | const activePackageNames = [...activePackages.values()]; |
| 27 | const firstPackageName = activePackageNames[0]; |
| 28 | const numTotal = uris.length; |
| 29 | const numRemaining = numTotal - numCompleted; |
| 30 | |
| 31 | // Instead of using the actual count here (activePackageNames.length - 1), which causes |
| 32 | // flickering as one completes and reduces the count before the next starts, just give |
| 33 | // an approximate value of the cap of Min(maxConcurrent, remaining) - 1 instead. |
| 34 | const otherActiveCount = Math.min(maxConcurrent, numRemaining) - 1; |
| 35 | if (firstPackageName) { |
| 36 | const statusMessage = otherActiveCount > 0 |
| 37 | ? `${firstPackageName} (+ ${otherActiveCount} others)...` |
| 38 | : firstPackageName; |
| 39 | operationProgress.progressReporter.report({ |
| 40 | message: `${statusMessage} (${numCompleted}/${numTotal} total)`, |
| 41 | increment: increment ? 100 / numTotal : undefined, |
| 42 | }); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | updateProgress(); |
| 47 | await runWithConcurrencyLimit( |
| 48 | uris, |
| 49 | maxConcurrent, |
| 50 | operationProgress.cancellationToken, |
| 51 | async (item: vs.Uri) => runSingleOperation(item, childOperationProgress).then(() => undefined), |
| 52 | (newCompleted: number, _total: number, item: vs.Uri) => { |
| 53 | numCompleted = newCompleted; |
| 54 | activePackages.delete(fsPath(item)); |
| 55 | updateProgress(true); |
| 56 | }, |
| 57 | (item: vs.Uri) => { |
| 58 | const itemPath = fsPath(item); |
| 59 | // Before choosing to use the folder name, try to use `package:foo`. |
| 60 | const packageOrFolderDisplayName = getPackageOrFolderDisplayName(itemPath); |
| 61 | activePackages.set(itemPath, packageOrFolderDisplayName); |
| 62 | updateProgress(); |
| 63 | }, |
| 64 | ); |
| 65 | } |
no test coverage detected