Skip to content

Commit

Permalink
fix: unesacpe fzf's {q}
Browse files Browse the repository at this point in the history
  • Loading branch information
ibhagwan committed Feb 3, 2024
1 parent 173e50c commit 40dbc1a
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 9 deletions.
17 changes: 11 additions & 6 deletions lua/fzf-lua/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions lua/fzf-lua/libuv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
7 changes: 4 additions & 3 deletions lua/fzf-lua/make_entry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions tests/libuv_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit 40dbc1a

Please sign in to comment.