From 173e50c7ec5158c72d2c9cdef3720d205d1696a8 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Mon, 29 Jan 2024 17:13:43 -0500 Subject: [PATCH] chore: normalize shell escaping of `fzf_opts` --- README.md | 10 +-- doc/fzf-lua.txt | 10 +-- lua/fzf-lua/config.lua | 7 ++ lua/fzf-lua/core.lua | 57 ++++++-------- lua/fzf-lua/defaults.lua | 118 ++++++++++++++-------------- lua/fzf-lua/libuv.lua | 11 +++ lua/fzf-lua/previewer/fzf.lua | 5 +- lua/fzf-lua/providers/dap.lua | 4 +- lua/fzf-lua/providers/module.lua | 13 ++- lua/fzf-lua/providers/nvim.lua | 61 +++++++------- lua/fzf-lua/providers/tmux.lua | 3 +- lua/fzf-lua/providers/ui_select.lua | 2 +- tests/libuv_spec.lua | 14 ++++ 13 files changed, 167 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index a4631e6ab..e1ce459db 100644 --- a/README.md +++ b/README.md @@ -639,9 +639,9 @@ require'fzf-lua'.setup { fzf_opts = { -- options are sent as `=` -- set to `false` to remove a flag - -- set to '' for a non-value flag + -- set to `true` for a no-value flag -- for raw args use `fzf_args` instead - ["--ansi"] = "", + ["--ansi"] = true, ["--info"] = "inline", ["--height"] = "100%", ["--layout"] = "reverse", @@ -983,7 +983,7 @@ require'fzf-lua'.setup { }, fzf_opts = { -- hide tabnr - ['--delimiter'] = "'[\\):]'", + ["--delimiter"] = "[\\):]", ["--with-nth"] = '2..', }, }, @@ -996,7 +996,7 @@ require'fzf-lua'.setup { fzf_opts = { -- do not include bufnr in fuzzy matching -- tiebreak by line no. - ['--delimiter'] = "'[\\]:]'", + ["--delimiter"] = "[\\]:]", ["--nth"] = '2..', ["--tiebreak"] = 'index', ["--tabstop"] = "1", @@ -1016,7 +1016,7 @@ require'fzf-lua'.setup { -- start = "cursor" -- start display from cursor? fzf_opts = { -- hide filename, tiebreak by line no. - ["--delimiter"] = "'[:]'", + ["--delimiter"] = "[:]", ["--with-nth"] = '2..', ["--tiebreak"] = 'index', ["--tabstop"] = "1", diff --git a/doc/fzf-lua.txt b/doc/fzf-lua.txt index 04746f1b9..9f557f040 100644 --- a/doc/fzf-lua.txt +++ b/doc/fzf-lua.txt @@ -732,9 +732,9 @@ open an issue and I'll be more than happy to help.** fzf_opts = { -- options are sent as `=` -- set to `false` to remove a flag - -- set to '' for a non-value flag + -- set to `true` for a no-value flag -- for raw args use `fzf_args` instead - ["--ansi"] = "", + ["--ansi"] = true, ["--info"] = "inline", ["--height"] = "100%", ["--layout"] = "reverse", @@ -1076,7 +1076,7 @@ open an issue and I'll be more than happy to help.** }, fzf_opts = { -- hide tabnr - ['--delimiter'] = "'[\\):]'", + ["--delimiter"] = "[\\):]", ["--with-nth"] = '2..', }, }, @@ -1089,7 +1089,7 @@ open an issue and I'll be more than happy to help.** fzf_opts = { -- do not include bufnr in fuzzy matching -- tiebreak by line no. - ['--delimiter'] = "'[\\]:]'", + ["--delimiter"] = "[\\]:]", ["--nth"] = '2..', ["--tiebreak"] = 'index', ["--tabstop"] = "1", @@ -1109,7 +1109,7 @@ open an issue and I'll be more than happy to help.** -- start = "cursor" -- start display from cursor? fzf_opts = { -- hide filename, tiebreak by line no. - ["--delimiter"] = "'[:]'", + ["--delimiter"] = "[:]", ["--with-nth"] = '2..', ["--tiebreak"] = 'index', ["--tabstop"] = "1", diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 774959c10..95ee1bfa8 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -274,6 +274,13 @@ function M.normalize_opts(opts, globals, __resume_key) type(M.globals[k]) == "table" and utils.tbl_deep_clone(M.globals[k]) or {}) end + -- backward compat: no-value flags should be set to `true`, in the past these + -- would be set to an empty string which would now translate into a shell escaped + -- string as we automatically shell escape all fzf_opts + for k, v in pairs(opts.fzf_opts) do + if v == "" then opts.fzf_opts[k] = true end + end + -- prioritize fzf-tmux split pane flags over the -- popup flag `-p` from fzf-lua defaults (#865) if type(opts.fzf_tmux_opts) == "table" then diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index 53fa849d2..e8e29103d 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -266,7 +266,7 @@ M.fzf = function(contents, opts) -- this provides a solution for saving the query -- when the user pressed a valid bind but not when -- aborting with or , see next comment - opts.fzf_opts["--print-query"] = "" + opts.fzf_opts["--print-query"] = true -- setup dummy callbacks for the default fzf 'abort' keybinds -- this way the query also gets saved when we do not 'accept' opts.actions = opts.actions or {} @@ -472,7 +472,7 @@ M.create_fzf_binds = function(binds) for key, action in pairs(dedup) do table.insert(tbl, string.format("%s:%s", key, action)) end - return libuv.shellescape(table.concat(tbl, ",")) + return table.concat(tbl, ",") end ---@param opts table @@ -488,18 +488,11 @@ M.build_fzf_cli = function(opts) }) do opts[o] = opts[o] or config.globals[o] end - -- preview and query have special handling: - -- 'opts.' is prioritized over 'fzf_opts[--name]' - -- 'opts.' is automatically shellescaped - for _, o in ipairs({ "query", "preview" }) do - local flag = string.format("--%s", o) - if opts[o] ~= nil then - -- opt can be 'false' (disabled), don't shellescape in this case - if type(opts[o]) == "string" then - opts.fzf_opts[flag] = libuv.shellescape(opts[o]) - else - opts.fzf_opts[flag] = opts[o] - end + -- below options can be specified directly in opts and will be + -- prioritized: opts. is prioritized over fzf_opts["--name"] + for _, flag in ipairs({ "query", "prompt", "header", "preview" }) do + if opts[flag] ~= nil then + opts.fzf_opts["--" .. flag] = opts[flag] end end opts.fzf_opts["--bind"] = M.create_fzf_binds(opts.keymap.fzf) @@ -512,23 +505,19 @@ M.build_fzf_cli = function(opts) opts.fzf_opts["--preview-window"] = opts.fzf_opts["--preview-window"] .. ":" .. opts.preview_offset end - -- shell escape the prompt - opts.fzf_opts["--prompt"] = (opts.prompt or opts.fzf_opts["--prompt"]) and - libuv.shellescape(opts.prompt or opts.fzf_opts["--prompt"]) if opts._is_skim then -- skim (rust version of fzf) doesn't support the '--info=' flag local info = opts.fzf_opts["--info"] opts.fzf_opts["--info"] = nil if info == "inline" then -- inline for skim is defined as: - opts.fzf_opts["--inline-info"] = "" + opts.fzf_opts["--inline-info"] = true end -- skim doesn't accept border args - local border = opts.fzf_opts["--border"] - if border == "none" then + if opts.fzf_opts["--border"] == "none" then opts.fzf_opts["--border"] = nil else - opts.fzf_opts["--border"] = "" + opts.fzf_opts["--border"] = true end end -- build the cli args @@ -543,17 +532,21 @@ M.build_fzf_cli = function(opts) end end for k, v in pairs(opts.fzf_opts) do - if type(v) == "string" or type(v) == "number" then - -- convert number type to string - v = tostring(v) - if utils.__IS_WINDOWS and v:match([[^'.*'$]]) then + -- flag can be set to `false` to negate a default + if v then + if utils.__IS_WINDOWS and type(v) == "string" and v:match([[^'.*'$]]) then -- replace single quote shellescape -- TODO: replace all so we never get here v = [["]] .. v:sub(2, #v - 1) .. [["]] end table.insert(cli_args, k) - if #v > 0 then - table.insert(cli_args, v) + 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)) + end + table.insert(cli_args, libuv.is_escaped(v) and v or libuv.shellescape(v)) end end end @@ -824,7 +817,7 @@ M.set_header = function(opts, hdr_tbl) end end if hdr_str and #hdr_str > 0 then - opts.fzf_opts["--header"] = libuv.shellescape(hdr_str) + opts.fzf_opts["--header"] = hdr_str end return opts end @@ -991,7 +984,7 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_expression, opts) opts.prompt = opts.__prompt or opts.prompt or opts.fzf_opts["--prompt"] if opts.prompt then opts.fzf_opts["--prompt"] = opts.prompt:match("[^%*]+") - opts.fzf_opts["--cmd-prompt"] = libuv.shellescape(opts.prompt) + opts.fzf_opts["--cmd-prompt"] = opts.prompt -- save original prompt and reset the current one since -- we're using the '--cmd-prompt' as the "main" prompt -- required for resume to have the asterisk prompt prefix @@ -1000,7 +993,7 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_expression, opts) end -- since we surrounded the skim placeholder with quotes -- we need to escape them in the initial query - opts.fzf_opts["--cmd-query"] = libuv.shellescape(utils.sk_escape(opts.query)) + opts.fzf_opts["--cmd-query"] = utils.sk_escape(opts.query) -- '--query' was set by 'resume()', skim has the option to switch back and -- forth between interactive command and fuzzy matching (using 'ctrl-q') -- setting both '--query' and '--cmd-query' will use to fuzzy match @@ -1019,8 +1012,8 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_expression, opts) opts.__fzf_init_cmd = initial_command:gsub(fzf_field_expression, libuv.shellescape(opts.query)) end - opts.fzf_opts["--disabled"] = "" - opts.fzf_opts["--query"] = libuv.shellescape(opts.query) + opts.fzf_opts["--disabled"] = true + opts.fzf_opts["--query"] = opts.query -- OR with true to avoid fzf's "Command failed:" message if opts.silent_fail ~= false then reload_command = reload_command .. " || " .. utils._if_win("break", "true") diff --git a/lua/fzf-lua/defaults.lua b/lua/fzf-lua/defaults.lua index 140b3694f..5a7a4c1a3 100644 --- a/lua/fzf-lua/defaults.lua +++ b/lua/fzf-lua/defaults.lua @@ -114,7 +114,7 @@ M.defaults = { }, fzf_bin = nil, fzf_opts = { - ["--ansi"] = "", + ["--ansi"] = true, ["--info"] = "inline", ["--height"] = "100%", ["--layout"] = "reverse", @@ -207,7 +207,7 @@ M.defaults.files = { cwd_prompt = true, cwd_prompt_shorten_len = 32, cwd_prompt_shorten_val = 1, - fzf_opts = { ["--info"] = "default", ["--multi"] = "" }, + fzf_opts = { ["--info"] = "default", ["--multi"] = true }, git_status_cmd = { "git", "-c", "color.status=false", "--no-optional-locks", "status", "--porcelain=v1" }, find_opts = [[-type f -not -path '*/\.git/*' -printf '%P\n']], @@ -230,7 +230,7 @@ M.defaults.git = { file_icons = true and M._has_devicons, color_icons = true, git_icons = true, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, winopts = { preview = { winopts = { cursorline = false } } }, }, @@ -244,7 +244,7 @@ M.defaults.git = { file_icons = true and M._has_devicons, color_icons = true, git_icons = true, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, actions = { ["right"] = { fn = actions.git_unstage, reload = true }, @@ -265,7 +265,7 @@ M.defaults.git = { ["default"] = actions.git_checkout, ["ctrl-y"] = { fn = actions.git_yank_commit, exec_silent = true }, }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, bcommits = { prompt = "BCommits> ", @@ -280,13 +280,13 @@ M.defaults.git = { ["ctrl-t"] = actions.git_buf_tabedit, ["ctrl-y"] = { fn = actions.git_yank_commit, exec_silent = true }, }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, branches = { prompt = "Branches> ", cmd = "git branch --all --color", preview = "git log --graph --pretty=oneline --abbrev-commit --color {1}", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.git_switch, }, @@ -299,7 +299,7 @@ M.defaults.git = { .. [[ %(subject) %(color:blue)%(taggername)%(color:reset)" refs/tags]], preview = [[git log --graph --color --pretty=format:"%C(yellow)%h%Creset ]] .. [[%Cgreen(%><(12)%cr%><|(12))%Creset %s %C(blue)<%an>%Creset" {1}]], - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.git_checkout }, }, stash = { @@ -314,8 +314,8 @@ M.defaults.git = { -- TODO: multiselect requires more work as dropping -- a stash changes the stash index, causing an error -- when the next stash is attempted - ["--no-multi"] = "", - ["--delimiter"] = "'[:]'", + ["--no-multi"] = true, + ["--delimiter"] = "[:]", }, }, icons = { @@ -338,7 +338,7 @@ M.defaults.grep = { file_icons = true and M._has_devicons, color_icons = true, git_icons = true, - fzf_opts = { ["--info"] = "default", ["--multi"] = "" }, + fzf_opts = { ["--info"] = "default", ["--multi"] = true }, grep_opts = utils.is_darwin() and "--binary-files=without-match --line-number --recursive --color=always " .. "--extended-regexp -e" @@ -360,7 +360,7 @@ M.defaults.args = { file_icons = true and M._has_devicons, color_icons = true, git_icons = true, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, actions = { ["ctrl-x"] = { fn = actions.arg_del, reload = true } }, } @@ -372,7 +372,7 @@ M.defaults.oldfiles = { color_icons = true, git_icons = false, stat_file = true, - fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = true }, _actions = function() return M.globals.actions.files end, } @@ -383,7 +383,7 @@ M.defaults.quickfix = { file_icons = true and M._has_devicons, color_icons = true, git_icons = false, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, } @@ -391,7 +391,7 @@ M.defaults.quickfix_stack = { prompt = "Quickfix Stack> ", marker = ">", previewer = { _ctor = previewers.builtin.quickfix, }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.set_qflist, }, } @@ -402,7 +402,7 @@ M.defaults.loclist = { file_icons = true and M._has_devicons, color_icons = true, git_icons = false, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, } @@ -410,7 +410,7 @@ M.defaults.loclist_stack = { prompt = "Locations Stack> ", marker = ">", previewer = { _ctor = previewers.builtin.quickfix, }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.set_qflist, }, } @@ -425,7 +425,7 @@ M.defaults.buffers = { no_action_set_cursor = true, cwd_only = false, cwd = nil, - fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = true }, _actions = function() return M.globals.actions.buffers end, actions = { ["ctrl-x"] = { fn = actions.buf_del, reload = true } }, _cached_hls = { "buf_nr", "buf_flag_cur", "buf_flag_alt" }, @@ -444,8 +444,8 @@ M.defaults.tabs = { ["ctrl-x"] = { fn = actions.buf_del, reload = true }, }, fzf_opts = { - ["--multi"] = "", - ["--delimiter"] = "'[\\):]'", + ["--multi"] = true, + ["--delimiter"] = "[\\):]", ["--with-nth"] = "3..", }, _cached_hls = { "buf_nr", "buf_flag_cur", "buf_flag_alt", "tab_title", "tab_marker" }, @@ -460,8 +460,8 @@ M.defaults.lines = { show_unlisted = false, no_term_buffers = true, fzf_opts = { - ["--no-multi"] = "", - ["--delimiter"] = "'[\\]:]'", + ["--no-multi"] = true, + ["--delimiter"] = "[\\]:]", ["--nth"] = "2..", ["--tiebreak"] = "index", ["--tabstop"] = "1", @@ -484,8 +484,8 @@ M.defaults.blines = { show_unlisted = true, no_term_buffers = false, fzf_opts = { - ["--no-multi"] = "", - ["--delimiter"] = "'[:]'", + ["--no-multi"] = true, + ["--delimiter"] = "[:]", ["--with-nth"] = "2..", ["--tiebreak"] = "index", ["--tabstop"] = "1", @@ -512,8 +512,8 @@ M.defaults.tags = { git_icons = false, color_icons = true, fzf_opts = { - ["--no-multi"] = "", - ["--delimiter"] = string.format("'[:%s]'", utils.nbsp), + ["--no-multi"] = true, + ["--delimiter"] = string.format("[:%s]", utils.nbsp), ["--tiebreak"] = "begin", ["--info"] = "default", }, @@ -532,8 +532,8 @@ M.defaults.btags = { git_icons = false, color_icons = true, fzf_opts = { - ["--no-multi"] = "", - ["--delimiter"] = string.format("'[:%s]'", utils.nbsp), + ["--no-multi"] = true, + ["--delimiter"] = string.format("[:%s]", utils.nbsp), ["--with-nth"] = "1,-1", ["--tiebreak"] = "begin", ["--info"] = "default", @@ -546,13 +546,13 @@ M.defaults.colorschemes = { prompt = "Colorschemes> ", live_preview = true, winopts = { height = 0.55, width = 0.50 }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.colorscheme }, } M.defaults.highlights = { prompt = "Highlights> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, previewer = { _ctor = previewers.builtin.highlights, }, } @@ -565,8 +565,8 @@ M.defaults.helptags = { ["ctrl-t"] = actions.help_tab, }, fzf_opts = { - ["--no-multi"] = "", - ["--delimiter"] = "'[ ]'", + ["--no-multi"] = true, + ["--delimiter"] = "[ ]", ["--with-nth"] = "..-2", }, previewer = { @@ -583,7 +583,7 @@ M.defaults.manpages = { ["ctrl-v"] = actions.man_vert, ["ctrl-t"] = actions.man_tab, }, - fzf_opts = { ["--tiebreak"] = "begin", ["--no-multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "begin", ["--no-multi"] = true }, previewer = "man", } @@ -595,7 +595,7 @@ M.defaults.lsp = { git_icons = false, cwd_only = false, async_or_timeout = 5000, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, } @@ -641,10 +641,10 @@ M.defaults.lsp.symbols = { exec_empty_query = true, -- new formatting options with symbol name at the start fzf_opts = { - ["--delimiter"] = string.format("'[:%s]'", utils.nbsp), + ["--delimiter"] = string.format("[:%s]", utils.nbsp), ["--tiebreak"] = "begin", ["--info"] = "default", - ["--no-multi"] = "", + ["--no-multi"] = true, }, line_field_index = "{-2}", -- line field index field_index_expr = "{}", -- entry field index @@ -706,7 +706,7 @@ M.defaults.lsp.code_actions = { async_or_timeout = 5000, previewer = "codeaction", -- previewer = "codeaction_native", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, } M.defaults.diagnostics = { @@ -718,7 +718,7 @@ M.defaults.diagnostics = { diag_icons = true, diag_source = false, multiline = true, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, _actions = function() return M.globals.actions.files end, -- signs = { -- ["Error"] = { text = "e", texthl = "DiagnosticError" }, @@ -734,7 +734,7 @@ M.defaults.builtin = { height = 0.65, width = 0.50, }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.run_builtin }, } @@ -742,16 +742,16 @@ M.defaults.profiles = { previewer = M._default_previewer_fn, prompt = "FzfLua profiles> ", fzf_opts = { - ["--delimiter"] = "'[:]'", + ["--delimiter"] = "[:]", ["--with-nth"] = "-1..", - ["--no-multi"] = "", + ["--no-multi"] = true, }, actions = { ["default"] = actions.apply_profile }, } M.defaults.marks = { prompt = "Marks> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.goto_mark }, previewer = { _ctor = previewers.builtin.marks }, } @@ -765,7 +765,7 @@ M.defaults.changes = { M.defaults.jumps = { prompt = "Jumps> ", cmd = "jumps", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.goto_jump }, previewer = { _ctor = previewers.builtin.jumps }, } @@ -775,7 +775,7 @@ M.defaults.tagstack = { file_icons = true and M._has_devicons, color_icons = true, git_icons = true, - fzf_opts = { ["--multi"] = "" }, + fzf_opts = { ["--multi"] = true }, previewer = M._default_previewer_fn, _actions = function() return M.globals.actions.files end, } @@ -792,15 +792,15 @@ M.defaults.autocmds = { previewer = { _ctor = previewers.builtin.autocmds }, _actions = function() return M.globals.actions.files end, fzf_opts = { - ["--delimiter"] = "'[:]'", + ["--delimiter"] = "[:]", ["--with-nth"] = "3..", - ["--no-multi"] = "", + ["--no-multi"] = true, }, } M.defaults.command_history = { prompt = "Command History> ", - fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = true }, actions = { ["default"] = actions.ex_run_cr, ["ctrl-e"] = actions.ex_run, @@ -809,7 +809,7 @@ M.defaults.command_history = { M.defaults.search_history = { prompt = "Search History> ", - fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = true }, actions = { ["default"] = actions.search_cr, ["ctrl-e"] = actions.search, @@ -820,14 +820,14 @@ M.defaults.registers = { prompt = "Registers> ", ignore_empty = true, actions = { ["default"] = actions.paste_register }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, } M.defaults.keymaps = { prompt = "Keymaps> ", previewer = { _ctor = previewers.builtin.keymaps }, winopts = { preview = { layout = "vertical" } }, - fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = "" }, + fzf_opts = { ["--tiebreak"] = "index", ["--no-multi"] = true }, ignore_patterns = { "^", "^" }, actions = { ["default"] = actions.keymap_apply, @@ -871,26 +871,26 @@ M.defaults.tmux = { cmd = "tmux list-buffers", register = [["]], actions = { ["default"] = actions.tmux_buf_set_reg }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true, ["--delimiter"] = "[:]" } }, } M.defaults.dap = { commands = { prompt = "DAP Commands> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, configurations = { prompt = "DAP Configurations> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, variables = { prompt = "DAP Variables> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, frames = { prompt = "DAP Frames> ", - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, }, breakpoints = { prompt = "DAP Breakpoints> ", @@ -900,9 +900,9 @@ M.defaults.dap = { previewer = M._default_previewer_fn, _actions = function() return M.globals.actions.files end, fzf_opts = { - ["--delimiter"] = "'[\\]:]'", + ["--delimiter"] = "[\\]:]", ["--with-nth"] = "2..", - ["--no-multi"] = "", + ["--no-multi"] = true, }, }, } @@ -912,7 +912,7 @@ M.defaults.complete_path = { file_icons = false, git_icons = false, color_icons = true, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, actions = { ["default"] = actions.complete }, } @@ -926,7 +926,7 @@ M.defaults.complete_file = { actions = { ["default"] = actions.complete }, previewer = M._default_previewer_fn, winopts = { preview = { hidden = "hidden" } }, - fzf_opts = { ["--no-multi"] = "" }, + fzf_opts = { ["--no-multi"] = true }, } M.defaults.complete_line = { complete = true } diff --git a/lua/fzf-lua/libuv.lua b/lua/fzf-lua/libuv.lua index a4997eb24..748ac054b 100644 --- a/lua/fzf-lua/libuv.lua +++ b/lua/fzf-lua/libuv.lua @@ -508,6 +508,17 @@ M.spawn_stdio = function(opts, fn_transform_str, fn_preprocess_str) end) end + +M.is_escaped = function(s, force_win) + local m + if is_windows or force_win then + m = s:match([[^".*"$]]) or s:match([[^%^".*%^"$]]) + else + m = s:match([[^'.*'$]]) or s:match([[^".*"$]]) + end + return m ~= nil +end + -- our own version of vim.fn.shellescape compatibile with fish shells -- * don't double-escape '\' (#340) -- * if possible, replace surrounding single quote with double diff --git a/lua/fzf-lua/previewer/fzf.lua b/lua/fzf-lua/previewer/fzf.lua index 0016bd51f..11a4a0176 100644 --- a/lua/fzf-lua/previewer/fzf.lua +++ b/lua/fzf-lua/previewer/fzf.lua @@ -189,8 +189,9 @@ function Previewer.cmd_async:parse_entry_and_verify(entrystr) local errcmd = nil -- verify the file exists on disk and is accessible if #filepath == 0 or not vim.loop.fs_stat(filepath) then - errcmd = ([[echo "%s: NO SUCH FILE OR ACCESS DENIED"]]):format( - filepath and #filepath > 0 and libuv.shellescape(filepath) or "") + errcmd = "echo " .. libuv.shellescape( + string.format("'%': NO SUCH FILE OR ACCESS DENIED", + filepath and #filepath > 0 and filepath or "")) end return filepath, entry, errcmd end diff --git a/lua/fzf-lua/providers/dap.lua b/lua/fzf-lua/providers/dap.lua index 0437df972..961876449 100644 --- a/lua/fzf-lua/providers/dap.lua +++ b/lua/fzf-lua/providers/dap.lua @@ -142,8 +142,8 @@ M.breakpoints = function(opts) end if opts.fzf_opts["--header"] == nil then - opts.fzf_opts["--header"] = libuv.shellescape((":: %s to delete a Breakpoint") - :format(utils.ansi_codes.yellow(""))) + opts.fzf_opts["--header"] = (":: %s to delete a Breakpoint") + :format(utils.ansi_codes.yellow("")) end opts = core.set_fzf_field_index(opts, "{3}", opts._is_skim and "{}" or "{..-2}") diff --git a/lua/fzf-lua/providers/module.lua b/lua/fzf-lua/providers/module.lua index 99b11391d..ffb15d451 100644 --- a/lua/fzf-lua/providers/module.lua +++ b/lua/fzf-lua/providers/module.lua @@ -11,12 +11,6 @@ M.metatable = function(opts) if not opts.metatable then opts.metatable = getmetatable("").__index end - local prev_act = shell.action(function(args) - -- TODO: retreive method help - local help = "" - return string.format("%s:%s", args[1], help) - end, nil, opts.debug) - local methods = {} for k, _ in pairs(opts.metatable) do if not opts.metatable_exclude or opts.metatable_exclude[k] == nil then @@ -26,7 +20,12 @@ M.metatable = function(opts) table.sort(methods, function(a, b) return a < b end) - opts.fzf_opts["--preview"] = prev_act + opts.preview = shell.raw_action(function(args) + -- TODO: retreive method help + local help = "" + return string.format("%s:%s", args[1], help) + end, nil, opts.debug) + opts.fzf_opts["--preview-window"] = "hidden:down:10" -- builtin is excluded from global resume diff --git a/lua/fzf-lua/providers/nvim.lua b/lua/fzf-lua/providers/nvim.lua index 26319dbea..4fc8e2c00 100644 --- a/lua/fzf-lua/providers/nvim.lua +++ b/lua/fzf-lua/providers/nvim.lua @@ -16,14 +16,6 @@ M.commands = function(opts) local buf_commands = vim.api.nvim_buf_get_commands(0, {}) local commands = vim.tbl_extend("force", {}, global_commands, buf_commands) - local prev_act = shell.action(function(args) - local cmd = args[1] - if commands[cmd] then - cmd = vim.inspect(commands[cmd]) - end - return cmd - end, nil, opts.debug) - local entries = {} if opts.sort_lastused then @@ -59,7 +51,13 @@ M.commands = function(opts) table.sort(entries, function(a, b) return a < b end) end - opts.fzf_opts["--preview"] = prev_act + opts.preview = shell.raw_action(function(args) + local cmd = args[1] + if commands[cmd] then + cmd = vim.inspect(commands[cmd]) + end + return cmd + end, nil, opts.debug) core.fzf_exec(entries, opts) end @@ -84,7 +82,7 @@ end local arg_header = function(sel_key, edit_key, text) sel_key = utils.ansi_codes.yellow(sel_key) edit_key = utils.ansi_codes.yellow(edit_key) - return libuv.shellescape((":: %s to %s, %s to edit"):format(sel_key, text, edit_key)) + return (":: %s to %s, %s to edit"):format(sel_key, text, edit_key) end M.command_history = function(opts) @@ -130,7 +128,7 @@ M.jumps = function(opts) table.insert(entries, 1, string.format("%6s %s %s %s", opts.h1 or "jump", "line", "col", "file/text")) - opts.fzf_opts["--header-lines"] = "1" + opts.fzf_opts["--header-lines"] = 1 core.fzf_exec(entries, opts) end @@ -199,19 +197,6 @@ M.marks = function(opts) string.format("marks %s", opts.marks and opts.marks or "")) marks = vim.split(marks, "\n") - --[[ local prev_act = shell.action(function (args, fzf_lines, _) - local mark = args[1]:match("[^ ]+") - local bufnr, lnum, _, _ = unpack(vim.fn.getpos("'"..mark)) - if vim.api.nvim_buf_is_loaded(bufnr) then - return vim.api.nvim_buf_get_lines(bufnr, lnum, fzf_lines+lnum, false) - else - local name = vim.fn.expand(args[1]:match(".* (.*)")) - if vim.fn.filereadable(name) ~= 0 then - return vim.fn.readfile(name, "", fzf_lines) - end - return "UNLOADED: " .. name - end - end) ]] local entries = {} local filter = opts.marks and vim.split(opts.marks, "") for i = #marks, 3, -1 do @@ -233,8 +218,20 @@ M.marks = function(opts) table.insert(entries, 1, string.format("%-5s %s %s %s", "mark", "line", "col", "file/text")) - -- opts.fzf_opts['--preview'] = prev_act - opts.fzf_opts["--header-lines"] = "1" + opts.fzf_opts["--header-lines"] = 1 + --[[ opts.preview = shell.raw_action(function (args, fzf_lines, _) + local mark = args[1]:match("[^ ]+") + local bufnr, lnum, _, _ = unpack(vim.fn.getpos("'"..mark)) + if vim.api.nvim_buf_is_loaded(bufnr) then + return vim.api.nvim_buf_get_lines(bufnr, lnum, fzf_lines+lnum, false) + else + local name = vim.fn.expand(args[1]:match(".* (.*)")) + if vim.fn.filereadable(name) ~= 0 then + return vim.fn.readfile(name, "", fzf_lines) + end + return "UNLOADED: " .. name + end + end) ]] core.fzf_exec(entries, opts) end @@ -267,12 +264,6 @@ M.registers = function(opts) reg:gsub("\n", utils.ansi_codes.magenta("\\n")) end - local prev_act = shell.action(function(args) - local r = args[1]:match("%[(.*)%] ") - local _, contents = pcall(vim.fn.getreg, r) - return contents and register_escape_special(contents) or args[1] - end, nil, opts.debug) - local entries = {} for _, r in ipairs(registers) do -- pcall as this could fail with: @@ -286,7 +277,11 @@ M.registers = function(opts) end end - opts.fzf_opts["--preview"] = prev_act + opts.preview = shell.raw_action(function(args) + local r = args[1]:match("%[(.*)%] ") + local _, contents = pcall(vim.fn.getreg, r) + return contents and register_escape_special(contents) or args[1] + end, nil, opts.debug) core.fzf_exec(entries, opts) end diff --git a/lua/fzf-lua/providers/tmux.lua b/lua/fzf-lua/providers/tmux.lua index 16f605f51..3975c90b9 100644 --- a/lua/fzf-lua/providers/tmux.lua +++ b/lua/fzf-lua/providers/tmux.lua @@ -14,8 +14,7 @@ M.buffers = function(opts) return string.format("[%s] %s", utils.ansi_codes.yellow(buf), data) end - opts.fzf_opts["--delimiter"] = "'[:]'" - opts.fzf_opts["--preview"] = shell.preview_action_cmd(function(items) + opts.fzf_opts["--preview"] = shell.raw_preview_action_cmd(function(items) local buf = items[1]:match("^%[(.-)%]") return string.format("tmux show-buffer -b %s", buf) end, opts.debug) diff --git a/lua/fzf-lua/providers/ui_select.lua b/lua/fzf-lua/providers/ui_select.lua index 2669c5b6c..1a0287669 100644 --- a/lua/fzf-lua/providers/ui_select.lua +++ b/lua/fzf-lua/providers/ui_select.lua @@ -95,7 +95,7 @@ M.ui_select = function(items, ui_opts, on_choice) end opts.fzf_opts = vim.tbl_extend("keep", opts.fzf_opts or {}, { - ["--no-multi"] = "", + ["--no-multi"] = true, ["--preview-window"] = "hidden:right:0", }) diff --git a/tests/libuv_spec.lua b/tests/libuv_spec.lua index 1cfe403b9..53d60715a 100644 --- a/tests/libuv_spec.lua +++ b/tests/libuv_spec.lua @@ -2,6 +2,20 @@ local libuv = require("fzf-lua.libuv") describe("Testing libuv module", function() + it("is_escpaed (posix)", function() + assert.is.False(libuv.is_escaped([[]])) + assert.is.True(libuv.is_escaped([[""]])) + assert.is.True(libuv.is_escaped([['']])) + assert.is.True(libuv.is_escaped([['foo']])) + end) + + it("is_escpaed (win)", function() + assert.is.False(libuv.is_escaped([[]], true)) + assert.is.True(libuv.is_escaped([[""]], true)) + assert.is.True(libuv.is_escaped([[^"^"]], true)) + assert.is.False(libuv.is_escaped([['']], true)) + end) + it("shellescape (win bslash)", function() assert.are.same(libuv.shellescape([[]], 1), [[""]]) assert.are.same(libuv.shellescape([[^]], 1), [["^"]])