diff options
Diffstat (limited to 'runtime/lua/vim/shared.lua')
-rw-r--r-- | runtime/lua/vim/shared.lua | 132 |
1 files changed, 80 insertions, 52 deletions
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 1c8defc93a..884929e33a 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -59,21 +59,52 @@ end)() --- Splits a string at each instance of a separator. --- ----@see |vim.split()| ----@see |luaref-patterns| ----@see https://www.lua.org/pil/20.2.html ----@see http://lua-users.org/wiki/StringLibraryTutorial ---- ----@param s string String to split ----@param sep string Separator or pattern ----@param plain (boolean|nil) If `true` use `sep` literally (passed to string.find) ----@return fun():string (function) Iterator over the split components -function vim.gsplit(s, sep, plain) - vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } }) +--- Example: +--- <pre>lua +--- for s in vim.gsplit(':aa::b:', ':', {plain=true}) do +--- print(s) +--- end +--- </pre> +--- +--- If you want to also inspect the separator itself (instead of discarding it), use +--- |string.gmatch()|. Example: +--- <pre>lua +--- for word, num in ('foo111bar222'):gmatch('([^0-9]*)(%d*)') do +--- print(('word: %s num: %s'):format(word, num)) +--- end +--- </pre> +--- +--- @see |string.gmatch()| +--- @see |vim.split()| +--- @see |luaref-patterns| +--- @see https://www.lua.org/pil/20.2.html +--- @see http://lua-users.org/wiki/StringLibraryTutorial +--- +--- @param s string String to split +--- @param sep string Separator or pattern +--- @param opts (table|nil) Keyword arguments |kwargs|: +--- - plain: (boolean) Use `sep` literally (as in string.find). +--- - trimempty: (boolean) Discard empty segments at start and end of the sequence. +---@return fun():string|nil (function) Iterator over the split components +function vim.gsplit(s, sep, opts) + local plain + local trimempty = false + if type(opts) == 'boolean' then + plain = opts -- For backwards compatibility. + else + vim.validate({ s = { s, 's' }, sep = { sep, 's' }, opts = { opts, 't', true } }) + opts = opts or {} + plain, trimempty = opts.plain, opts.trimempty + end local start = 1 local done = false + -- For `trimempty`: + local empty_start = true -- Only empty segments seen so far. + local empty_segs = 0 -- Empty segments found between non-empty segments. + local nonemptyseg = nil + local function _pass(i, j, ...) if i then assert(j + 1 > start, 'Infinite loop detected') @@ -87,16 +118,44 @@ function vim.gsplit(s, sep, plain) end return function() - if done or (s == '' and sep == '') then - return - end - if sep == '' then + if trimempty and empty_segs > 0 then + -- trimempty: Pop the collected empty segments. + empty_segs = empty_segs - 1 + return '' + elseif trimempty and nonemptyseg then + local seg = nonemptyseg + nonemptyseg = nil + return seg + elseif done or (s == '' and sep == '') then + return nil + elseif sep == '' then if start == #s then done = true end return _pass(start + 1, start) end - return _pass(s:find(sep, start, plain)) + + local seg = _pass(s:find(sep, start, plain)) + + -- Trim empty segments from start/end. + if trimempty and seg == '' then + while not done and seg == '' do + empty_segs = empty_segs + 1 + seg = _pass(s:find(sep, start, plain)) + end + if done and seg == '' then + return nil + elseif empty_start then + empty_start = false + empty_segs = 0 + return seg + end + nonemptyseg = seg ~= '' and seg or nil + seg = '' + empty_segs = empty_segs - 1 + end + + return seg end end @@ -111,48 +170,17 @@ end --- </pre> --- ---@see |vim.gsplit()| +---@see |string.gmatch()| --- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs (table|nil) Keyword arguments: ---- - plain: (boolean) If `true` use `sep` literally (passed to string.find) ---- - trimempty: (boolean) If `true` remove empty items from the front ---- and back of the list +---@param opts (table|nil) Keyword arguments |kwargs| accepted by |vim.gsplit()| ---@return string[] List of split components -function vim.split(s, sep, kwargs) - local plain - local trimempty = false - if type(kwargs) == 'boolean' then - -- Support old signature for backward compatibility - plain = kwargs - else - vim.validate({ kwargs = { kwargs, 't', true } }) - kwargs = kwargs or {} - plain = kwargs.plain - trimempty = kwargs.trimempty - end - +function vim.split(s, sep, opts) 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 + for c in vim.gsplit(s, sep, opts) do + table.insert(t, c) end - - if trimempty then - for i = #t, 1, -1 do - if t[i] ~= '' then - break - end - table.remove(t, i) - end - end - return t end |