Skip to content

Commit

Permalink
Add a cc.strings.split method
Browse files Browse the repository at this point in the history
This is largely copied from metis, with the documentation updated.
  • Loading branch information
SquidDev committed Jul 24, 2024
1 parent 1d45935 commit 63e40cf
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,63 @@ local function ensure_width(line, width)
return line
end

--[[- Split a string into parts, each separated by a deliminator.
For instance, splitting the string `"a b c"` with the deliminator `" "`, would
return a table with three strings: `"a"`, `"b"`, and `"c"`.
By default, the deliminator is given as a [Lua pattern][pattern]. Passing `true`
to the `plain` argument will cause the deliminator to be treated as a litteral
string.
[pattern]: https://www.lua.org/manual/5.3/manual.html#6.4.1
@tparam string str The string to split.
@tparam string deliminator The pattern to split this string on.
@tparam[opt=false] boolean plain Treat the deliminator as a plain string, rather than a pattern.
@tparam[opt] number limit The maximum number of elements in the returned list.
@treturn { string... } The list of split strings.
@usage Split a string into words.
require "cc.strings".split("This is a sentence.", "%s+")
@usage Split a string by "-" into at most 3 elements.
require "cc.strings".split("a-separated-string-of-sorts", "-", true, 3)
@see table.concat To join strings together.
@since 1.112.0
]]
local function split(str, deliminator, plain, limit)
expect(1, str, "string")
expect(2, deliminator, "string")
expect(3, plain, "boolean", "nil")
expect(4, limit, "number", "nil")

local out, out_n, pos = {}, 0, 1
while not limit or out_n < limit - 1 do
local start, finish = str:find(deliminator, pos, plain)
if not start then break end

out_n = out_n + 1
out[out_n] = str:sub(pos, start - 1)

-- Require us to advance by at least one character.
if finish < start then error("separator is empty", 2) end

pos = finish + 1
end

if pos == 1 then return { str } end

out[out_n + 1] = str:sub(pos)
return out
end

return {
wrap = wrap,
ensure_width = ensure_width,
split = split,
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,33 @@ describe("cc.strings", function()
expect(str.ensure_width("test string is long", 15)):eq("test string is ")
end)
end)

describe("split", function()
it("splits with empty segments", function()
expect(str.split("", "%-")):same { "" }
expect(str.split("-", "%-")):same { "", "" }
expect(str.split("---", "%-")):same { "", "", "", "" }
expect(str.split("-a", "%-")):same { "", "a" }
expect(str.split("a-", "%-")):same { "a", "" }
end)

it("cannot split with an empty separator", function()
expect.error(str.split, "abc", ""):eq("separator is empty")
end)

it("splits on patterns", function()
expect(str.split("a.bcd ef", "%W+")):same { "a", "bcd", "ef" }
end)

it("splits on literal strings", function()
expect(str.split("a-bcd-ef", "-", true)):same { "a", "bcd", "ef" }
end)

it("accepts a limit", function()
expect(str.split("foo-bar-baz-qux-quyux", "-", true, 3)):same { "foo", "bar", "baz-qux-quyux" }
expect(str.split("foo-bar-baz", "-", true, 5)):same { "foo", "bar", "baz" }
expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" }
expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" }
end)
end)
end)

0 comments on commit 63e40cf

Please sign in to comment.