You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 lines
10 KiB
Lua

local path = require "fzf-lua.path"
local core = require "fzf-lua.core"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local libuv = require "fzf-lua.libuv"
local last_search = {}
local M = {}
local get_grep_cmd = function(opts, search_query, no_esc)
if opts.cmd_fn and type(opts.cmd_fn) == 'function' then
return opts.cmd_fn(opts, search_query, no_esc)
end
if opts.raw_cmd and #opts.raw_cmd>0 then
return opts.raw_cmd
end
local command = nil
if opts.cmd and #opts.cmd > 0 then
command = opts.cmd
elseif vim.fn.executable("rg") == 1 then
command = string.format("rg %s", opts.rg_opts)
else
command = string.format("grep %s", opts.grep_opts)
end
-- filename takes precedence over directory
-- filespec takes precedence over all and doesn't shellescape
-- this is so user can send a file populating command instead
local search_path = ''
if opts.filespec and #opts.filespec>0 then
search_path = opts.filespec
elseif opts.filename and #opts.filename>0 then
search_path = vim.fn.shellescape(opts.filename)
end
search_query = search_query or ''
if not (no_esc or opts.no_esc) then
search_query = utils.rg_escape(search_query)
end
-- do not escape at all
if not (no_esc == 2 or opts.no_esc == 2) then
search_query = vim.fn.shellescape(search_query)
end
return string.format('%s %s %s', command, search_query, search_path)
end
local function set_search_header(opts, type)
if not opts then opts = {} end
if opts.no_header then return opts end
if not opts.cwd_header then opts.cwd_header = "cwd:" end
if not opts.search_header then opts.search_header = "Searching for:" end
local header_str
local cwd_str = opts.cwd and ("%s %s"):format(opts.cwd_header, opts.cwd)
local search_str = opts.search and #opts.search > 0 and
("%s %s"):format(opts.search_header, opts.search)
-- 1: only search
-- 2: only cwd
-- otherwise, all
if type == 1 then header_str = search_str or ''
elseif type == 2 then header_str = cwd_str or ''
else
header_str = search_str or ''
if #header_str>0 and cwd_str and #cwd_str>0 then
header_str = header_str .. ", "
end
header_str = header_str .. (cwd_str or '')
end
if not header_str or #header_str==0 then return opts end
opts.fzf_opts['--header'] = vim.fn.shellescape(header_str)
return opts
end
M.grep = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
no_esc = last_search.no_esc
opts.search = last_search.query
end
-- if user did not provide a search term
-- provide an input prompt
if not opts.search then
opts.search = vim.fn.input(opts.input_prompt) or ''
end
--[[ if not opts.search or #opts.search == 0 then
utils.info("Please provide a valid search string")
return
end ]]
-- search query in header line
opts = set_search_header(opts)
-- save the search query so the use can
-- call the same search again
last_search = {}
last_search.no_esc = no_esc or opts.no_esc
last_search.query = opts.search
local command = get_grep_cmd(opts, opts.search, no_esc)
opts.fzf_fn = libuv.spawn_nvim_fzf_cmd(
{ cmd = command, cwd = opts.cwd, pid_cb = opts._pid_cb },
function(x)
return core.make_entry_file(opts, x)
end)
--[[ opts.cb_selected = function(_, x)
return x
end ]]
opts = core.set_fzf_line_args(opts)
core.fzf_files(opts)
opts.search = nil
end
M.live_grep = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
no_esc = last_search.no_esc
opts.search = last_search.query
end
opts.query = opts.search or ''
if opts.search and #opts.search>0 then
-- save the search query so the use can
-- call the same search again
last_search = {}
last_search.no_esc = true
last_search.query = opts.search
-- escape unless the user requested not to
if not (no_esc or opts.no_esc) then
opts.query = utils.rg_escape(opts.search)
end
end
-- search query in header line
opts = set_search_header(opts, 2)
opts._reload_command = function(query)
if query and not opts.do_not_save_last_search then
last_search = {}
last_search.no_esc = true
last_search.query = query
end
-- can be nill when called as fzf initial command
query = query or ''
-- TODO: need to empty filespec
-- fix this collision, rename to _filespec
opts.no_esc = nil
opts.filespec = nil
return get_grep_cmd(opts, query, true)
end
if opts.experimental and (opts.git_icons or opts.file_icons) then
opts._fn_transform = function(x)
return core.make_entry_file(opts, x)
end
end
opts = core.set_fzf_line_args(opts)
opts = core.set_fzf_interactive_cmd(opts)
core.fzf_files(opts)
end
M.live_grep_native = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
no_esc = last_search.no_esc
opts.search = last_search.query
end
local query = opts.search or ''
if opts.search and #opts.search>0 then
-- save the search query so the use can
-- call the same search again
last_search = {}
last_search.no_esc = no_esc or opts.no_esc
last_search.query = opts.search
-- escape unless the user requested not to
if not (no_esc or opts.no_esc) then
query = utils.rg_escape(opts.search)
end
end
-- search query in header line
opts = set_search_header(opts, 2)
-- fzf already adds single quotes around the placeholder when expanding
-- for skim we surround it with double quotes or single quote searches fail
local placeholder = utils._if(opts._is_skim, '"{}"', '{q}')
local initial_command = get_grep_cmd(opts , placeholder, 2)
local reload_command = initial_command
if not opts.exec_empty_query then
reload_command = ('[ -z %s ] || %s'):format(placeholder, reload_command)
end
if opts._is_skim then
-- skim interactive mode does not need a piped command
opts.fzf_fn = nil
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(reload_command))
else
opts.fzf_fn = {}
if opts.exec_empty_query or (opts.search and #opts.search > 0) then
opts.fzf_fn = libuv.spawn_nvim_fzf_cmd(
{cmd = initial_command:gsub(placeholder, vim.fn.shellescape(query)),
cwd = opts.cwd, pid_cb = opts._pid_cb },
function(x)
return core.make_entry_file(opts, x)
end)
end
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(
("%s || true"):format(reload_command))))
end
-- we cannot parse any entries as they're not getting called
-- past the initial command, until I can find a solution for
-- that icons must be disabled
opts.git_icons = false
opts.file_icons = false
opts = core.set_fzf_line_args(opts)
core.fzf_files(opts)
opts.search = nil
end
M.live_grep_sk = function(opts)
if not opts then opts = {} end
opts.fzf_bin = "sk"
M.live_grep(opts)
end
M.live_grep_fzf = function(opts)
if not opts then opts = {} end
opts.fzf_bin = "fzf"
M.live_grep(opts)
end
M.live_grep_resume = function(opts)
if not opts then opts = {} end
if not opts.search then
opts.continue_last_search =
(opts.continue_last_search == nil and
opts.repeat_last_search == nil and true) or
(opts.continue_last_search or opts.repeat_last_search)
end
return M.live_grep(opts)
end
M.live_grep_glob = function(opts)
if not opts then opts = {} end
if vim.fn.executable("rg") ~= 1 then
utils.warn("'--glob|iglob' flags requires 'rg' (https://github.com/BurntSushi/ripgrep)")
return
end
opts.cmd_fn = function(opts, query, no_esc)
local glob_arg, glob_str = "", ""
local search_query = query or ""
if query:find(opts.glob_separator) then
search_query, glob_str = query:match("(.*)"..opts.glob_separator.."(.*)")
for _, s in ipairs(utils.strsplit(glob_str, "%s")) do
glob_arg = glob_arg .. (" %s %s")
:format(opts.glob_flag, vim.fn.shellescape(s))
end
end
-- copied over from get_grep_cmd
local search_path = ''
if opts.filespec and #opts.filespec>0 then
search_path = opts.filespec
elseif opts.filename and #opts.filename>0 then
search_path = vim.fn.shellescape(opts.filename)
end
if not (no_esc or opts.no_esc) then
search_query = utils.rg_escape(search_query)
end
-- do not escape at all
if not (no_esc == 2 or opts.no_esc == 2) then
search_query = vim.fn.shellescape(search_query)
end
local cmd = ("rg %s %s -- %s %s")
:format(opts.rg_opts, glob_arg, search_query, search_path)
return cmd
end
return M.live_grep(opts)
end
M.grep_last = function(opts)
if not opts then opts = {} end
opts.continue_last_search = true
return M.grep(opts)
end
M.grep_cword = function(opts)
if not opts then opts = {} end
opts.search = vim.fn.expand("<cword>")
return M.grep(opts)
end
M.grep_cWORD = function(opts)
if not opts then opts = {} end
opts.search = vim.fn.expand("<cWORD>")
return M.grep(opts)
end
M.grep_visual = function(opts)
if not opts then opts = {} end
opts.search = utils.get_visual_selection()
return M.grep(opts)
end
M.grep_project = function(opts)
if not opts then opts = {} end
if not opts.search then opts.search = '' end
return M.grep(opts)
end
M.grep_curbuf = function(opts)
if not opts then opts = {} end
opts.rg_opts = config.globals.grep.rg_opts .. " --with-filename"
opts.grep_opts = config.globals.grep.grep_opts .. " --with-filename"
if opts.exec_empty_query == nil then
opts.exec_empty_query = true
end
opts.filename = vim.api.nvim_buf_get_name(0)
if #opts.filename > 0 then
opts.filename = path.relative(opts.filename, vim.loop.cwd())
return M.live_grep(opts)
else
utils.info("Rg current buffer requires actual file on disk")
return
end
end
return M