new feautre: <c-i> to toggle between 'grep|live_grep'

main
bhagwan 2 years ago
parent e2c6f8cbf9
commit 27f58c51c2

@ -548,8 +548,15 @@ require'fzf-lua'.setup {
rg_opts = "--column --line-number --no-heading --color=always --smart-case --max-columns=512",
grep_opts = "--binary-files=without-match --line-number --recursive --color=auto --perl-regexp",
-- 'live_grep_glob' options:
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
actions = {
-- actions inherit from 'actions.files' and merge
-- this action toggles between 'grep' and 'live_grep'
["ctrl-i"] = { actions.grep_lgrep }
},
no_header = false, -- hide grep|cwd header?
no_header_i = false, -- hide interactive header?
},
args = {
prompt = 'Args ',
@ -631,7 +638,13 @@ require'fzf-lua'.setup {
-- 'tags_live_grep' options, `rg` prioritizes over `grep`
rg_opts = "--no-heading --color=always --smart-case",
grep_opts = "--color=auto --perl-regexp",
-- actions inherit from 'actions.files'
actions = {
-- actions inherit from 'actions.files' and merge
-- this action toggles between 'grep' and 'live_grep'
["ctrl-i"] = { actions.grep_lgrep }
},
no_header = false, -- hide grep|cwd header?
no_header_i = false, -- hide interactive header?
},
btags = {
prompt = 'BTags ',

@ -594,8 +594,15 @@ Consult the list below for available settings:
rg_opts = "--column --line-number --no-heading --color=always --smart-case --max-columns=512",
grep_opts = "--binary-files=without-match --line-number --recursive --color=auto --perl-regexp",
-- 'live_grep_glob' options:
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
actions = {
-- actions inherit from 'actions.files' and merge
-- this action toggles between 'grep' and 'live_grep'
["ctrl-i"] = { actions.grep_lgrep }
},
no_header = false, -- hide grep|cwd header?
no_header_i = false, -- hide interactive header?
},
args = {
prompt = 'Args ',
@ -677,7 +684,13 @@ Consult the list below for available settings:
-- 'tags_live_grep' options, `rg` prioritizes over `grep`
rg_opts = "--no-heading --color=always --smart-case",
grep_opts = "--color=auto --perl-regexp",
-- actions inherit from 'actions.files'
actions = {
-- actions inherit from 'actions.files' and merge
-- this action toggles between 'grep' and 'live_grep'
["ctrl-i"] = { actions.grep_lgrep }
},
no_header = false, -- hide grep|cwd header?
no_header_i = false, -- hide interactive header?
},
btags = {
prompt = 'BTags ',

@ -530,4 +530,27 @@ M.arg_del = function(selected, opts)
M.vimcmd_file(vimcmd, selected, opts)
end
M.grep_lgrep = function(_, opts)
-- 'FNCREF' is set only on 'M.live_grep' calls
-- 'MODULE' is set on 'M.grep' and 'live_grep' calls
assert(opts.__MODULE__
and type(opts.__MODULE__.grep) == 'function'
or type(opts.__MODULE__.live_grep) == 'function')
local o = vim.tbl_extend("keep", {
search = false,
continue_last_search = true,
continue_last_search_default = '',
}, opts.__call_opts or {})
if opts.__FNCREF__ then
opts.__MODULE__.grep(o)
-- require'fzf-lua.actions'.ensure_insert_mode()
else
opts.__MODULE__.live_grep(o)
-- require'fzf-lua.actions'.ensure_insert_mode()
end
end
return M

@ -272,6 +272,9 @@ M.globals.grep = {
grep_opts = "--binary-files=without-match --line-number --recursive --color=auto --perl-regexp",
rg_opts = "--column --line-number --no-heading --color=always --smart-case --max-columns=512",
_actions = function() return M.globals.actions.files end,
actions = {
["ctrl-i"] = { actions.grep_lgrep }
},
-- live_grep_glob options
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-", -- query separator pattern (lua): ' --'
@ -385,6 +388,9 @@ M.globals.tags = {
git_icons = true,
color_icons = true,
_actions = function() return M.globals.actions.files end,
actions = {
["ctrl-i"] = { actions.grep_lgrep }
},
}
M.globals.btags = {
previewer = { _ctor = previewers.builtin.tags },
@ -626,6 +632,10 @@ end
function M.normalize_opts(opts, defaults)
if not opts then opts = {} end
-- save the user's call parameters separately
-- we reuse those with 'actions.grep_lgrep'
opts.__call_opts = opts.__call_opts or utils.deepcopy(opts)
-- opts can also be a function that returns an opts table
if type(opts) == 'function' then
opts = opts()
@ -814,6 +824,7 @@ M._action_to_helpstr = {
[actions.git_buf_vsplit] = "git-buffer-vsplit",
[actions.arg_add] = "arg-list-add",
[actions.arg_del] = "arg-list-delete",
[actions.grep_lgrep] = "grep<->lgrep",
}
return M

@ -69,33 +69,35 @@ M.fzf = function(opts, contents)
-- providers
config.__resume_data.last_query = nil
end
if opts.global_resume_query then
-- We use this option to print the query on line 1
-- later to be removed from the result by M.fzf()
-- this providers a solution for saving the query
-- when the user pressed a valid bind but not when
-- aborting with <C-c> or <Esc>, see next comment
opts.fzf_opts['--print-query'] = ''
-- Signals to the win object resume is enabled
-- so we can setup the keypress event monitoring
-- since we already have the query on valid
-- exit codes we only need to monitor <C-c>, <Esc>
opts.fn_save_query = function(query)
config.__resume_data.last_query = query and #query>0 and query or nil
end
-- 'au InsertCharPre' would be the best option here
-- but it does not work for terminals:
-- https://github.com/neovim/neovim/issues/5018
-- this is causing lag when typing too fast (#271)
-- also not possible with skim (no 'change' event)
--[[ if not opts._is_skim then
local raw_act = shell.raw_action(function(args)
opts.fn_save_query(args[1])
end, "{q}")
opts._fzf_cli_args = ('--bind=change:execute-silent:%s'):
format(vim.fn.shellescape(raw_act))
end ]]
end
if opts.save_query or
opts.global_resume and opts.global_resume_query then
-- We use this option to print the query on line 1
-- later to be removed from the result by M.fzf()
-- this providers a solution for saving the query
-- when the user pressed a valid bind but not when
-- aborting with <C-c> or <Esc>, see next comment
opts.fzf_opts['--print-query'] = ''
-- Signals to the win object resume is enabled
-- so we can setup the keypress event monitoring
-- since we already have the query on valid
-- exit codes we only need to monitor <C-c>, <Esc>
opts.fn_save_query = function(query)
config.__resume_data.last_query = query and #query>0 and query or nil
end
-- 'au InsertCharPre' would be the best option here
-- but it does not work for terminals:
-- https://github.com/neovim/neovim/issues/5018
-- this is causing lag when typing too fast (#271)
-- also not possible with skim (no 'change' event)
--[[ if not opts._is_skim then
local raw_act = shell.raw_action(function(args)
opts.fn_save_query(args[1])
end, "{q}")
opts._fzf_cli_args = ('--bind=change:execute-silent:%s'):
format(vim.fn.shellescape(raw_act))
end ]]
end
-- setup the fzf window and preview layout
local fzf_win = win(opts)
@ -152,7 +154,7 @@ M.fzf = function(opts, contents)
fzf_win:create()
-- save the normalized winopts, otherwise we
-- lose overrides by 'winopts_fn|winopts_raw'
opts.winopts = fzf_win.winopts
opts.winopts.preview = fzf_win.winopts.preview
local selected, exit_code = fzf.raw_fzf(contents, M.build_fzf_cli(opts),
{ fzf_binary = opts.fzf_bin, fzf_cwd = opts.cwd })
-- This was added by 'resume':
@ -162,6 +164,10 @@ M.fzf = function(opts, contents)
if selected and #selected>0 and
opts.fzf_opts['--print-query'] ~= nil then
if opts.fn_save_query then
-- reminder: this doesn't get called with 'live_grep' when using skim
-- due to a bug where '--print-query --interactive' combo is broken:
-- skim always prints an emtpy line where the typed query should be
-- see addtional note above 'opts.save_query' inside 'live_grep_mt'
opts.fn_save_query(selected[1])
end
table.remove(selected, 1)
@ -337,6 +343,7 @@ M.mt_cmd_wrapper = function(opts)
"color_icons",
"strip_cwd_prefix",
"rg_glob",
"__module__",
}
-- caller reqested rg with glob support
if o.rg_glob then
@ -446,30 +453,42 @@ M.set_fzf_field_index = function(opts, default_idx, default_expr)
return opts
end
M.set_header = function(opts, type)
M.set_header = function(opts, flags)
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
if not opts.grep_header then opts.grep_header = "Grep string:" end
if not opts.cwd and opts.show_cwd_header then opts.cwd = vim.loop.cwd() end
local header_str
local cwd_str =
opts.cwd and (opts.show_cwd_header ~= false) and
(opts.show_cwd_header or opts.cwd ~= vim.loop.cwd()) and
("%s %s"):format(opts.cwd_header, opts.cwd:gsub("^"..vim.env.HOME, "~"))
("%s %s"):format(opts.cwd_header,
utils.ansi_codes.red(opts.cwd:gsub("^"..vim.env.HOME, "~")))
local search_str = opts.search and #opts.search > 0 and
("%s %s"):format(opts.search_header, opts.search)
("%s %s"):format(opts.grep_header, utils.ansi_codes.red(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 ''
if flags == 1 then header_str = search_str or ''
elseif flags == 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 .. ", "
header_str = ("%s%s%s"):format(
cwd_str and cwd_str or '',
cwd_str and search_str and ', ' or '',
search_str and search_str or '')
end
-- check for 'actions.grep_lgrep' and "ineteractive" header
if not opts.no_header_i then
for k, v in pairs(opts.actions) do
if type(v) == 'table' and v[1] == actions.grep_lgrep then
local to = opts.__FNCREF__ and 'Grep' or 'Live Grep'
header_str = (':: <%s> to %s%s'):format(
utils.ansi_codes.yellow(k),
utils.ansi_codes.red(to),
header_str and #header_str>0 and ", "..header_str or '')
end
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'] = libuv.shellescape(header_str)
@ -573,12 +592,12 @@ 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_opts['--prompt'] = '*' .. opts.prompt
opts.fzf_opts['--cmd-prompt'] = vim.fn.shellescape(opts.prompt)
opts.fzf_opts['--prompt'] = opts.prompt:match("[^%*]+")
opts.fzf_opts['--cmd-prompt'] = libuv.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_opts['--cmd-query'] = libuv.shellescape(utils.sk_escape(query))
opts._fzf_cli_args = string.format( "-i -c %s", act_cmd)
else
-- fzf already adds single quotes
@ -586,10 +605,10 @@ M.set_fzf_interactive = function(opts, act_cmd, placeholder)
opts.fzf_fn = {}
if opts.exec_empty_query or (query and #query>0) then
opts.fzf_fn = act_cmd:gsub(placeholder,
#query>0 and utils.lua_escape(vim.fn.shellescape(query)) or "''")
#query>0 and utils.lua_escape(libuv.shellescape(query)) or "''")
end
opts.fzf_opts['--phony'] = ''
opts.fzf_opts['--query'] = vim.fn.shellescape(query)
opts.fzf_opts['--query'] = libuv.shellescape(query)
opts._fzf_cli_args = string.format('--bind=%s',
vim.fn.shellescape(string.format("change:reload:%s || true", act_cmd)))
end

@ -221,9 +221,11 @@ M.preprocess = function(opts)
-- save our last search argument for resume
if opts.argv_expr and opts.cmd:match(argvz) then
local query = argv(nil, opts.debug)
set_config_section('globals.grep._last_search',
{ query = query, no_esc = true })
set_config_section('__resume_data.last_query', query)
if opts.__module__ then
set_config_section(("globals.%s._last_search"):format(opts.__module__),
{ query = query, no_esc = true })
end
end
-- did the caller request rg with glob support?

@ -4,12 +4,19 @@ local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local libuv = require "fzf-lua.libuv"
local function get_last_search()
local function get_last_search(opts)
if opts.__MODULE__ and opts.__MODULE__.get_last_search then
return opts.__MODULE__.get_last_search(opts)
end
local last_search = config.globals.grep._last_search or {}
return last_search.query, last_search.no_esc
end
local function set_last_search(query, no_esc)
local function set_last_search(opts, query, no_esc)
if opts.__MODULE__ and opts.__MODULE__.set_last_search then
opts.__MODULE__.set_last_search(opts, query, no_esc)
return
end
config.globals.grep._last_search = {
query = query,
no_esc = no_esc
@ -19,6 +26,11 @@ local function set_last_search(query, no_esc)
end
end
local function set_live_grep_prompt(prompt)
-- prefix all live_grep prompts with an asterisk
return prompt:match("^%*") and prompt or '*'..prompt
end
local M = {}
local get_grep_cmd = function(opts, search_query, no_esc)
@ -72,9 +84,13 @@ M.grep = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
-- we need this for 'actions.grep_lgrep'
opts.__MODULE__ = opts.__MODULE__ or M
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
opts.search, no_esc = get_last_search()
opts.search, no_esc = get_last_search(opts)
opts.search = opts.search or opts.continue_last_search_default
end
-- if user did not provide a search term
@ -93,7 +109,7 @@ M.grep = function(opts)
-- save the search query so the use can
-- call the same search again
set_last_search(opts.search, no_esc or opts.no_esc)
set_last_search(opts, opts.search, no_esc or opts.no_esc)
opts.cmd = get_grep_cmd(opts, opts.search, no_esc)
local contents = core.mt_cmd_wrapper(opts)
@ -115,18 +131,22 @@ M.live_grep_st = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
-- we need this for 'actions.grep_lgrep'
opts.__MODULE__ = opts.__MODULE__ or M
opts.prompt = set_live_grep_prompt(opts.prompt)
assert(not opts.multiprocess)
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
opts.search, no_esc = get_last_search()
opts.search, no_esc = get_last_search(opts)
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
set_last_search(opts.search, true)
set_last_search(opts, opts.search, true)
-- escape unless the user requested not to
if not (no_esc or opts.no_esc) then
opts.query = utils.rg_escape(opts.search)
@ -138,7 +158,7 @@ M.live_grep_st = function(opts)
opts._reload_command = function(query)
if query and not (opts.save_last_search == false) then
set_last_search(query, true)
set_last_search(opts, query, true)
end
-- can be nill when called as fzf initial command
query = query or ''
@ -156,6 +176,19 @@ M.live_grep_st = function(opts)
end
end
-- see notes for this section in 'live_grep_mt'
if not opts._is_skim then
opts.save_query = true
opts.fn_post_fzf = function(o, _)
local last_search, _ = get_last_search(o)
local last_query = config.__resume_data and config.__resume_data.last_query
if not opts.exec_empty_query
and last_search ~= last_query then
set_last_search(opts, last_query or '')
end
end
end
-- disable global resume
-- conflicts with 'change:reload' event
opts.global_resume_query = false
@ -172,18 +205,23 @@ M.live_grep_mt = function(opts)
opts = config.normalize_opts(opts, config.globals.grep)
if not opts then return end
-- we need this for 'actions.grep_lgrep'
opts.__MODULE__ = opts.__MODULE__ or M
opts.__module__ = opts.__module__ or 'grep'
opts.prompt = set_live_grep_prompt(opts.prompt)
assert(opts.multiprocess)
local no_esc = false
if opts.continue_last_search or opts.repeat_last_search then
opts.search, no_esc = get_last_search()
opts.search, no_esc = get_last_search(opts)
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
set_last_search(opts.search, no_esc or opts.no_esc)
set_last_search(opts, opts.search, no_esc or opts.no_esc)
-- escape unless the user requested not to
if not (no_esc or opts.no_esc) then
query = utils.rg_escape(opts.search)
@ -226,7 +264,7 @@ M.live_grep_mt = function(opts)
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['--prompt'] = opts.prompt:match("[^%*]+")
opts.fzf_opts['--cmd-prompt'] = vim.fn.shellescape(opts.prompt)
opts.prompt = nil
-- since we surrounded the skim placeholder with quotes
@ -247,6 +285,30 @@ M.live_grep_mt = function(opts)
("%s || true"):format(reload_command))))
end
-- when running 'live_grep' with 'exec_empty_query=false' (default)
-- an empty typed query will not be saved as the 'neovim --headless'
-- command isn't executed resulting in '_last_search.query' never
-- cleared, always having a minimum of one characer.
-- this signals 'core.fzf' to add the '--print-query' flag and
-- handle the typed query on process exit using 'opts.fn_save_query'
-- due to a skim bug this doesn't work when used in conjucntion with
-- the '--interactive' flag, the line with the typed query is printed
-- to stdout but is always empty
-- to understand this issue, run 'live_grep', type a query and then
-- delete it and press <C-i> to switch to 'grep', instead of an empty
-- search the last typed character will be used as the search string
if not opts._is_skim then
opts.save_query = true
opts.fn_post_fzf = function(o, _)
local last_search, _ = get_last_search(o)
local last_query = config.__resume_data and config.__resume_data.last_query
if not opts.exec_empty_query
and last_search ~= last_query then
set_last_search(opts, last_query or '')
end
end
end
-- disable global resume
-- conflicts with 'change:reload' event
opts.global_resume_query = false

@ -7,6 +7,21 @@ local make_entry = require "fzf-lua.make_entry"
local M = {}
function M.get_last_search(_)
local last_search = config.globals.tags._last_search or {}
return last_search.query, last_search.no_esc
end
function M.set_last_search(_, query, no_esc)
config.globals.tags._last_search = {
query = query,
no_esc = no_esc
}
if config.__resume_data then
config.__resume_data.last_query = query
end
end
local function get_tags_cmd(opts, flags)
local query = nil
local cmd = "grep"
@ -27,6 +42,10 @@ end
local function tags(opts)
-- we need this for 'actions.grep_lgrep'
opts.__MODULE__ = opts.__MODULE__ or M
opts.__module__ = opts.__module__ or 'tags'
-- signal actions this is a ctag
opts._ctag = true
opts.ctags_file = opts.ctags_file and vim.fn.expand(opts.ctags_file) or "tags"
@ -63,7 +82,6 @@ local function tags(opts)
if opts.lgrep then
-- live_grep requested by caller ('tags_live_grep')
opts.prompt = opts.prompt:match("^*") and opts.prompt or '*' .. opts.prompt
opts.filename = opts._ctags_file
if opts.multiprocess then
return require'fzf-lua.providers.grep'.live_grep_mt(opts)
@ -76,6 +94,10 @@ local function tags(opts)
end
end
-- save the search query so the use can
-- call the same search again
M.set_last_search(opts, opts.search, opts.no_esc)
opts._curr_file = opts._curr_file and
path.relative(opts._curr_file, opts.cwd or vim.loop.cwd())
opts.cmd = opts.cmd or get_tags_cmd(opts)
@ -105,6 +127,11 @@ end
M.grep = function(opts)
opts = opts or {}
if opts.continue_last_search or opts.repeat_last_search then
opts.search, opts.no_esc = M.get_last_search(opts)
opts.search = opts.search or opts.continue_last_search_default
end
if not opts.search then
opts.search = vim.fn.input(opts.input_prompt or 'Grep For> ')
end

@ -104,7 +104,7 @@ 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

@ -20,7 +20,11 @@ function FzfWin.save_query(key)
if not self then return end
local lines = vim.api.nvim_buf_get_lines(self.fzf_bufnr, 0, 1, false)
if not lines or vim.tbl_isempty(lines) then return end
local query = lines[1]:gsub("^"..self.prompt, ""):match("[^<]+")
-- 'live_grep' prepends an asterisk to the prompt
-- remove '*' from the start of the line & prompt
local query = lines[1]:gsub("^%*+", "")
:gsub("^"..utils.lua_escape(self.prompt:match("[^%*]+")), "")
:match("[^<]+")
-- trim whitespaces at the end
query = query and query:gsub("%s*$", "")
if self.fn_save_query then
@ -1078,13 +1082,16 @@ function FzfWin.toggle_help()
if k == 'default' then k = 'enter' end
if type(v) =='table' then
v = config.get_action_helpstr(v[1]) or v
else
elseif v then
v = config.get_action_helpstr(v) or v
end
table.insert(keymaps,
format_bind('action', k,
("%s"):format(v):gsub(" ", ""),
opts.mode_width, opts.keybind_width, opts.name_width))
if v then
-- skips 'v == false'
table.insert(keymaps,
format_bind('action', k,
("%s"):format(v):gsub(" ", ""),
opts.mode_width, opts.keybind_width, opts.name_width))
end
end
end

Loading…
Cancel
Save