Skip to content

Commit

Permalink
fixup! fix: tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mfussenegger committed Aug 9, 2024
1 parent 0a22899 commit 1cbb41b
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 61 deletions.
16 changes: 16 additions & 0 deletions doc/dap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,22 @@ pick_file({opts}) *dap.utils.pick_file*
})
<

splitstr({str}) *dap.utils.splitstr*

Split an argument string on whitespace into a list, except if the
whitespace is contained within single or double quotes.

Parameters:
{str} The string to split

>lua
require("dap.utils").splitstr("Hello world")
-- result: {"Hello", "world"}

require("dap.utils").splitstr("Keeps 'a quoted string' intact")
-- result: {"Keeps", "a quoted string", "intact"}
<

==============================================================================
DAP Session *dap-session*

Expand Down
82 changes: 30 additions & 52 deletions lua/dap/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -302,64 +302,42 @@ function M.pick_file(opts)
end


--- Split args string into a table of arguments.
--- Works with single and double quoted strings. Escaped quotes are supported.
--- Split an argument string on whitespace characters into a list,
--- except if the whitespace is contained within single or double quotes.
---
--- <pre>
--- require("dap.utils").split_args("runserver --debug true --reason 'I\'m dumb'")
--- {runserver, --debug, true, I'm dumb}
--- </pre>
--- Examples:
---
--- <pre>
--- require("dap.utils").split_args('--comment "I\'m \"this\" close"')
--- {--comment, I'm "this" close}
--- </pre>
--- ```lua
--- require("dap.utils").splitstr("hello world")
--- {"hello", "world"}
--- ```
---
--- ```lua
--- require("dap.utils").splitstr('a "quoted string" is preserved')
--- {"a", "quoted string", "is, "preserved"}
--- ```
---
--- Inspired by http://lua-users.org/wiki/LpegRecipes
--- @param args string
--- @return table
function M.split_args(args)
--- Requires nvim 0.10+
---
--- @param str string
--- @return string[]
function M.splitstr(str)
local lpeg = vim.lpeg

local P, S, C, Cc, Ct = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Ct

--- @param id string
--- @param patt vim.lpeg.Capture
--- @return vim.lpeg.Pattern
local function token(id, patt)
return Ct(Cc(id) * C(patt))
end

local single_quoted = P("'") * ((1 - S("'\r\n\f\\")) + (P("\\") * 1)) ^ 0 * "'"
local double_quoted = P('"') * ((1 - S('"\r\n\f\\')) + (P("\\") * 1)) ^ 0 * '"'

local whitespace = token("whitespace", S("\r\n\f\t ") ^ 1)
local word = token("word", (1 - S("' \r\n\f\t\"")) ^ 1)
local string = token("string", single_quoted + double_quoted)

local pattern = Ct((string + whitespace + word) ^ 0)

local t = {}
local tokens = lpeg.match(pattern, args)

-- somehow, this did not work out
if tokens == nil or type(tokens) == "integer" then
return t
end

for _, tok in ipairs(tokens) do
if tok[1] ~= "whitespace" then
if tok[1] == "string" then
-- cut off quotes and replace escaped quotes
local v, _ = tok[2]:sub(2, -2):gsub("\\(['\"])", "%1")
table.insert(t, v)
else
table.insert(t, tok[2])
end
end
local P, S, C = lpeg.P, lpeg.S, lpeg.C

---@param quotestr string
---@return vim.lpeg.Pattern
local function qtext(quotestr)
local quote = P(quotestr)
local escaped_quote = P('\\') * quote
return quote * C(((1 - P(quote)) + escaped_quote) ^ 0) * quote
end

return t
local space = S(" \t\n\r") ^ 1
local unquoted = C((1 - space) ^ 0)
local element = qtext('"') + qtext("'") + unquoted
local p = lpeg.Ct(element * (space * element) ^ 0)
return lpeg.match(p, str)
end


Expand Down
37 changes: 28 additions & 9 deletions spec/utils_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,39 @@ describe('utils.fmt_error', function ()
end)
end)

describe('utils.split_args', function ()
it('works with standard string', function ()
assert.are.same({'this', 'is', 'a', 'standard', 'string'}, require('dap.utils').split_args('this is a standard string'))
describe('utils.splitstr', function ()
if vim.fn.has("nvim-0.10") == 0 then
return
end
it('works with plain string', function ()
assert.are.same({"hello", "world"}, utils.splitstr("hello world"))
end)

it('with "double quoted" string', function ()
assert.are.same({'with', 'double quoted', 'string'}, require('dap.utils').split_args('with "double quoted" string'))
it('works extra whitespace', function ()
assert.are.same({"hello", "world"}, utils.splitstr('hello world'))
end)

it("with 'single quoted' string", function ()
assert.are.same({'with', 'single quoted', 'string'}, require('dap.utils').split_args("with 'single quoted' string"))
it('empty quoted', function ()
assert.are.same({"hello", "", "world"}, utils.splitstr('hello "" world'))
end)

it('"double \"escaped\" quoted" string', function ()
assert.are.same({'double "escaped" quoted', 'string'}, require('dap.utils').split_args('"double \"escaped\" quoted" string'))
it('with double quoted string', function ()
assert.are.same({'with', 'double quoted', 'string'}, utils.splitstr('with "double quoted" string'))
end)

it("with single quoted string", function ()
assert.are.same({'with', 'single quoted', 'string'}, utils.splitstr("with 'single quoted' string"))
end)

it("with unbalanced quote", function ()
assert.are.same({"with", "\"single", "quoted", "string"}, utils.splitstr("with \"single quoted string"))
end)

it("with unbalanced single quoted string", function ()
assert.are.same({"with", "'single", "quoted", "string"}, utils.splitstr("with 'single quoted string"))
end)

it('escaped quote', function ()
assert.are.same({'foo', '"bar'}, utils.splitstr('foo \"bar'))
end)
end)

0 comments on commit 1cbb41b

Please sign in to comment.