From 40dbc1a7d3b5e59959e843eed8780db22bacfaa3 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Mon, 29 Jan 2024 20:12:20 -0500 Subject: [PATCH] fix: unesacpe fzf's {q} --- lua/fzf-lua/core.lua | 17 +++++++++++------ lua/fzf-lua/libuv.lua | 27 +++++++++++++++++++++++++++ lua/fzf-lua/make_entry.lua | 7 ++++--- tests/libuv_spec.lua | 26 ++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index e8e29103..6584363e 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -542,11 +542,15 @@ M.build_fzf_cli = function(opts) table.insert(cli_args, k) if type(v) == "string" or type(v) == "number" then v = tostring(v) -- convert number type to string - if libuv.is_escaped(v) then - utils.warn(string.format("`fzf_opts` are automatically shellescaped." - .. " Please remove surrounding quotes from %s=%s", k, v)) + if k == "--query" then + table.insert(cli_args, libuv.shellescape(v)) + else + if libuv.is_escaped(v) then + utils.warn(string.format("`fzf_opts` are automatically shellescaped." + .. " Please remove surrounding quotes from %s=%s", k, v)) + end + table.insert(cli_args, libuv.is_escaped(v) and v or libuv.shellescape(v)) end - table.insert(cli_args, libuv.is_escaped(v) and v or libuv.shellescape(v)) end end end @@ -1009,8 +1013,9 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_expression, opts) -- use `true` as $FZF_DEFAULT_COMMAND instead (#510) opts.__fzf_init_cmd = utils._if_win("break", "true") if opts.exec_empty_query or (opts.query and #opts.query > 0) then - opts.__fzf_init_cmd = initial_command:gsub(fzf_field_expression, - libuv.shellescape(opts.query)) + -- gsub doesn't like single % on rhs + local escaped_q = libuv.shellescape(libuv.escape_q(opts.query)):gsub("%%", "%%%%") + opts.__fzf_init_cmd = initial_command:gsub(fzf_field_expression, escaped_q) end opts.fzf_opts["--disabled"] = true opts.fzf_opts["--query"] = opts.query diff --git a/lua/fzf-lua/libuv.lua b/lua/fzf-lua/libuv.lua index 748ac054..249d0492 100644 --- a/lua/fzf-lua/libuv.lua +++ b/lua/fzf-lua/libuv.lua @@ -661,6 +661,33 @@ M.shellescape = function(s, win_style) end end +-- Windows fzf oddities, fzf's {q} will send escaped blackslahes, +-- but only when the backslash prefixes another character which +-- isn't a backslash +M.unescape_q = function(s) + if not is_windows then return s end + local ret = s:gsub("\\+[^\\]", function(x) + local bslash_num = #x:match([[\+]]) + return string.rep([[\]], + bslash_num == 1 and bslash_num or bslash_num / 2) .. x:sub(-1) + end) + return ret +end + +-- with live_grep, we use a modified "reload" command as our +-- FZF_DEFAULT_COMMAND and due to the above oddity with fzf +-- doing weird extra escaping with {q}, we use this to simulate +-- {q} being sent via the reload action as the initial command +-- TODO: better solution for these stupid hacks (upstream issues?) +M.escape_q = function(s) + if not is_windows then return s end + local ret = s:gsub("\\+[^\\]", function(x) + local bslash_num = #x:match([[\+]]) + return string.rep([[\]], bslash_num * 2) .. x:sub(-1) + end) + return ret +end + ---@param opts string ---@param fn_transform string? ---@param fn_preprocess string? diff --git a/lua/fzf-lua/make_entry.lua b/lua/fzf-lua/make_entry.lua index e8a58fe9..8c1be3e4 100644 --- a/lua/fzf-lua/make_entry.lua +++ b/lua/fzf-lua/make_entry.lua @@ -337,12 +337,13 @@ M.preprocess = function(opts) -- If no index was supplied use the last argument local idx = tonumber(i) and tonumber(i) + 6 or #vim.v.argv local arg = vim.v.argv[idx] + if debug == "v" or debug == "verbose" then + io.stdout:write(("[DEBUGV]: raw_argv(%d) = %s\n"):format(idx, arg)) + end if utils.__IS_WINDOWS then - -- fzf's {q} will send escaped blackslahes, unescape - arg = arg:gsub([[\\]], [[\]]) + arg = libuv.unescape_q(arg) end if debug == "v" or debug == "verbose" then - io.stdout:write(("[DEBUGV]: raw_argv(%d) = %s\n"):format(idx, arg)) io.stdout:write(("[DEBUGV]: esc_argv(%d) = %s\n"):format(idx, libuv.shellescape(arg))) end return arg diff --git a/tests/libuv_spec.lua b/tests/libuv_spec.lua index 53d60715..46daed19 100644 --- a/tests/libuv_spec.lua +++ b/tests/libuv_spec.lua @@ -89,4 +89,30 @@ describe("Testing libuv module", function() assert.are.same(libuv.shellescape([[foo\\^^"bar]], 2), [[^"foo\\^^^^\^"bar^"]]) assert.are.same(libuv.shellescape([[foo\\\^^^"]], 2), [[^"foo\\\^^^^^^\^"^"]]) end) + + it("escape {q} (win)", function() + assert.are.same(libuv.escape_q([[]]), [[]]) + assert.are.same(libuv.escape_q([[\]]), [[\]]) + assert.are.same(libuv.escape_q([[\\]]), [[\\]]) + assert.are.same(libuv.escape_q([[foo]]), [[foo]]) + assert.are.same(libuv.escape_q([[\foo]]), [[\\foo]]) + assert.are.same(libuv.escape_q([[\\foo]]), [[\\\\foo]]) + assert.are.same(libuv.escape_q([[\\\foo]]), [[\\\\\\foo]]) + assert.are.same(libuv.escape_q([[\\\\foo]]), [[\\\\\\\\foo]]) + assert.are.same(libuv.escape_q([[foo\]]), [[foo\]]) + assert.are.same(libuv.escape_q([[foo\\]]), [[foo\\]]) + end) + + it("unescape {q} (win)", function() + assert.are.same(libuv.unescape_q([[]]), [[]]) + assert.are.same(libuv.unescape_q([[\]]), [[\]]) + assert.are.same(libuv.unescape_q([[\\]]), [[\\]]) + assert.are.same(libuv.unescape_q([[foo]]), [[foo]]) + assert.are.same(libuv.unescape_q([[\foo]]), [[\foo]]) + assert.are.same(libuv.unescape_q([[\\foo]]), [[\foo]]) + assert.are.same(libuv.unescape_q([[\\\foo]]), [[\foo]]) + assert.are.same(libuv.unescape_q([[\\\\foo]]), [[\\foo]]) + assert.are.same(libuv.unescape_q([[foo\]]), [[foo\]]) + assert.are.same(libuv.unescape_q([[foo\\]]), [[foo\\]]) + end) end)