Skip to content

Commit 0d094aa

Browse files
authored
feat: sorted installed toolchains (#611)
This PR ensures that the installed toolchains in all commands that display them are sorted in a sensible manner.
1 parent ba0d7e4 commit 0d094aa

File tree

4 files changed

+92
-11
lines changed

4 files changed

+92
-11
lines changed

vscode-lean4/src/utils/elan.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z, ZodTypeAny } from 'zod'
44
import { batchExecute, batchExecuteWithProgress, ExecutionExitCode, ExecutionResult } from './batch'
55
import { FileUri } from './exturi'
66
import { groupByUniqueKey } from './groupBy'
7+
import { semVerRegex } from './semverRegex'
78

89
export const elanStableChannel = 'leanprover/lean4:stable'
910
export const elanNightlyChannel = 'leanprover/lean4:nightly'
@@ -14,11 +15,6 @@ export function isElanEagerResolutionVersion(version: SemVer) {
1415
return version.major >= elanEagerResolutionMajorVersion
1516
}
1617

17-
// Suggested at https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
18-
19-
const semVerRegex =
20-
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
21-
2218
const elanVersionRegex =
2319
/^elan ((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)/
2420

vscode-lean4/src/utils/elanCommands.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import assert from 'assert'
12
import { promises } from 'fs'
3+
import { SemVer, valid } from 'semver'
24
import { commands, Disposable, OutputChannel, QuickPickItem, QuickPickItemKind, window } from 'vscode'
35
import { ExecutionExitCode } from './batch'
46
import { LeanClientProvider } from './clientProvider'
@@ -27,6 +29,89 @@ function displayElanNotInstalledError() {
2729
displayNotification('Error', 'Elan is not installed.')
2830
}
2931

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+
30115
export class ElanCommandProvider implements Disposable {
31116
private subscriptions: Disposable[] = []
32117

@@ -212,7 +297,7 @@ export class ElanCommandProvider implements Disposable {
212297
if (toolchainInfo === undefined) {
213298
return
214299
}
215-
const installedToolchains = toolchainInfo.toolchains
300+
const installedToolchains = sortToolchains(toolchainInfo.toolchains)
216301
if (installedToolchains.length === 0) {
217302
displayNotification('Information', 'No Lean versions installed.')
218303
return
@@ -375,7 +460,7 @@ export class ElanCommandProvider implements Disposable {
375460
if (toolchainInfo === undefined) {
376461
return undefined
377462
}
378-
const installedToolchains = toolchainInfo.toolchains
463+
const installedToolchains = sortToolchains(toolchainInfo.toolchains)
379464
const installedToolchainIndex = new Set(installedToolchains)
380465

381466
let stableToolchains: string[] = []

vscode-lean4/src/utils/manifest.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { SemVer } from 'semver'
33
import { Uri } from 'vscode'
44
import { z } from 'zod'
55
import { FileUri } from './exturi'
6-
7-
// Suggested at https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
8-
const semVerRegex =
9-
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
6+
import { semVerRegex } from './semverRegex'
107

118
export interface DirectGitDependency {
129
name: string
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Suggested at https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
2+
export const semVerRegex =
3+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/

0 commit comments

Comments
 (0)