feat: add trimempty optional parameter to vim.split

The `split()` VimL function trims empty items from the returned list by
default, so that, e.g.

  split("\nhello\nworld\n\n", "\n")

returns

  ["hello", "world"]

The Lua implementation of vim.split does not do this. For example,

  vim.split("\nhello\nworld\n\n", "\n")

returns

  {'', 'hello', 'world', '', ''}

Add an optional parameter to the vim.split function that, when true,
trims these empty elements from the front and back of the returned
table. This is only possible for vim.split and not vim.gsplit; because
vim.gsplit is an iterator, there is no way for it to know if the current
item is the last non-empty item.

Note that in order to preserve backward compatibility, the parameter for
the Lua vim.split function is `trimempty`, while the VimL function uses
`keepempty` (i.e. they are opposites). This means there is a disconnect
between these two functions that may surprise users.
This commit is contained in:
Gregory Anders 2021-07-29 14:48:04 -06:00
parent 05d685be52
commit 5fa26e2c2f
3 changed files with 59 additions and 25 deletions

View File

@ -1373,20 +1373,23 @@ pesc({s}) *vim.pesc()*
See also: ~ See also: ~
https://github.com/rxi/lume https://github.com/rxi/lume
split({s}, {sep}, {plain}) *vim.split()* split({s}, {sep}, {plain}, {trimempty}) *vim.split()*
Splits a string at each instance of a separator. Splits a string at each instance of a separator.
Examples: > Examples: >
split(":aa::b:", ":") --> {'','aa','','b',''} split(":aa::b:", ":") --> {'','aa','','b',''}
split("axaby", "ab?") --> {'','x','y'} split("axaby", "ab?") --> {'','x','y'}
split(x*yz*o, "*", true) --> {'x','yz','o'} split("x*yz*o", "*", true) --> {'x','yz','o'}
split("|x|y|z|", "|", true, true) --> {'x', 'y', 'z'}
< <
Parameters: ~ Parameters: ~
{s} String to split {s} String to split
{sep} Separator string or pattern {sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to {plain} If `true` use `sep` literally (passed to
String.find) String.find)
{trimempty} If `true` remove empty items from the front
and back of the list
Return: ~ Return: ~
List-like table of the split components. List-like table of the split components.

View File

@ -98,17 +98,41 @@ end
--- <pre> --- <pre>
--- split(":aa::b:", ":") --> {'','aa','','b',''} --- split(":aa::b:", ":") --> {'','aa','','b',''}
--- split("axaby", "ab?") --> {'','x','y'} --- split("axaby", "ab?") --> {'','x','y'}
--- split(x*yz*o, "*", true) --> {'x','yz','o'} --- split("x*yz*o", "*", true) --> {'x','yz','o'}
--- split("|x|y|z|", "|", true, true) --> {'x', 'y', 'z'}
--- </pre> --- </pre>
-- ---
---@see |vim.gsplit()| ---@see |vim.gsplit()|
--- ---
---@param s String to split ---@param s String to split
---@param sep Separator string or pattern ---@param sep Separator string or pattern
---@param plain If `true` use `sep` literally (passed to String.find) ---@param plain If `true` use `sep` literally (passed to String.find)
---@param trimempty If `true` remove empty items from the front and back of the list
---@returns List-like table of the split components. ---@returns List-like table of the split components.
function vim.split(s,sep,plain) function vim.split(s, sep, plain, trimempty)
local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end -- Only need to validate trimempty since the rest are validated by vim.gsplit
vim.validate{trimempty={trimempty, 'b', true}}
local t = {}
local skip = trimempty
for c in vim.gsplit(s, sep, plain) do
if c ~= "" then
skip = false
end
if not skip then
table.insert(t, c)
end
end
if trimempty then
for i = #t, 1, -1 do
if t[i] ~= "" then
break
end
table.remove(t, i)
end
end
return t return t
end end

View File

@ -237,27 +237,29 @@ describe('lua stdlib', function()
end) end)
it("vim.split", function() it("vim.split", function()
local split = function(str, sep, plain) local split = function(str, sep, plain, trimempty)
return exec_lua('return vim.split(...)', str, sep, plain) return exec_lua('return vim.split(...)', str, sep, plain, trimempty)
end end
local tests = { local tests = {
{ "a,b", ",", false, { 'a', 'b' } }, { "a,b", ",", false, false, { 'a', 'b' } },
{ ":aa::bb:", ":", false, { '', 'aa', '', 'bb', '' } }, { ":aa::bb:", ":", false, false, { '', 'aa', '', 'bb', '' } },
{ "::ee::ff:", ":", false, { '', '', 'ee', '', 'ff', '' } }, { ":aa::bb:", ":", false, true, { 'aa', '', 'bb' } },
{ "ab", ".", false, { '', '', '' } }, { "::ee::ff:", ":", false, false, { '', '', 'ee', '', 'ff', '' } },
{ "a1b2c", "[0-9]", false, { 'a', 'b', 'c' } }, { "::ee::ff:", ":", false, true, { 'ee', '', 'ff' } },
{ "xy", "", false, { 'x', 'y' } }, { "ab", ".", false, false, { '', '', '' } },
{ "here be dragons", " ", false, { "here", "be", "dragons"} }, { "a1b2c", "[0-9]", false, false, { 'a', 'b', 'c' } },
{ "axaby", "ab?", false, { '', 'x', 'y' } }, { "xy", "", false, false, { 'x', 'y' } },
{ "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } }, { "here be dragons", " ", false, false, { "here", "be", "dragons"} },
{ "", "", false, {} }, { "axaby", "ab?", false, false, { '', 'x', 'y' } },
{ "", "a", false, { '' } }, { "f v2v v3v w2w ", "([vw])2%1", false, false, { 'f ', ' v3v ', ' ' } },
{ "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } }, { "", "", false, false, {} },
{ "", "a", false, false, { '' } },
{ "x*yz*oo*l", "*", true, false, { 'x', 'yz', 'oo', 'l' } },
} }
for _, t in ipairs(tests) do for _, t in ipairs(tests) do
eq(t[4], split(t[1], t[2], t[3])) eq(t[5], split(t[1], t[2], t[3], t[4]))
end end
local loops = { local loops = {
@ -288,6 +290,11 @@ describe('lua stdlib', function()
vim/shared.lua:0: in function 'gsplit' vim/shared.lua:0: in function 'gsplit'
vim/shared.lua:0: in function <vim/shared.lua:0>]]), vim/shared.lua:0: in function <vim/shared.lua:0>]]),
pcall_err(split, 'string', 'string', 1)) pcall_err(split, 'string', 'string', 1))
eq(dedent([[
Error executing lua: vim/shared.lua:0: trimempty: expected boolean, got number
stack traceback:
vim/shared.lua:0: in function <vim/shared.lua:0>]]),
pcall_err(split, 'string', 'string', false, 42))
end) end)
it('vim.trim', function() it('vim.trim', function()