From 976ee884009bd092a2d6a4346b4ccfe4cffcc1a4 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Thu, 22 Jul 2021 21:34:12 -0700 Subject: [PATCH] added initial skim (rust fzf) support --- README.md | 1 + lua/fzf-lua/config.lua | 1 + lua/fzf-lua/core.lua | 12 ++- lua/fzf-lua/init.lua | 3 + lua/fzf-lua/providers/grep.lua | 114 +++++++++++++++++------------ lua/fzf-lua/providers/helptags.lua | 2 +- lua/fzf-lua/providers/lsp.lua | 9 ++- lua/fzf-lua/providers/oldfiles.lua | 6 +- lua/fzf-lua/providers/quickfix.lua | 6 +- lua/fzf-lua/utils.lua | 3 +- 10 files changed, 95 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index f8a71a7..8384e81 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ require'fzf-lua'.setup { win_col = 0.50, -- window col position (0=left, 1=right) -- win_border = false, -- window border? or borderchars? win_border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' }, + -- fzf_bin = 'sk', -- use skim instead of fzf? fzf_layout = 'reverse', -- fzf '--layout=' fzf_args = '', -- adv: fzf extra args, empty unless adv preview_cmd = '', -- 'head -n $FZF_PREVIEW_LINES', diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 6e57217..7cda1c6 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -16,6 +16,7 @@ M.win_row = 0.30 M.win_col = 0.50 M.win_border = true M.default_prompt = '> ' +M.fzf_bin = nil M.fzf_layout = 'reverse' M.preview_cmd = nil -- auto detect head|bat M.preview_border = 'border' diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index 99861f3..6d5cffe 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -47,11 +47,12 @@ M.build_fzf_cli = function(opts) local cfg = require'fzf-lua.config' opts.prompt = opts.prompt or cfg.default_prompt opts.preview_offset = opts.preview_offset or '' + opts.fzf_bin = opts.fzf_bin or cfg.fzf_bin local cli = string.format( [[ %s --layout=%s --bind=%s --prompt=%s]] .. [[ --preview-window='%s%s' --preview=%s]] .. - [[ --height=100%% --ansi --info=inline]] .. - [[ %s %s %s]], + [[ --height=100%% --ansi]] .. + [[ %s %s %s %s]], opts.fzf_args or cfg.fzf_args or '', opts.fzf_layout or cfg.fzf_layout, utils._if(opts.fzf_binds, opts.fzf_binds, @@ -60,6 +61,8 @@ M.build_fzf_cli = function(opts) utils._if(opts.preview_window, opts.preview_window, cfg.preview_window()), utils._if(#opts.preview_offset>0, ":"..opts.preview_offset, ''), utils._if(opts.preview, opts.preview, M.preview_cmd(opts, cfg)), + -- HACK: support skim (rust version of fzf) + utils._if(opts.fzf_bin and opts.fzf_bin:find('sk')~=nil, "--inline-info", "--info=inline"), utils._if(actions.expect(opts.actions), actions.expect(opts.actions), ''), utils._if(opts.nomulti, '--no-multi', '--multi'), utils._if(opts.cli_args, opts.cli_args, '') @@ -154,11 +157,6 @@ end M.fzf_files = function(opts) - if not opts or not opts.fzf_fn then - utils.warn("Core.fzf_files(opts) cannot run without callback fn") - return - end - -- reset git tracking opts.diff_files, opts.untracked_files = nil, nil if not utils.is_git_repo() then opts.git_icons = false end diff --git a/lua/fzf-lua/init.lua b/lua/fzf-lua/init.lua index dd1eed2..1975f0f 100644 --- a/lua/fzf-lua/init.lua +++ b/lua/fzf-lua/init.lua @@ -53,6 +53,7 @@ function M.setup(opts) win_border = "any", -- boolean|table (borderchars) winopts_raw = "function", default_prompt = "string", + fzf_bin = "string", fzf_args = "string", fzf_layout = "string", fzf_binds = "table", @@ -185,6 +186,8 @@ function M.setup(opts) end -- reset default window opts if set by user fzf.default_window_options = config.winopts() + -- set the fzf binary if set by the user + if config.fzf_bin then fzf.fzf_binary = config.fzf_bin end end -- we usually send winopts with every fzf.fzf call diff --git a/lua/fzf-lua/providers/grep.lua b/lua/fzf-lua/providers/grep.lua index 0d6b0b4..7f2a99f 100644 --- a/lua/fzf-lua/providers/grep.lua +++ b/lua/fzf-lua/providers/grep.lua @@ -11,7 +11,7 @@ local config = require "fzf-lua.config" local M = {} -local get_grep_cmd = function(opts) +local get_grep_cmd = function(opts, search_query, no_esc) local command = nil if opts.cmd and #opts.cmd > 0 then @@ -30,11 +30,12 @@ local get_grep_cmd = function(opts) search_path = vim.fn.shellescape(opts.cwd) end - return string.format("%s -- %s %s", command, - utils._if(opts.last_search and #opts.last_search>0, - vim.fn.shellescape(opts.last_search), "{q}"), - search_path - ) + if search_query == nil then search_query = '' + elseif not no_esc then + search_query = vim.fn.shellescape(utils.rg_escape(search_query)) + end + + return string.format("%s -- %s %s", command, search_query, search_path) end M.grep = function(opts) @@ -46,28 +47,26 @@ M.grep = function(opts) "rg_opts", "grep_opts", }) - if opts.search and #opts.search>0 then - opts.search = utils.rg_escape(opts.search) - end - - if opts.repeat_last_search == true then + if opts.continue_last_search or opts.repeat_last_search then opts.search = config.grep.last_search end - -- save the next search as last_search so we - -- let the caller have an option to run the - -- same search again + + -- if user did not provide a search term + -- provide an input prompt if not opts.search or #opts.search == 0 then - config.grep.last_search = vim.fn.input(opts.input_prompt) - else - config.grep.last_search = opts.search + opts.search = vim.fn.input(opts.input_prompt) end - opts.last_search = config.grep.last_search - if not opts.last_search or #opts.last_search == 0 then - utils.info("Please provider valid search string") + + if not opts.search or #opts.search == 0 then + utils.info("Please provide a valid search string") return end - local command = get_grep_cmd(opts) + -- save the search query so the use can + -- call the same search again + config.grep.last_search = opts.search + + local command = get_grep_cmd(opts, opts.search) opts.fzf_fn = fzf_helpers.cmd_line_transformer( command, @@ -94,9 +93,35 @@ M.grep = function(opts) ]] opts.preview_offset = "+{3}-/2" core.fzf_files(opts) + opts.search = nil end +M.live_grep_sk = function(opts) + + -- "'{}'" opens sk with an empty search_query showing all files + -- "{}" opens sk without executing an empty string query + -- the problem is the latter doesn't support escaped chars + -- TODO: how to open without a query with special char support + local sk_args = get_grep_cmd(opts , "'{}'", true) + + opts.cli_args = "--delimiter='[: ]' " .. + string.format("--cmd-prompt='%s' -i -c %s", + opts.prompt, + vim.fn.shellescape(sk_args)) + + opts.git_icons = false + opts.file_icons = false + opts.filespec = '{1}' + opts.preview_offset = "+{2}-/2" + opts.preview_args = "--highlight-line={2}" + + opts.fzf_fn = nil --function(_) end + core.fzf_files(opts) + + opts.search = nil +end + M.live_grep = function(opts) opts = config.getopts(opts, config.grep, { @@ -106,41 +131,33 @@ M.live_grep = function(opts) "rg_opts", "grep_opts", }) + if opts.continue_last_search or opts.repeat_last_search then + opts.search = config.grep.last_search + end + if opts.search and #opts.search>0 then - opts.search = utils.rg_escape(opts.search) + -- save the search query so the use can + -- call the same search again + config.grep.last_search = opts.search end - -- resetting last_search will return - -- {q} placeholder in our command - opts.last_search = opts.search - local initial_command = get_grep_cmd(opts) - opts.last_search = nil - local reload_command = get_grep_cmd(opts) .. " || true" + -- HACK: support skim (rust version of fzf) + opts.fzf_bin = opts.fzf_bin or config.fzf_bin + if opts.fzf_bin and opts.fzf_bin:find('sk')~=nil then + return M.live_grep_sk(opts) + end - --[[ local fzf_binds = utils.tbl_deep_clone(config.fzf_binds) - table.insert(fzf_binds, string.format("change:reload:%s", reload_command)) - opts.fzf_binds = vim.fn.shellescape(table.concat(fzf_binds, ',')) ]] + -- use {q} as a placeholder for fzf + local initial_command = get_grep_cmd(opts, opts.search) + local reload_command = get_grep_cmd(opts, "{q}", true) .. " || true" opts.cli_args = "--delimiter='[: ]' " .. string.format("--phony --query=%s --bind=%s", - utils._if(opts.search and #opts.search>0, opts.search, [['']]), + utils._if(opts.search and #opts.search>0, + vim.fn.shellescape(utils.rg_escape(opts.search)), + [['']]), vim.fn.shellescape(string.format("change:reload:%s", reload_command))) - opts.preview_args = "--highlight-line={3}" -- bat higlight - --[[ - # Preview with bat, matching line in the middle of the window below - # the fixed header of the top 3 lines - # - # ~3 Top 3 lines as the fixed header - # +{2} Base scroll offset extracted from the second field - # +3 Extra offset to compensate for the 3-line header - # /2 Put in the middle of the preview area - # - '--preview-window '~3:+{2}+3/2'' - ]] - opts.preview_offset = "+{3}-/2" - - -- TODO: -- this is not getting called past the initial command -- until we fix that we cannot use icons as they interfere @@ -158,11 +175,12 @@ M.live_grep = function(opts) end) core.fzf_files(opts) + opts.search = nil end M.grep_last = function(opts) if not opts then opts = {} end - opts.repeat_last_search = true + opts.continue_last_search = true return M.grep(opts) end diff --git a/lua/fzf-lua/providers/helptags.lua b/lua/fzf-lua/providers/helptags.lua index dae9d97..3a78028 100644 --- a/lua/fzf-lua/providers/helptags.lua +++ b/lua/fzf-lua/providers/helptags.lua @@ -88,7 +88,7 @@ local fzf_function = function (cb) end end -- done - -- cb(nil) + cb(nil, function() end) end)() end diff --git a/lua/fzf-lua/providers/lsp.lua b/lua/fzf-lua/providers/lsp.lua index 81a81ca..e87562a 100644 --- a/lua/fzf-lua/providers/lsp.lua +++ b/lua/fzf-lua/providers/lsp.lua @@ -122,7 +122,11 @@ local function set_lsp_fzf_fn(opts) -- we use this passthrough so we can send the -- coroutine variable (not used rn but who knows?) opts.lsp_handler.target = function(_, _, result) - return opts.lsp_handler.handler(opts, cb, co, result) + opts.lsp_handler.handler(opts, cb, co, result) + -- close the pipe to fzf, this + -- removes the loading indicator in fzf + cb(nil, function() end) + return end local _, cancel_all = vim.lsp.buf_request(opts.bufnr, @@ -375,6 +379,9 @@ M.diagnostics = function(opts) end end -- coroutine.yield() + -- close the pipe to fzf, this + -- removes the loading indicator in fzf + cb(nil, function() end) end)() end diff --git a/lua/fzf-lua/providers/oldfiles.lua b/lua/fzf-lua/providers/oldfiles.lua index b40a207..3e32aec 100644 --- a/lua/fzf-lua/providers/oldfiles.lua +++ b/lua/fzf-lua/providers/oldfiles.lua @@ -54,10 +54,12 @@ M.oldfiles = function(opts) x = core.make_entry_file(opts, x) cb(x, function(err) if err then return end - -- cb(nil) -- to close the pipe to fzf, this removes the loading - -- indicator in fzf + -- close the pipe to fzf, this + -- removes the loading indicator in fzf + cb(nil, function() end) end) end + cb(nil, function() end) end --[[ opts.cb_selected = function(_, x) diff --git a/lua/fzf-lua/providers/quickfix.lua b/lua/fzf-lua/providers/quickfix.lua index 36a395a..52f5b3e 100644 --- a/lua/fzf-lua/providers/quickfix.lua +++ b/lua/fzf-lua/providers/quickfix.lua @@ -31,10 +31,12 @@ local quickfix_run = function(opts, cfg, locations) x = core.make_entry_file(opts, x) cb(x, function(err) if err then return end - -- cb(nil) -- to close the pipe to fzf, this removes the loading - -- indicator in fzf + -- close the pipe to fzf, this + -- removes the loading indicator in fzf + cb(nil, function() end) end) end + cb(nil, function() end) end --[[ opts.cb_selected = function(_, x) diff --git a/lua/fzf-lua/utils.lua b/lua/fzf-lua/utils.lua index e403311..7e1512e 100644 --- a/lua/fzf-lua/utils.lua +++ b/lua/fzf-lua/utils.lua @@ -55,9 +55,10 @@ function M.is_git_repo() end function M.rg_escape(str) + if not str then return str end -- [(~'"\/$?'`*&&||;[]<>)] -- escape "\~$?*|[()" - return str:gsub("[\\~$?*|\\[()]", function(x) + return str:gsub("[\\~$?*|{\\[()]", function(x) return '\\' .. x end) end