Skip to content

Commit 988871a

Browse files
authored
refactor: clean up semver handling (#761)
1 parent 6652840 commit 988871a

File tree

9 files changed

+61
-38
lines changed

9 files changed

+61
-38
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vscode-lean4/src/diagnostics/setupDiagnoser.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as os from 'os'
2-
import * as s from 'semver'
2+
import * as semver from 'semver'
33
import { SemVer } from 'semver'
44
import { OutputChannel, extensions, version } from 'vscode'
55
import { ExecutionExitCode, ExecutionResult, batchExecute } from '../utils/batch'
@@ -8,10 +8,11 @@ import {
88
ElanDumpStateWithoutNetResult,
99
elanDumpStateWithNet,
1010
elanDumpStateWithoutNet,
11+
elanVersionRegex,
1112
isElanEagerResolutionVersion,
1213
} from '../utils/elan'
1314
import { FileUri } from '../utils/exturi'
14-
import { ToolchainUpdateMode, leanRunner } from '../utils/leanCmdRunner'
15+
import { ToolchainUpdateMode, leanRunner, leanVersionRegex } from '../utils/leanCmdRunner'
1516
import { checkParentFoldersForLeanProject, isValidLeanProject } from '../utils/projectInfo'
1617

1718
const minimumSupportedMacOSVersion = new SemVer('19.0.0')
@@ -23,11 +24,10 @@ export type OSVersionDiagnosis =
2324

2425
function diagnoseOSVersion(): OSVersionDiagnosis {
2526
// When in doubt, we consider an OS version as not being unsupported.
26-
const release = os.release()
27-
if (!s.valid(release)) {
27+
const currentVersion = semver.parse(os.release())
28+
if (currentVersion === null) {
2829
return { kind: 'NotUnsupported' }
2930
}
30-
const currentVersion = new SemVer(release)
3131
switch (os.type()) {
3232
case 'Darwin':
3333
if (currentVersion.compare(minimumSupportedMacOSVersion) >= 0) {
@@ -114,7 +114,12 @@ export function versionQueryResult(executionResult: ExecutionResult, versionRege
114114
return { kind: 'InvalidVersion', versionResult: executionResult.stdout }
115115
}
116116

117-
return { kind: 'Success', version: new SemVer(match[1]) }
117+
const parsed = semver.parse(match[1])
118+
if (parsed === null) {
119+
return { kind: 'InvalidVersion', versionResult: executionResult.stdout }
120+
}
121+
122+
return { kind: 'Success', version: parsed }
118123
}
119124

120125
export function checkElanVersion(elanVersionResult: VersionQueryResult): ElanVersionDiagnosis {
@@ -313,12 +318,12 @@ export class SetupDiagnoser {
313318

314319
async queryLeanVersion(): Promise<VersionQueryResult> {
315320
const leanVersionResult = await this.runLeanCommand('lean', ['--version'], 'Checking Lean version')
316-
return versionQueryResult(leanVersionResult, /version (\d+\.\d+\.\d+(\w|-)*)/)
321+
return versionQueryResult(leanVersionResult, leanVersionRegex)
317322
}
318323

319324
async queryElanVersion(): Promise<VersionQueryResult> {
320325
const elanVersionResult = await this.runSilently('elan', ['--version'])
321-
return versionQueryResult(elanVersionResult, /elan (\d+\.\d+\.\d+)/)
326+
return versionQueryResult(elanVersionResult, elanVersionRegex)
322327
}
323328

324329
async queryElanShow(): Promise<ExecutionResult> {

vscode-lean4/src/leanclient.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import { logger } from './utils/logger'
5151
// @ts-ignore
5252
import * as fs from 'fs'
5353
import { glob } from 'glob'
54-
import { SemVer } from 'semver'
54+
import * as semver from 'semver'
5555
import { formatCommandExecutionOutput } from './utils/batch'
5656
import {
5757
c2pConverter,
@@ -541,8 +541,9 @@ export class LeanClient implements Disposable {
541541
}
542542
})
543543
await this.client.start()
544-
const version = this.client.initializeResult?.serverInfo?.version
545-
if (version && new SemVer(version).compare('0.2.0') < 0) {
544+
const rawVersion = this.client.initializeResult?.serverInfo?.version
545+
const version = rawVersion !== undefined ? semver.parse(rawVersion) : null
546+
if (version !== null && version.compare('0.2.0') < 0) {
546547
if (this.staleDepNotifier) {
547548
this.staleDepNotifier.dispose()
548549
}

vscode-lean4/src/utils/elan.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import * as semver from 'semver'
12
import { SemVer } from 'semver'
23
import { OutputChannel } from 'vscode'
34
import { z, ZodTypeAny } from 'zod'
45
import { batchExecute, batchExecuteWithProgress, ExecutionExitCode, ExecutionResult } from './batch'
56
import { FileUri } from './exturi'
67
import { groupByUniqueKey } from './groupBy'
7-
import { semVerRegex } from './semverRegex'
8+
import { semVerSchema } from './zod'
89

910
export const elanStableChannel = 'leanprover/lean4:stable'
1011
export const elanNightlyChannel = 'leanprover/lean4:nightly'
@@ -15,8 +16,7 @@ export function isElanEagerResolutionVersion(version: SemVer) {
1516
return version.major >= elanEagerResolutionMajorVersion
1617
}
1718

18-
const elanVersionRegex =
19-
/^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-]+)*))?)/
19+
export const elanVersionRegex = /^elan (\S+)/
2020

2121
export type ElanVersionResult =
2222
| { kind: 'Success'; version: SemVer }
@@ -31,7 +31,11 @@ export async function elanVersion(): Promise<ElanVersionResult> {
3131
if (match === null) {
3232
return { kind: 'ExecutionError', message: 'Cannot parse output of `elan --version`: ' + r.stdout }
3333
}
34-
return { kind: 'Success', version: new SemVer(match[1]) }
34+
const version = semver.parse(match[1])
35+
if (version === null) {
36+
return { kind: 'ExecutionError', message: 'Cannot parse output of `elan --version`: ' + r.stdout }
37+
}
38+
return { kind: 'Success', version }
3539
case ExecutionExitCode.CannotLaunch:
3640
return { kind: 'ElanNotInstalled' }
3741
case ExecutionExitCode.ExecutionError:
@@ -305,8 +309,8 @@ function parseElanStateDump(elanDumpStateOutput: string): ElanStateDump | undefi
305309

306310
const stateDumpSchema = z.object({
307311
elan_version: z.object({
308-
current: z.string().regex(semVerRegex),
309-
newest: zodElanResult(z.string().regex(semVerRegex)),
312+
current: semVerSchema,
313+
newest: zodElanResult(semVerSchema),
310314
}),
311315
toolchains: z.object({
312316
installed: z.array(
@@ -349,8 +353,8 @@ function parseElanStateDump(elanDumpStateOutput: string): ElanStateDump | undefi
349353

350354
const stateDump: ElanStateDump = {
351355
elanVersion: {
352-
current: new SemVer(s.elan_version.current),
353-
newest: convertElanResult(s.elan_version.newest, version => new SemVer(version)),
356+
current: s.elan_version.current,
357+
newest: convertElanResult(s.elan_version.newest, v => v),
354358
},
355359
toolchains: {
356360
installed,

vscode-lean4/src/utils/elanCommands.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assert from 'assert'
22
import { promises } from 'fs'
3-
import { SemVer, valid } from 'semver'
3+
import * as semver from 'semver'
4+
import { SemVer } from 'semver'
45
import { commands, Disposable, OutputChannel, QuickPickItem, QuickPickItemKind, window } from 'vscode'
56
import { ExecutionExitCode } from './batch'
67
import { LeanClientProvider } from './clientProvider'
@@ -43,8 +44,9 @@ function parseToolchain(toolchain: string): LeanToolchain {
4344
if (version[0] === 'v') {
4445
version = version.substring(1)
4546
}
46-
if (valid(version)) {
47-
return { kind: 'Release', fullName: toolchain, version: new SemVer(version) }
47+
const parsed = semver.parse(version)
48+
if (parsed !== null) {
49+
return { kind: 'Release', fullName: toolchain, version: parsed }
4850
}
4951
}
5052

vscode-lean4/src/utils/leanCmdRunner.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
import { FileUri } from './exturi'
1818
import { displayNotification, displayNotificationWithInput } from './notifs'
1919

20+
export const leanVersionRegex = /version ([^,\s]+)/
21+
2022
export type ToolchainUpdateMode = 'UpdateAutomatically' | 'PromptAboutUpdate' | 'DoNotUpdate'
2123

2224
function shouldUpdateToolchainAutomatically(mode: ToolchainUpdateMode) {

vscode-lean4/src/utils/manifest.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { SemVer } from 'semver'
33
import { Uri } from 'vscode'
44
import { z } from 'zod'
55
import { FileUri } from './exturi'
6-
import { semVerRegex } from './semverRegex'
6+
import { semVerSchema } from './zod'
77

88
export interface DirectGitDependency {
99
name: string
@@ -23,17 +23,15 @@ type ManifestVersion =
2323
| { kind: 'LegacyNumberVersion'; version: number; versionAsSemVer: SemVer }
2424
| { kind: 'SemVer'; version: SemVer }
2525

26-
function asManifestVersion(versionField: number | string): ManifestVersion {
27-
switch (typeof versionField) {
28-
case 'string':
29-
return { kind: 'SemVer', version: new SemVer(versionField) }
30-
case 'number':
31-
return {
32-
kind: 'LegacyNumberVersion',
33-
version: versionField,
34-
versionAsSemVer: new SemVer(`0.${versionField}.0`),
35-
}
26+
function asManifestVersion(versionField: number | SemVer): ManifestVersion {
27+
if (typeof versionField === 'number') {
28+
return {
29+
kind: 'LegacyNumberVersion',
30+
version: versionField,
31+
versionAsSemVer: new SemVer(`0.${versionField}.0`),
32+
}
3633
}
34+
return { kind: 'SemVer', version: versionField }
3735
}
3836

3937
function parseVersion1To6Manifest(version: SemVer, parsedJson: any) {
@@ -150,7 +148,10 @@ export function parseAsManifest(jsonString: string): Manifest | undefined {
150148
}
151149

152150
const versionSchema = z.object({
153-
version: z.union([z.number().int().nonnegative(), z.string().regex(semVerRegex)]),
151+
version: z.union([
152+
z.number().int().nonnegative(),
153+
semVerSchema,
154+
]),
154155
})
155156
const versionResult = versionSchema.safeParse(parsedJson)
156157
if (!versionResult.success) {

vscode-lean4/src/utils/semverRegex.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

vscode-lean4/src/utils/zod.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as semver from 'semver'
2+
import { z } from 'zod'
3+
4+
export const semVerSchema = z.string().transform((v, ctx) => {
5+
const parsed = semver.parse(v)
6+
if (parsed === null) {
7+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Invalid semver' })
8+
return z.NEVER
9+
}
10+
return parsed
11+
})

0 commit comments

Comments
 (0)