|
| 1 | +import assert from 'assert' |
1 | 2 | import { promises } from 'fs' |
| 3 | +import { SemVer, valid } from 'semver' |
2 | 4 | import { commands, Disposable, OutputChannel, QuickPickItem, QuickPickItemKind, window } from 'vscode' |
3 | 5 | import { ExecutionExitCode } from './batch' |
4 | 6 | import { LeanClientProvider } from './clientProvider' |
@@ -27,6 +29,89 @@ function displayElanNotInstalledError() { |
27 | 29 | displayNotification('Error', 'Elan is not installed.') |
28 | 30 | } |
29 | 31 |
|
| 32 | +type LeanToolchain = |
| 33 | + | { kind: 'Unknown'; fullName: string } |
| 34 | + | { kind: 'Release'; fullName: string; version: SemVer } |
| 35 | + | { kind: 'Nightly'; fullName: string; date: Date } |
| 36 | + | { kind: 'PRRelease'; fullName: string; pr: number } |
| 37 | + |
| 38 | +function parseToolchain(toolchain: string): LeanToolchain { |
| 39 | + const releaseMatch = toolchain.match(/leanprover\/lean4:(.+)/) |
| 40 | + if (releaseMatch) { |
| 41 | + let version = releaseMatch[1] |
| 42 | + if (version[0] === 'v') { |
| 43 | + version = version.substring(1) |
| 44 | + } |
| 45 | + if (valid(version)) { |
| 46 | + return { kind: 'Release', fullName: toolchain, version: new SemVer(version) } |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + const nightlyMatch = toolchain.match(/leanprover\/lean4-nightly:nightly-(.+)/) |
| 51 | + if (nightlyMatch) { |
| 52 | + const date = new Date(nightlyMatch[1]) |
| 53 | + if (!isNaN(date.valueOf())) { |
| 54 | + return { kind: 'Nightly', fullName: toolchain, date } |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + const prReleaseMatch = toolchain.match(/leanprover\/lean4-pr-releases:pr-release-(\d+)/) |
| 59 | + if (prReleaseMatch) { |
| 60 | + const pr = Number.parseInt(prReleaseMatch[1]) |
| 61 | + return { kind: 'PRRelease', fullName: toolchain, pr } |
| 62 | + } |
| 63 | + |
| 64 | + return { kind: 'Unknown', fullName: toolchain } |
| 65 | +} |
| 66 | + |
| 67 | +function toolchainKindPrio(k: 'Unknown' | 'Release' | 'Nightly' | 'PRRelease'): number { |
| 68 | + switch (k) { |
| 69 | + case 'Unknown': |
| 70 | + return 3 |
| 71 | + case 'Release': |
| 72 | + return 2 |
| 73 | + case 'Nightly': |
| 74 | + return 1 |
| 75 | + case 'PRRelease': |
| 76 | + return 0 |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +function compareToolchainKinds( |
| 81 | + k1: 'Unknown' | 'Release' | 'Nightly' | 'PRRelease', |
| 82 | + k2: 'Unknown' | 'Release' | 'Nightly' | 'PRRelease', |
| 83 | +): number { |
| 84 | + return toolchainKindPrio(k1) - toolchainKindPrio(k2) |
| 85 | +} |
| 86 | + |
| 87 | +function compareToolchains(t1: LeanToolchain, t2: LeanToolchain): number { |
| 88 | + const kindComparison = compareToolchainKinds(t1.kind, t2.kind) |
| 89 | + if (kindComparison !== 0) { |
| 90 | + return kindComparison |
| 91 | + } |
| 92 | + switch (t1.kind) { |
| 93 | + case 'Unknown': |
| 94 | + assert(t2.kind === 'Unknown') |
| 95 | + return -1 * t1.fullName.localeCompare(t2.fullName) |
| 96 | + case 'Release': |
| 97 | + assert(t2.kind === 'Release') |
| 98 | + return t1.version.compare(t2.version) |
| 99 | + case 'Nightly': |
| 100 | + assert(t2.kind === 'Nightly') |
| 101 | + return t1.date.valueOf() - t2.date.valueOf() |
| 102 | + case 'PRRelease': |
| 103 | + assert(t2.kind === 'PRRelease') |
| 104 | + return t1.pr - t2.pr |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +function sortToolchains(ts: string[]): string[] { |
| 109 | + return ts |
| 110 | + .map(t => parseToolchain(t)) |
| 111 | + .sort((t1, t2) => -1 * compareToolchains(t1, t2)) |
| 112 | + .map(t => t.fullName) |
| 113 | +} |
| 114 | + |
30 | 115 | export class ElanCommandProvider implements Disposable { |
31 | 116 | private subscriptions: Disposable[] = [] |
32 | 117 |
|
@@ -212,7 +297,7 @@ export class ElanCommandProvider implements Disposable { |
212 | 297 | if (toolchainInfo === undefined) { |
213 | 298 | return |
214 | 299 | } |
215 | | - const installedToolchains = toolchainInfo.toolchains |
| 300 | + const installedToolchains = sortToolchains(toolchainInfo.toolchains) |
216 | 301 | if (installedToolchains.length === 0) { |
217 | 302 | displayNotification('Information', 'No Lean versions installed.') |
218 | 303 | return |
@@ -375,7 +460,7 @@ export class ElanCommandProvider implements Disposable { |
375 | 460 | if (toolchainInfo === undefined) { |
376 | 461 | return undefined |
377 | 462 | } |
378 | | - const installedToolchains = toolchainInfo.toolchains |
| 463 | + const installedToolchains = sortToolchains(toolchainInfo.toolchains) |
379 | 464 | const installedToolchainIndex = new Set(installedToolchains) |
380 | 465 |
|
381 | 466 | let stableToolchains: string[] = [] |
|
0 commit comments