Repository: nvim-lua/plenary.nvim
Pull request #515 (2023-08-13 08:27:34)

Fix couple bugs with multiple results and varargs (...)
The `vararg.rotate`, and both `functional.partial` and `fun.bind` (which are the same), had couple mistakes. I'll mention the particular bugs in the diff. For a reference, see: https://www.lua.org/manual/5.4/manual.html#3.4.12.

These fixes make the functions generally correct. Since these bugs have been there from the beginning, the real use is more limited (i.e. bind a single ~~(non nil)~~ parameter, or rotate 1-16 arguments). So another valid fix would be to limit the generality to these already working cases.

Let me know if that would be preferable.


Also let me know if there is some convention I've missed, or if you would like me to document this also on the issues.

Conni2461: rebase should fix the tests, see https://github.com/nvim-lua/plenary.nvim/commit/f97a0765556712a454de600e6e639375760ceb71

Patch:
diff --git a/.luacheckrc b/.luacheckrc
index 256267f..0bd6ca4 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -27,7 +27,6 @@ read_globals = {
 exclude_files = {
   "lua/plenary/profile/lua_profiler.lua",
   "lua/plenary/profile/memory_profiler.lua",
-  "lua/plenary/vararg/rotate.lua",
   "lua/plenary/async_lib/*.lua",
 }
 
diff --git a/lua/plenary/busted.lua b/lua/plenary/busted.lua
index 1072160..4e64a4e 100644
--- a/lua/plenary/busted.lua
+++ b/lua/plenary/busted.lua
@@ -215,6 +215,8 @@ clear = mod.clear
 assert = require "luassert"
 
 mod.run = function(file)
+  file = file:gsub("\\", "/")
+
   print("\n" .. HEADER)
   print("Testing: ", file)
 
diff --git a/lua/plenary/fun.lua b/lua/plenary/fun.lua
index 014c723..e70367f 100644
--- a/lua/plenary/fun.lua
+++ b/lua/plenary/fun.lua
@@ -1,20 +1,6 @@
-local tbl = require "plenary.tbl"
-
 local M = {}
 
-function M.bind(fn, ...)
-  if select("#", ...) == 1 then
-    local arg = ...
-    return function(...)
-      fn(arg, ...)
-    end
-  end
-
-  local args = tbl.pack(...)
-  return function(...)
-    fn(tbl.unpack(args), ...)
-  end
-end
+M.bind = require("plenary.functional").partial
 
 function M.arify(fn, argc)
   return function(...)
diff --git a/lua/plenary/functional.lua b/lua/plenary/functional.lua
index 3ab5bb4..6a5b367 100644
--- a/lua/plenary/functional.lua
+++ b/lua/plenary/functional.lua
@@ -16,11 +16,17 @@ function f.join(array, sep)
   return table.concat(vim.tbl_map(tostring, array), sep)
 end
 
-function f.partial(fun, ...)
-  local args = { ... }
-  return function(...)
-    return fun(unpack(args), ...)
+local function bind_n(fn, n, a, ...)
+  if n == 0 then
+    return fn
   end
+  return bind_n(function(...)
+    return fn(a, ...)
+  end, n - 1, ...)
+end
+
+function f.partial(fun, ...)
+  return bind_n(fun, select("#", ...), ...)
 end
 
 function f.any(fun, iterable)
diff --git a/lua/plenary/profile/p.lua b/lua/plenary/profile/p.lua
index c9ec1d8..9eb6f16 100644
--- a/lua/plenary/profile/p.lua
+++ b/lua/plenary/profile/p.lua
@@ -41,7 +41,7 @@
 
 -- Cache some library functions and objects.
 local jit = require("jit")
-assert(jit.version_num == 20100, "LuaJIT core/library version mismatch")
+assert(20100 <= jit.version_num and jit.version_num <= 20199, "LuaJIT core/library version mismatch: " .. jit.version)
 local profile = require("jit.profile")
 local vmdef = require("jit.vmdef")
 local math = math
diff --git a/lua/plenary/vararg/rotate.lua b/lua/plenary/vararg/rotate.lua
index a2f60d0..6b71c63 100644
--- a/lua/plenary/vararg/rotate.lua
+++ b/lua/plenary/vararg/rotate.lua
@@ -6,10 +6,6 @@ local tbl = require "plenary.tbl"
 
 local rotate_lookup = {}
 
-rotate_lookup[0] = function()
-  return A0
-end
-
 rotate_lookup[1] = function(A0)
   return A0
 end
@@ -71,12 +67,16 @@ rotate_lookup[15] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A
 end
 
 local function rotate_n(first, ...)
+  local n = select("#", ...) + 1
   local args = tbl.pack(...)
-  args[#args + 1] = first
-  return tbl.unpack(args)
+  args[n] = first
+  return tbl.unpack(args, 1, n)
 end
 
 local function rotate(nargs, ...)
+  if nargs == nil or nargs < 1 then
+    return
+  end
   return (rotate_lookup[nargs] or rotate_n)(...)
 end
 
diff --git a/plugin/plenary.vim b/plugin/plenary.vim
index e85b0ec..b9af5cb 100644
--- a/plugin/plenary.vim
+++ b/plugin/plenary.vim
@@ -1,9 +1,9 @@
 
 " Create command for running busted
 command! -nargs=1 -complete=file PlenaryBustedFile
-      \ lua require('plenary.busted').run(vim.fn.expand("<args>"))
+      \ lua require('plenary.busted').run(vim.fn.expand([[<args>]]))
 
 command! -nargs=+ -complete=file PlenaryBustedDirectory
-      \ lua require('plenary.test_harness').test_directory_command("<args>")
+      \ lua require('plenary.test_harness').test_directory_command([[<args>]])
 
 nnoremap <Plug>PlenaryTestFile :lua require('plenary.test_harness').test_directory(vim.fn.expand("%:p"))<CR>
diff --git a/scripts/vararg/rotate.lua b/scripts/vararg/rotate.lua
index 1e1719c..6790a9c 100644
--- a/scripts/vararg/rotate.lua
+++ b/scripts/vararg/rotate.lua
@@ -7,19 +7,23 @@ local tbl = require('plenary.tbl')
 
 local rotate_lookup = {}
 
-{% for n in range(0, amount) %}
+{% for n in range(1, amount) %}
   rotate_lookup[{{n}}] = function ({% for n in range(n) %} A{{n}} {{ ", " if not loop.last else "" }} {% endfor %})
     return {% for n in range(1, n) %} A{{n}}, {% endfor %} A0
   end
 {% endfor %}
 
 local function rotate_n(first, ...)
+  local n = select("#", ...) + 1
   local args = tbl.pack(...)
-  args[#args+1] = first
-  return tbl.unpack(args)
+  args[n] = first
+  return tbl.unpack(args, 1, n)
 end
 
 local function rotate(nargs, ...)
+  if nargs == nil or nargs < 1 then
+    return
+  end
   return (rotate_lookup[nargs] or rotate_n)(...)
 end
 


Test patch:
diff --git a/lua/plenary/test_harness.lua b/lua/plenary/test_harness.lua
index 91aa49f..0dc8354 100644
--- a/lua/plenary/test_harness.lua
+++ b/lua/plenary/test_harness.lua
@@ -42,6 +42,7 @@ end
 
 function harness.test_directory(directory, opts)
   print "Starting..."
+  directory = directory:gsub("\\", "/")
   local minimal = not opts or not opts.init or opts.minimal or opts.minimal_init
 
   opts = vim.tbl_deep_extend("force", {
diff --git a/tests/plenary/functional_spec.lua b/tests/plenary/functional_spec.lua
new file mode 100644
index 0000000..9a34b22
--- /dev/null
+++ b/tests/plenary/functional_spec.lua
@@ -0,0 +1,18 @@
+local f = require "plenary.functional"
+
+describe("functional", function()
+  describe("partial", function()
+    local function args(...)
+      assert.is.equal(4, select("#", ...))
+      return table.concat({ ... }, ",")
+    end
+    it("should bind correct parameters", function()
+      local expected = args(1, 2, 3, 4)
+      assert.is.equal(expected, f.partial(args)(1, 2, 3, 4))
+      assert.is.equal(expected, f.partial(args, 1)(2, 3, 4))
+      assert.is.equal(expected, f.partial(args, 1, 2)(3, 4))
+      assert.is.equal(expected, f.partial(args, 1, 2, 3)(4))
+      assert.is.equal(expected, f.partial(args, 1, 2, 3, 4)())
+    end)
+  end)
+end)
diff --git a/tests/plenary/rotate_spec.lua b/tests/plenary/rotate_spec.lua
index ad5e21a..825228b 100644
--- a/tests/plenary/rotate_spec.lua
+++ b/tests/plenary/rotate_spec.lua
@@ -5,13 +5,26 @@ local eq = function(a, b)
 end
 
 describe("rotate", function()
+  it("should return as many values, as the first argument", function()
+    local args = {}
+    for _ = 0, 20 do
+      local n = select("#", unpack(args))
+      assert.is.equal(n, select("#", rotate(n, unpack(args))))
+      args[#args + 1] = n
+    end
+  end)
+
   it("should rotate varargs", function()
     eq({ rotate(3, 1, 2, 3) }, { 2, 3, 1 })
     eq({ rotate(9, 1, 2, 3, 4, 5, 6, 7, 8, 9) }, { 2, 3, 4, 5, 6, 7, 8, 9, 1 })
   end)
 
+  it("should rotate zero", function()
+    assert.is.equal(0, select("#", rotate(0)))
+  end)
+
   it("should rotate none", function()
-    eq({ rotate() }, {})
+    assert.is.equal(0, select("#", rotate()))
   end)
 
   it("should rotate one", function()