From 1cbb41b340d12d5f57be4056a00dab15aaf65366 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Fri, 9 Aug 2024 14:01:43 +0200 Subject: [PATCH] fixup! fix: tests --- doc/dap.txt | 16 +++++++++ lua/dap/utils.lua | 82 +++++++++++++++++---------------------------- spec/utils_spec.lua | 37 +++++++++++++++----- 3 files changed, 74 insertions(+), 61 deletions(-) diff --git a/doc/dap.txt b/doc/dap.txt index 63b4617f..30977ccd 100644 --- a/doc/dap.txt +++ b/doc/dap.txt @@ -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* diff --git a/lua/dap/utils.lua b/lua/dap/utils.lua index 16b07fe2..ce06b8b1 100644 --- a/lua/dap/utils.lua +++ b/lua/dap/utils.lua @@ -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. --- ----
---- require("dap.utils").split_args("runserver --debug true --reason 'I\'m dumb'")
---- {runserver, --debug, true, I'm dumb}
---- 
+--- Examples: --- ----
---- require("dap.utils").split_args('--comment "I\'m \"this\" close"')
---- {--comment, I'm "this" close}
---- 
+--- ```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 diff --git a/spec/utils_spec.lua b/spec/utils_spec.lua index 37313ed8..ef1b9db8 100644 --- a/spec/utils_spec.lua +++ b/spec/utils_spec.lua @@ -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)