Merge #34715 vim.version improvements

This commit is contained in:
Justin M. Keyes
2025-07-01 04:19:42 -07:00
committed by GitHub
4 changed files with 276 additions and 70 deletions

View File

@@ -28,6 +28,7 @@
--- >1.2.3 greater than 1.2.3
--- <1.2.3 before 1.2.3
--- >=1.2.3 at least 1.2.3
--- <=1.2.3 at most 1.2.3
--- ~1.2.3 is >=1.2.3 <1.3.0 "reasonably close to 1.2.3"
--- ^1.2.3 is >=1.2.3 <2.0.0 "compatible with 1.2.3"
--- ^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
@@ -42,7 +43,7 @@
--- * any version
--- x same
---
--- 1.2.3 - 2.3.4 is >=1.2.3 <=2.3.4
--- 1.2.3 - 2.3.4 is >=1.2.3 <2.3.4
---
--- Partial right: missing pieces treated as x (2.3 => 2.3.x).
--- 1.2.3 - 2.3 is >=1.2.3 <2.4.0
@@ -222,40 +223,11 @@ function M.last(versions)
end
---@class vim.VersionRange
---@inlinedoc
---@field from vim.Version
---@field to? vim.Version
local VersionRange = {}
---@nodoc
---@param version string|vim.Version
function VersionRange:has(version)
if type(version) == 'string' then
---@diagnostic disable-next-line: cast-local-type
version = M.parse(version)
elseif getmetatable(version) ~= Version then
-- Need metatable to compare versions.
version = setmetatable(vim.deepcopy(version, true), Version)
end
if version then
if self.from == self.to then
return version == self.from
end
return version >= self.from and (self.to == nil or version < self.to)
end
end
--- Parses a semver |version-range| "spec" and returns a range object:
---
--- ```
--- {
--- from: Version
--- to: Version
--- has(v: string|Version)
--- }
--- ```
---
--- `:has()` checks if a version is in the range (inclusive `from`, exclusive `to`).
--- Check if a version is in the range (inclusive `from`, exclusive `to`).
---
--- Example:
---
@@ -276,12 +248,42 @@ end
---
--- @see # https://github.com/npm/node-semver#ranges
--- @since 11
---
--- @param version string|vim.Version
--- @return boolean
function VersionRange:has(version)
if type(version) == 'string' then
---@diagnostic disable-next-line: cast-local-type
version = M.parse(version)
elseif getmetatable(version) ~= Version then
-- Need metatable to compare versions.
version = setmetatable(vim.deepcopy(version, true), Version)
end
if not version then
return false
end
if self.from == self.to then
return version == self.from
end
return version >= self.from and (self.to == nil or version < self.to)
end
local range_mt = {
__index = VersionRange,
__tostring = function(self)
if not self.to then
return '>=' .. tostring(self.from)
end
return ('%s - %s'):format(tostring(self.from), tostring(self.to))
end,
}
--- Parses a semver |version-range| "spec" and returns |vim.VersionRange| object:
--- @since 11
--- @param spec string Version range "spec"
--- @return vim.VersionRange?
function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
if spec == '*' or spec == '' then
return setmetatable({ from = M.parse('0.0.0') }, { __index = VersionRange })
return setmetatable({ from = M.parse('0.0.0') }, range_mt)
end
---@type number?
@@ -295,7 +297,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
return setmetatable({
from = ra and ra.from,
to = rb and (#parts == 3 and rb.from or rb.to),
}, { __index = VersionRange })
}, range_mt)
end
---@type string, string
local mods, version = spec:lower():match('^([%^=<>~]*)(.*)$')
@@ -314,9 +316,23 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
from = M._version({})
elseif mods == '<=' then
from = M._version({})
to.patch = to.patch + 1
-- HACK: construct the smallest reasonable version bigger than `to`
-- to simulate `<=` while using exclusive right hand side
if to.prerelease then
to.prerelease = to.prerelease .. '.0'
else
to.patch = to.patch + 1
to.prerelease = '0'
end
elseif mods == '>' then
from.patch = from.patch + 1
-- HACK: construct the smallest reasonable version bigger than `from`
-- to simulate `>` while using inclusive left hand side
if from.prerelease then
from.prerelease = from.prerelease .. '.0'
else
from.patch = from.patch + 1
from.prerelease = '0'
end
to = nil
elseif mods == '>=' then
to = nil
@@ -341,7 +357,25 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
end
end
---@diagnostic enable: need-check-nil
return setmetatable({ from = from, to = to }, { __index = VersionRange })
return setmetatable({ from = from, to = to }, range_mt)
end
end
--- Computes the common range shared by the given ranges.
---
--- @since 14
--- @param r1 vim.VersionRange First range to intersect.
--- @param r2 vim.VersionRange Second range to intersect.
--- @return vim.VersionRange? Maximal range that is present inside both `r1` and `r2`.
--- `nil` if such range does not exist.
function M.intersect(r1, r2)
assert(getmetatable(r1) == range_mt)
assert(getmetatable(r2) == range_mt)
local from = r1.from <= r2.from and r2.from or r1.from
local to = (r1.to == nil or (r2.to ~= nil and r2.to <= r1.to)) and r2.to or r1.to
if to == nil or from < to or (from == to and r1:has(from) and r2:has(from)) then
return setmetatable({ from = from, to = to }, VersionRange)
end
end