From fa4b02fa67e5d04e37de7c767f811d497a72f95e Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Mon, 15 Jan 2024 12:13:09 -0500 Subject: [PATCH] feat(vim.version): add `vim.version.le` and `vim.version.ge` - Problem: One cannot easily write something like, for example: `version_current >= {0, 10, 0}`; writing like `not vim.version.lt(version_current, {0, 10, 0})` is verbose. - Solution: add {`le`,`ge`} in addition to {`lt`,`gt`}. - Also improve typing on the operator methods: allow `string` as well. - Update the example in `vim.version.range()` docs: `ge` in place of `gt` better matches the semantics of `range:has`. --- runtime/doc/lua.txt | 46 ++++++++++++++++++++-------- runtime/doc/news.txt | 2 ++ runtime/lua/vim/version.lua | 42 +++++++++++++++++-------- test/functional/lua/version_spec.lua | 34 ++++++++++++++++++++ 4 files changed, 100 insertions(+), 24 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 24474255ba..433a9fc266 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -3576,8 +3576,8 @@ vim.version.cmp({v1}, {v2}) *vim.version.cmp()* otherwise-equivalent versions. Parameters: ~ - • {v1} (`Version|number[]`) Version object. - • {v2} (`Version|number[]`) Version to compare with `v1` . + • {v1} (`Version|number[]|string`) Version object. + • {v2} (`Version|number[]|string`) Version to compare with `v1` . Return: ~ (`integer`) -1 if `v1 < v2`, 0 if `v1 == v2`, 1 if `v1 > v2`. @@ -3587,8 +3587,18 @@ vim.version.eq({v1}, {v2}) *vim.version.eq()* usage. Parameters: ~ - • {v1} (`Version|number[]`) - • {v2} (`Version|number[]`) + • {v1} (`Version|number[]|string`) + • {v2} (`Version|number[]|string`) + + Return: ~ + (`boolean`) + +vim.version.ge({v1}, {v2}) *vim.version.ge()* + Returns `true` if `v1 >= v2` . See |vim.version.cmp()| for usage. + + Parameters: ~ + • {v1} (`Version|number[]|string`) + • {v2} (`Version|number[]|string`) Return: ~ (`boolean`) @@ -3597,8 +3607,8 @@ vim.version.gt({v1}, {v2}) *vim.version.gt()* Returns `true` if `v1 > v2` . See |vim.version.cmp()| for usage. Parameters: ~ - • {v1} (`Version|number[]`) - • {v2} (`Version|number[]`) + • {v1} (`Version|number[]|string`) + • {v2} (`Version|number[]|string`) Return: ~ (`boolean`) @@ -3612,12 +3622,22 @@ vim.version.last({versions}) *vim.version.last()* Return: ~ (`Version?`) +vim.version.le({v1}, {v2}) *vim.version.le()* + Returns `true` if `v1 <= v2` . See |vim.version.cmp()| for usage. + + Parameters: ~ + • {v1} (`Version|number[]|string`) + • {v2} (`Version|number[]|string`) + + Return: ~ + (`boolean`) + vim.version.lt({v1}, {v2}) *vim.version.lt()* Returns `true` if `v1 < v2` . See |vim.version.cmp()| for usage. Parameters: ~ - • {v1} (`Version|number[]`) - • {v2} (`Version|number[]`) + • {v1} (`Version|number[]|string`) + • {v2} (`Version|number[]|string`) Return: ~ (`boolean`) @@ -3638,7 +3658,8 @@ vim.version.parse({version}, {opts}) *vim.version.parse()* "1.0", "0-x", "tmux 3.2a" into valid versions. Return: ~ - (`table?`) parsed_version Version object or `nil` if input is invalid. + (`Version?`) parsed_version Version object or `nil` if input is + invalid. See also: ~ • # https://semver.org/spec/v2.0.0.html @@ -3662,9 +3683,10 @@ vim.version.range({spec}) *vim.version.range()* print(r:has(vim.version())) -- check against current Nvim version < - Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly: >lua - local r = vim.version.range('1.0.0 - 2.0.0') - print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to)) + Or use cmp(), le(), lt(), ge(), gt(), and/or eq() to compare a version + against `.to` and `.from` directly: >lua + local r = vim.version.range('1.0.0 - 2.0.0') -- >=1.0, <2.0 + print(vim.version.ge({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to)) < Parameters: ~ diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 29b0ec1ec6..899127ff5a 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -294,6 +294,8 @@ The following new APIs and features were added. • Terminal buffers respond to OSC background and foreground requests. |default-autocmds| +• |vim.version.le()| and |vim.version.ge()| are added to |vim.version|. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua index 0873402e29..4f52938c6e 100644 --- a/runtime/lua/vim/version.lua +++ b/runtime/lua/vim/version.lua @@ -259,11 +259,12 @@ end --- print(r:has(vim.version())) -- check against current Nvim version --- ``` --- ---- Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly: +--- Or use cmp(), le(), lt(), ge(), gt(), and/or eq() to compare a version +--- against `.to` and `.from` directly: --- --- ```lua ---- local r = vim.version.range('1.0.0 - 2.0.0') ---- print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to)) +--- local r = vim.version.range('1.0.0 - 2.0.0') -- >=1.0, <2.0 +--- print(vim.version.ge({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to)) --- ``` --- --- @see # https://github.com/npm/node-semver#ranges @@ -364,8 +365,8 @@ end --- --- @note Per semver, build metadata is ignored when comparing two otherwise-equivalent versions. --- ----@param v1 Version|number[] Version object. ----@param v2 Version|number[] Version to compare with `v1`. +---@param v1 Version|number[]|string Version object. +---@param v2 Version|number[]|string Version to compare with `v1`. ---@return integer -1 if `v1 < v2`, 0 if `v1 == v2`, 1 if `v1 > v2`. function M.cmp(v1, v2) local v1_parsed = assert(M._version(v1), create_err_msg(v1)) @@ -380,24 +381,40 @@ function M.cmp(v1, v2) end ---Returns `true` if the given versions are equal. See |vim.version.cmp()| for usage. ----@param v1 Version|number[] ----@param v2 Version|number[] +---@param v1 Version|number[]|string +---@param v2 Version|number[]|string ---@return boolean function M.eq(v1, v2) return M.cmp(v1, v2) == 0 end +---Returns `true` if `v1 <= v2`. See |vim.version.cmp()| for usage. +---@param v1 Version|number[]|string +---@param v2 Version|number[]|string +---@return boolean +function M.le(v1, v2) + return M.cmp(v1, v2) <= 0 +end + ---Returns `true` if `v1 < v2`. See |vim.version.cmp()| for usage. ----@param v1 Version|number[] ----@param v2 Version|number[] +---@param v1 Version|number[]|string +---@param v2 Version|number[]|string ---@return boolean function M.lt(v1, v2) return M.cmp(v1, v2) == -1 end +---Returns `true` if `v1 >= v2`. See |vim.version.cmp()| for usage. +---@param v1 Version|number[]|string +---@param v2 Version|number[]|string +---@return boolean +function M.ge(v1, v2) + return M.cmp(v1, v2) >= 0 +end + ---Returns `true` if `v1 > v2`. See |vim.version.cmp()| for usage. ----@param v1 Version|number[] ----@param v2 Version|number[] +---@param v1 Version|number[]|string +---@param v2 Version|number[]|string ---@return boolean function M.gt(v1, v2) return M.cmp(v1, v2) == 1 @@ -417,7 +434,7 @@ end --- - strict (boolean): Default false. If `true`, no coercion is attempted on --- input not conforming to semver v2.0.0. If `false`, `parse()` attempts to --- coerce input such as "1.0", "0-x", "tmux 3.2a" into valid versions. ----@return table|nil parsed_version Version object or `nil` if input is invalid. +---@return Version? parsed_version Version object or `nil` if input is invalid. function M.parse(version, opts) assert(type(version) == 'string', create_err_msg(version)) opts = opts or { strict = false } @@ -426,6 +443,7 @@ end setmetatable(M, { --- Returns the current Nvim version. + ---@return Version __call = function() local version = vim.fn.api_info().version -- Workaround: vim.fn.api_info().version reports "prerelease" as a boolean. diff --git a/test/functional/lua/version_spec.lua b/test/functional/lua/version_spec.lua index c321421ad0..3bc9e26d41 100644 --- a/test/functional/lua/version_spec.lua +++ b/test/functional/lua/version_spec.lua @@ -288,21 +288,55 @@ describe('version', function() eq(vim.version.last({ v('2.0.0'), v('1.2.3') }), v('2.0.0')) end) + it('le()', function() + eq(true, vim.version.le('1', '1')) + eq(true, vim.version.le({ 3, 1, 4 }, '3.1.4')) + eq(true, vim.version.le('1', '2')) + eq(true, vim.version.le({ 0, 7, 4 }, { 3 })) + eq(false, vim.version.le({ 3 }, { 0, 7, 4 })) + eq(false, vim.version.le({ major = 3, minor = 3, patch = 0 }, { 3, 2, 0 })) + eq(false, vim.version.le('2', '1')) + end) + it('lt()', function() + eq(false, vim.version.lt('1', '1')) + eq(false, vim.version.lt({ 3, 1, 4 }, '3.1.4')) eq(true, vim.version.lt('1', '2')) + eq(true, vim.version.lt({ 0, 7, 4 }, { 3 })) eq(false, vim.version.lt({ 3 }, { 0, 7, 4 })) eq(false, vim.version.lt({ major = 3, minor = 3, patch = 0 }, { 3, 2, 0 })) + eq(false, vim.version.lt('2', '1')) + end) + + it('ge()', function() + eq(true, vim.version.ge('1', '1')) + eq(true, vim.version.ge({ 3, 1, 4 }, '3.1.4')) + eq(true, vim.version.ge('2', '1')) + eq(true, vim.version.ge({ 3 }, { 0, 7, 4 })) + eq(true, vim.version.ge({ major = 3, minor = 3, patch = 0 }, { 3, 2, 0 })) + eq(false, vim.version.ge('1', '2')) + eq(false, vim.version.ge({ 0, 7, 4 }, { 3 })) end) it('gt()', function() + eq(false, vim.version.gt('1', '1')) + eq(false, vim.version.gt({ 3, 1, 4 }, '3.1.4')) eq(true, vim.version.gt('2', '1')) eq(true, vim.version.gt({ 3 }, { 0, 7, 4 })) eq(true, vim.version.gt({ major = 3, minor = 3, patch = 0 }, { 3, 2, 0 })) + eq(false, vim.version.gt('1', '2')) + eq(false, vim.version.gt({ 0, 7, 4 }, { 3 })) end) it('eq()', function() eq(true, vim.version.eq('2', '2')) eq(true, vim.version.eq({ 3, 1, 0 }, '3.1.0')) eq(true, vim.version.eq({ major = 3, minor = 3, patch = 0 }, { 3, 3, 0 })) + eq(false, vim.version.eq('2', '3')) + + -- semver: v3 == v3.0 == v3.0.0 + eq(true, vim.version.eq('3', { 3, 0, 0 })) + eq(true, vim.version.eq({ 3, 0 }, { 3 })) + eq(true, vim.version.eq({ 3, 0, 0 }, { 3 })) end) end)