diff --git a/README.md b/README.md index 2751911..7384d3e 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ I tried to make it as customizable as possible, if you find you need to change s customization can be achieved by calling the `setup()` function or individually sending parameters to a builtin command, for exmaple: ```lua -:lua require('fzf-lua').files({ fzf_layout = 'reverse-list' }) +:lua require('fzf-lua').files({ fzf_opts = {['--layout'] = 'reverse-list'} }) ``` Consult the list below for available settings: @@ -224,8 +224,17 @@ require'fzf-lua'.setup { hl_border = 'FloatBorder', -- window border color }, -- fzf_bin = 'sk', -- use skim instead of fzf? - fzf_layout = 'reverse', -- fzf '--layout=' options - fzf_args = '', -- adv: fzf extra args, empty unless adv + fzf_opts = { + -- options are sent as `=` + -- set to `false` to remove a flag + -- set to '' for a non-value flag + -- for raw args use `fzf_args` instead + ['--ansi'] = '', + ['--prompt'] = ' >', + ['--info'] = 'inline', + ['--height'] = '100%', + ['--layout'] = 'reverse', + }, fzf_binds = { -- fzf '--bind=' options ["f2"] = "toggle-preview", ["f3"] = "toggle-preview-wrap", diff --git a/doc/fzf-lua.txt b/doc/fzf-lua.txt index a8ce6bd..21d69dd 100644 --- a/doc/fzf-lua.txt +++ b/doc/fzf-lua.txt @@ -236,7 +236,7 @@ customization can be achieved by calling the setup() function or individually sending parameters to a builtin command, for exmaple: > - :lua require('fzf-lua').files({ fzf_layout = 'reverse-list' }) + :lua require('fzf-lua').files({ fzf_opts = {['--layout'] = 'reverse-list'} }) < Consult the list below for available settings: @@ -259,8 +259,17 @@ Consult the list below for available settings: hl_border = 'FloatBorder', -- window border color }, -- fzf_bin = 'sk', -- use skim instead of fzf? - fzf_layout = 'reverse', -- fzf '--layout=' options - fzf_args = '', -- adv: fzf extra args, empty unless adv + fzf_opts = { + -- options are sent as `=` + -- set to `false` to remove a flag + -- set to '' for a non-value flag + -- for raw args use `fzf_args` instead + ['--ansi'] = '', + ['--prompt'] = ' >', + ['--info'] = 'inline', + ['--height'] = '100%', + ['--layout'] = 'reverse', + }, fzf_binds = { -- fzf '--bind=' options ["f2"] = "toggle-preview", ["f3"] = "toggle-preview-wrap", diff --git a/lua/fzf-lua/actions.lua b/lua/fzf-lua/actions.lua index 2ece309..6f994a6 100644 --- a/lua/fzf-lua/actions.lua +++ b/lua/fzf-lua/actions.lua @@ -5,7 +5,7 @@ local M = {} -- return fzf '--expect=' string from actions keyval tbl M.expect = function(actions) - if not actions then return '' end + if not actions then return nil end local keys = {} for k, v in pairs(actions) do if k ~= "default" and v ~= false then diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 8387b8c..59faa61 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -34,10 +34,14 @@ M.globals = { vim.cmd("set winhl=Normal:Normal,FloatBorder:FloatBorder") end, ]] }, - default_prompt = '> ', fzf_bin = nil, - fzf_colors = nil, - fzf_layout = 'reverse', + fzf_opts = { + ['--ansi'] = '', + ['--prompt'] = ' >', + ['--info'] = 'inline', + ['--height'] = '100%', + ['--layout'] = 'reverse', + }, fzf_binds = { -- toggle preview -- toggle preview text wrap @@ -501,6 +505,7 @@ end function M.normalize_opts(opts, defaults) if not opts then opts = {} end + if not opts.fzf_opts then opts.fzf_opts = {} end opts = vim.tbl_deep_extend("keep", opts, defaults) if defaults.winopts then if not opts.winopts then opts.winopts = {} end diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index b248814..1ceb1e5 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -12,7 +12,6 @@ M.fzf = function(opts, contents) local fzf_win = win(opts) if not fzf_win then return end -- instantiate the previewer - -- if not opts.preview and not previewer and local previewer, preview_opts = nil, nil if opts.previewer and type(opts.previewer) == 'string' then preview_opts = config.globals.previewers[opts.previewer] @@ -28,14 +27,14 @@ M.fzf = function(opts, contents) previewer = preview_opts._ctor()(preview_opts, opts, fzf_win) end if previewer then - opts.preview = previewer:cmdline() + opts.fzf_opts['--preview'] = previewer:cmdline() if type(previewer.preview_window) == 'function' then -- do we need to override the preview_window args? -- this can happen with the builtin previewer -- (1) when using a split we use the previewer as placeholder -- (2) we use 'nohidden:right:0' to trigger preview function -- calls without displaying the native fzf previewer split - opts.preview_window = previewer:preview_window(opts.preview_window) + opts.fzf_opts['--preview-window'] = previewer:preview_window(opts.preview_window) end end @@ -121,40 +120,83 @@ M.create_fzf_binds = function(binds) return "--bind=" .. vim.fn.shellescape(table.concat(tbl, ",")) end -M.build_fzf_cli = function(opts, debug_print) - opts.prompt = opts.prompt or config.globals.default_prompt - opts.preview_offset = opts.preview_offset or '' - opts.fzf_info = opts.fzf_info or config.globals.fzf_info - opts.fzf_ansi = opts.fzf_ansi or config.globals.fzf_ansi - - if not opts.fzf_info then +M.build_fzf_cli = function(opts) + opts.fzf_opts = vim.tbl_extend("force", config.globals.fzf_opts, opts.fzf_opts or {}) + -- copy from globals + for _, o in ipairs({ + 'fzf_info', + 'fzf_ansi', + 'fzf_binds', + 'fzf_colors', + 'fzf_layout', + 'fzf_args', + 'fzf_raw_args', + 'fzf_cli_args', + }) do + opts[o] = opts[o] or config.globals[o] + end + opts.fzf_opts["--bind"] = M.create_fzf_binds(opts.fzf_binds) + opts.fzf_opts["--color"] = M.create_fzf_colors(opts.fzf_colors) + opts.fzf_opts["--expect"] = actions.expect(opts.actions) + opts.fzf_opts["--preview"] = opts.preview or opts.fzf_opts["--preview"] + if opts.fzf_opts["--preview-window"] == nil then + opts.fzf_opts["--preview-window"] = M.preview_window(opts) + end + if opts.preview_offset and #opts.preview_offset>0 then + opts.fzf_opts["--preview-window"] = + opts.fzf_opts["--preview-window"] .. ":" .. opts.preview_offset + end + -- shell escape the prompt + opts.fzf_opts["--prompt"] = + vim.fn.shellescape(opts.prompt or opts.fzf_opts["--prompt"]) + -- multi | no-multi (select) + if opts.nomulti or opts.fzf_opts["--no-multi"] then + opts.fzf_opts["--multi"] = nil + opts.fzf_opts["--no-multi"] = '' + else + opts.fzf_opts["--multi"] = '' + opts.fzf_opts["--no-multi"] = nil + end + -- backward compatibility, add all previously known options + for k, v in pairs({ + ['--ansi'] = 'fzf_ansi', + ['--layout'] = 'fzf_layout' + }) do + if opts[v] and #opts[v]==0 then + opts.fzf_opts[k] = nil + elseif opts[v] then + opts.fzf_opts[k] = opts[v] + end + end + local extra_args = '' + for _, o in ipairs({ + 'fzf_args', + 'fzf_raw_args', + 'fzf_cli_args', + '_fzf_cli_args', + }) do + if opts[o] then extra_args = extra_args .. " " .. opts[o] end + end + if opts._is_skim then + local info = opts.fzf_opts["--info"] -- skim (rust version of fzf) doesn't -- support the '--info=' flag - opts.fzf_info = utils._if(opts._is_skim, "--inline-info", "--info=inline") + opts.fzf_opts["--info"] = nil + if info == 'inline' then + -- inline for skim is defined as: + opts.fzf_opts["--inline-info"] = '' + end end - - local cli = string.format( - [[ %s %s %s --layout=%s --prompt=%s]] .. - [[ --preview-window=%s%s --preview=%s]] .. - [[ --height=100%%]] .. - [[ %s %s %s %s %s %s %s]], - opts.fzf_args or config.globals.fzf_args or '', - M.create_fzf_colors(opts.fzf_colors or config.globals.fzf_colors), - M.create_fzf_binds(opts.fzf_binds or config.globals.fzf_binds), - opts.fzf_layout or config.globals.fzf_layout, - vim.fn.shellescape(opts.prompt), - utils._if(opts.preview_window, opts.preview_window, M.preview_window(opts)), - utils._if(#opts.preview_offset>0, ":"..opts.preview_offset, ''), - utils._if(opts.preview and #opts.preview>0, opts.preview, "''"), - opts.fzf_ansi or '--ansi', opts.fzf_info or '', - utils._if(actions.expect(opts.actions), actions.expect(opts.actions), ''), - utils._if(opts.nomulti, '--no-multi', '--multi'), - utils._if(opts.fzf_cli_args, opts.fzf_cli_args, ''), - utils._if(opts._fzf_cli_args, opts._fzf_cli_args, ''), - utils._if(opts._fzf_header_args, opts._fzf_header_args, '') - ) - if debug_print then print(cli) end - return cli + -- build the clip args + local cli_args = '' + for k, v in pairs(opts.fzf_opts) do + if v then + v = v:gsub(k .. '=', '') + cli_args = cli_args .. + (" %s%s"):format(k,#v>0 and "="..v or '') + end + end + return cli_args .. extra_args end local get_diff_files = function(opts) @@ -235,7 +277,7 @@ end M.set_fzf_line_args = function(opts) opts._line_placeholder = 2 -- delimiters are ':' and - opts._fzf_cli_args = (opts._fzf_cli_args or '') .. " --delimiter='[:\\t]'" + opts.fzf_opts["--delimiter"] = vim.fn.shellescape('[:\\t]') --[[ # # Explanation of the fzf preview offset options: @@ -424,13 +466,13 @@ M.set_fzf_interactive = function(opts, act_cmd, placeholder) end -- skim interactive mode does not need a piped command opts.fzf_fn = nil - opts._fzf_cli_args = string.format( - "--prompt='*%s' --cmd-prompt='%s' --cmd-query=%s -i -c %s", - opts.prompt, opts.prompt, - -- since we surrounded the skim placeholder with quotes - -- we need to escape them in the initial query - vim.fn.shellescape(utils.sk_escape(query)), - act_cmd) + opts.fzf_opts['--prompt'] = '*' .. opts.prompt + opts.fzf_opts['--cmd-prompt'] = vim.fn.shellescape(opts.prompt) + opts.prompt = nil + -- since we surrounded the skim placeholder with quotes + -- we need to escape them in the initial query + opts.fzf_opts['--cmd-query'] = vim.fn.shellescape(utils.sk_escape(query)) + opts._fzf_cli_args = string.format( "-i -c %s", act_cmd) else -- fzf already adds single quotes -- around the place holder @@ -442,8 +484,9 @@ M.set_fzf_interactive = function(opts, act_cmd, placeholder) return M.make_entry_file(opts, x) end) end - opts._fzf_cli_args = string.format('--phony --query=%s --bind=%s', - vim.fn.shellescape(query), + opts.fzf_opts['--phony'] = '' + opts.fzf_opts['--query'] = vim.fn.shellescape(query) + opts._fzf_cli_args = string.format('--bind=%s', vim.fn.shellescape(string.format("change:reload:%s || true", act_cmd))) end diff --git a/lua/fzf-lua/providers/buffers.lua b/lua/fzf-lua/providers/buffers.lua index 964a3ed..8d03a7b 100644 --- a/lua/fzf-lua/providers/buffers.lua +++ b/lua/fzf-lua/providers/buffers.lua @@ -177,11 +177,10 @@ M.buffers = function(opts) items = add_buffer_entry(opts, buf, items, header_line) end - opts.preview = act - opts._fzf_cli_args = utils._if( - header_line and not opts.ignore_current_buffer, - '--header-lines=1', '' - ) + opts.fzf_opts['--preview'] = act + if header_line and not opts.ignore_current_buffer then + opts.fzf_opts['--header-lines'] = '1' + end local selected = core.fzf(opts, items) if not selected then return end @@ -243,12 +242,12 @@ M.buffer_lines = function(opts) -- ignore bufnr when searching -- disable multi-select opts.nomulti = true - opts.preview_window = 'hidden:right:0' - opts._fzf_cli_args = "--delimiter=']' --nth 2,-1" + opts.fzf_opts["--preview-window"] = 'hidden:right:0' + opts.fzf_opts["--delimiter"] = vim.fn.shellescape(']') + opts.fzf_opts["--nth"] = '2,-1' if opts.search and #opts.search>0 then - opts._fzf_cli_args = opts._fzf_cli_args .. - (" --query=%s"):format(vim.fn.shellescape(opts.search)) + opts.fzf_opts['--query'] = vim.fn.shellescape(opts.search) end local selected = core.fzf(opts, items) @@ -318,8 +317,9 @@ M.tabs = function(opts) end opts.nomulti = true - opts.preview_window = 'hidden:right:0' - opts.fzf_cli_args = "--delimiter='[\\)]' --with-nth=2" + opts.fzf_opts["--preview-window"] = 'hidden:right:0' + opts.fzf_opts["--delimiter"] = vim.fn.shellescape('[\\)]') + opts.fzf_opts["---with-nth"] = '2' local selected = core.fzf(opts, items) diff --git a/lua/fzf-lua/providers/colorschemes.lua b/lua/fzf-lua/providers/colorschemes.lua index 4231b35..f5236e0 100644 --- a/lua/fzf-lua/providers/colorschemes.lua +++ b/lua/fzf-lua/providers/colorschemes.lua @@ -37,9 +37,9 @@ M.colorschemes = function(opts) -- must add ':nohidden' or fzf ignore the preview action -- disabling our live preview of colorschemes - opts.preview = prev_act - opts.preview_window = opts.preview_window or 'nohidden:right:0' - opts.nomulti = utils._if(opts.nomulti~=nil, opts.nomulti, true) + opts.fzf_opts['--preview'] = prev_act + opts.fzf_opts['--no-multi'] = '' + opts.fzf_opts['--preview-window'] = 'nohidden:right:0' local selected = core.fzf(opts, colors) diff --git a/lua/fzf-lua/providers/files.lua b/lua/fzf-lua/providers/files.lua index 6ecaa48..8c9e854 100644 --- a/lua/fzf-lua/providers/files.lua +++ b/lua/fzf-lua/providers/files.lua @@ -58,8 +58,9 @@ M.files_resume = function(opts) local command = get_files_cmd(opts) - opts._fzf_cli_args = ('--query="%s" --bind=change:execute-silent:%s'): - format(last_query, vim.fn.shellescape(raw_act)) + opts.fzf_opts['--query'] = vim.fn.shellescape(last_query) + opts._fzf_cli_args = ('--bind=change:execute-silent:%s'): + format(vim.fn.shellescape(raw_act)) opts.fzf_fn = fzf_helpers.cmd_line_transformer( {cmd = command, cwd = opts.cwd}, diff --git a/lua/fzf-lua/providers/grep.lua b/lua/fzf-lua/providers/grep.lua index ccf4276..8f7a7c9 100644 --- a/lua/fzf-lua/providers/grep.lua +++ b/lua/fzf-lua/providers/grep.lua @@ -70,10 +70,7 @@ local function set_search_header(opts, type) header_str = header_str .. (cwd_str or '') end if not header_str or #header_str==0 then return opts end - opts._fzf_header_args = opts._fzf_header_args or '' - opts._fzf_header_args = string.format([[%s --header=%s ]], - opts._fzf_header_args, - vim.fn.shellescape(header_str)) + opts.fzf_opts['--header'] = vim.fn.shellescape(header_str) return opts end @@ -210,12 +207,12 @@ M.live_grep_native = function(opts) if opts._is_skim then -- skim interactive mode does not need a piped command opts.fzf_fn = nil - opts._fzf_cli_args = string.format( - "--prompt='*%s' --cmd-prompt='%s' --cmd-query=%s -i -c %s", - opts.prompt, opts.prompt, - -- since we surrounded the skim placeholder with quotes - -- we need to escape them in the initial query - vim.fn.shellescape(utils.sk_escape(query)), + opts.fzf_opts['--prompt'] = '*' .. opts.prompt + opts.fzf_opts['--cmd-prompt'] = vim.fn.shellescape(opts.prompt) + -- since we surrounded the skim placeholder with quotes + -- we need to escape them in the initial query + opts.fzf_opts['--cmd-query'] = vim.fn.shellescape(utils.sk_escape(query)) + opts._fzf_cli_args = string.format("-i -c %s", vim.fn.shellescape( ("(cd %s && %s)"):format( vim.fn.shellescape(opts.cwd or '.'), @@ -230,8 +227,9 @@ M.live_grep_native = function(opts) return core.make_entry_file(opts, x) end) end - opts._fzf_cli_args = string.format('--phony --query=%s --bind=%s', - vim.fn.shellescape(query), + opts.fzf_opts['--phony'] = '' + opts.fzf_opts['--query'] = vim.fn.shellescape(query) + opts._fzf_cli_args = string.format('--bind=%s', vim.fn.shellescape(("change:reload:%s"):format( ("(cd %s && %s || true)"):format( vim.fn.shellescape(opts.cwd or '.'), diff --git a/lua/fzf-lua/providers/helptags.lua b/lua/fzf-lua/providers/helptags.lua index dc690be..5182da3 100644 --- a/lua/fzf-lua/providers/helptags.lua +++ b/lua/fzf-lua/providers/helptags.lua @@ -104,9 +104,9 @@ M.helptags = function(opts) -- local prev_act = action(function (args) end) - opts.nomulti = true - opts.preview_window = 'hidden:right:0' - opts._fzf_cli_args = "--nth 1" + opts.fzf_opts['--no-multi'] = '' + opts.fzf_opts['--preview-window'] = 'hidden:right:0' + opts.fzf_opts['--nth'] = '1' local selected = core.fzf(opts, fzf_function) diff --git a/lua/fzf-lua/providers/lsp.lua b/lua/fzf-lua/providers/lsp.lua index c60268b..f93b93d 100644 --- a/lua/fzf-lua/providers/lsp.lua +++ b/lua/fzf-lua/providers/lsp.lua @@ -344,8 +344,8 @@ M.code_actions = function(opts) opts.nomulti = true opts.previewer = false - opts.preview_window = 'right:0' - opts._fzf_cli_args = "--delimiter=':'" + opts.fzf_opts["--preview-window"] = 'right:0' + opts.fzf_opts["--delimiter"] = vim.fn.shellescape(':') opts = set_lsp_fzf_fn(opts) -- error or no sync request no results diff --git a/lua/fzf-lua/providers/manpages.lua b/lua/fzf-lua/providers/manpages.lua index d61317e..12d629e 100644 --- a/lua/fzf-lua/providers/manpages.lua +++ b/lua/fzf-lua/providers/manpages.lua @@ -42,9 +42,10 @@ M.manpages = function(opts) utils.ansi_codes.red(man), desc) end) - opts.nomulti = true - opts.preview_window = 'hidden:right:0' - opts._fzf_cli_args = "--tiebreak begin --nth 1,2" + opts.fzf_opts['--no-multi'] = '' + opts.fzf_opts['--preview-window'] = 'hidden:right:0' + opts.fzf_opts['--tiebreak'] = 'begin' + opts.fzf_opts['--nth'] = '1,2' local selected = core.fzf(opts, fzf_fn) diff --git a/lua/fzf-lua/providers/module.lua b/lua/fzf-lua/providers/module.lua index 6aae2ec..5e026ad 100644 --- a/lua/fzf-lua/providers/module.lua +++ b/lua/fzf-lua/providers/module.lua @@ -33,9 +33,9 @@ M.metatable = function(opts) table.sort(methods, function(a, b) return a", "", "execute") + opts.fzf_opts['--header'] = arg_header("", "", "execute") history(opts, "cmd") end M.search_history = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.search_history) if not opts then return end - opts._fzf_cli_args = arg_header("", "", "search") + opts.fzf_opts['--header'] = arg_header("", "", "search") history(opts, "search") end @@ -126,9 +126,8 @@ M.marks = function(opts) table.sort(entries, function(a, b) return a