diff --git a/README.md b/README.md index cfc77e20..145f6778 100644 --- a/README.md +++ b/README.md @@ -575,6 +575,14 @@ require'fzf-lua'.setup { rg_glob = false, -- default to glob parsing? glob_flag = "--iglob", -- for case sensitive globs use '--glob' glob_separator = "%s%-%-", -- query separator pattern (lua): ' --' + -- advanced usage: for custom argument parsing define + -- 'rg_glob_fn' to return a pair: + -- first returned argument is the new search query + -- second returned argument are addtional rg flags + -- rg_glob_fn = function(opts, query) + -- ... + -- return flags, new_query + -- end, actions = { -- actions inherit from 'actions.files' and merge -- this action toggles between 'grep' and 'live_grep' diff --git a/doc/fzf-lua.txt b/doc/fzf-lua.txt index 8eeebf3f..41983c6a 100644 --- a/doc/fzf-lua.txt +++ b/doc/fzf-lua.txt @@ -620,6 +620,14 @@ Consult the list below for available settings: rg_glob = false, -- default to glob parsing? glob_flag = "--iglob", -- for case sensitive globs use '--glob' glob_separator = "%s%-%-", -- query separator pattern (lua): ' --' + -- advanced usage: for custom argument parsing define + -- 'rg_glob_fn' to return a pair: + -- first returned argument is the new search query + -- second returned argument are addtional rg flags + -- rg_glob_fn = function(opts, query) + -- ... + -- return flags, new_query + -- end, actions = { -- actions inherit from 'actions.files' and merge -- this action toggles between 'grep' and 'live_grep' @@ -723,6 +731,8 @@ Consult the list below for available settings: file_icons = true, git_icons = true, color_icons = true, + rg_opts = "--no-heading --color=always", + grep_opts = "--color=auto --perl-regexp", fzf_opts = { ['--delimiter'] = "'[\\]:]'", ["--with-nth"] = '2..', diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 9df2b875..143efe85 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -779,6 +779,21 @@ function M.normalize_opts(opts, defaults) return opts end +M.bytecode = function(s, datatype) + local keys = utils.strsplit(s, '.') + local iter = M + for i=1,#keys do + iter = iter[keys[i]] + if not iter then break end + if i == #keys and type(iter) == datatype then + -- Not sure if second argument 'true' is needed + -- can't find any references for it other than + -- it being used in packer.nvim + return string.dump(iter, true) + end + end +end + M.set_action_helpstr = function(fn, helpstr) assert(type(fn) == 'function') M._action_to_helpstr[fn] = helpstr diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index 4b2e10d0..4e02ef26 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -29,6 +29,7 @@ M.fzf_resume = function(opts) if opts.__FNCREF__ then -- HACK for 'live_grep' and 'lsp_live_workspace_symbols' opts.cmd = nil + opts.search = nil opts.continue_last_search = true opts.__FNCREF__(opts) else diff --git a/lua/fzf-lua/make_entry.lua b/lua/fzf-lua/make_entry.lua index 4c72da26..91b4d6ba 100644 --- a/lua/fzf-lua/make_entry.lua +++ b/lua/fzf-lua/make_entry.lua @@ -19,7 +19,7 @@ M._devicons_path = _G._devicons_path ---@diagnostic disable-next-line: undefined-field M._devicons_setup = _G._devicons_setup -local function load_config_section(s, datatype) +local function load_config_section(s, datatype, optional) if config then local keys = utils.strsplit(s, '.') local iter, sect = config, nil @@ -34,17 +34,28 @@ local function load_config_section(s, datatype) elseif M._fzf_lua_server then -- load config from our running instance local res = nil + local is_bytecode = false + local exec_str, exec_opts = nil, nil + if datatype == 'function' then + is_bytecode = true + exec_opts = { s, datatype } + exec_str = ("return require'fzf-lua'.config.bytecode(...)"):format(s) + else + exec_opts = {} + exec_str = ("return require'fzf-lua'.config.%s"):format(s) + end local ok, errmsg = pcall(function() local chan_id = vim.fn.sockconnect("pipe", M._fzf_lua_server, { rpc = true }) - res = vim.rpcrequest(chan_id, "nvim_exec_lua", ([[ - return require'fzf-lua'.config.%s - ]]):format(s), {}) + res = vim.rpcrequest(chan_id, "nvim_exec_lua", exec_str, exec_opts) vim.fn.chanclose(chan_id) end) - if not ok then + if ok and is_bytecode then + ok, res = pcall(loadstring, res) + end + if not ok and not optional then io.stderr:write(("Error loading remote config section '%s': %s\n") :format(s, errmsg)) - elseif type(res) == datatype then + elseif ok and type(res) == datatype then return res end end @@ -141,11 +152,12 @@ end pcall(load_devicons) if not config then - local _config = { globals = { git = {}, files = {} } } + local _config = { globals = { git = {}, files = {}, grep = {} } } _config.globals.git.icons = load_config_section('globals.git.icons', 'table') or {} _config.globals.file_icon_colors = load_config_section('globals.file_icon_colors', 'table') or {} _config.globals.file_icon_padding = load_config_section('globals.file_icon_padding', 'string') _config.globals.files.git_status_cmd = load_config_section('globals.files.git_status_cmd', 'table') + _config.globals.grep.rg_glob_fn = load_config_section('globals.grep.rg_glob_fn', 'function', true) _config.globals.nbsp = load_config_section('globals.nbsp', 'string') if _config.globals.nbsp then utils.nbsp = _config.globals.nbsp end @@ -198,10 +210,13 @@ M.glob_parse = function(opts, query) if not query or not query:find(opts.glob_separator) then return query, nil end - local glob_args = "" + if config.globals.grep.rg_glob_fn then + return config.globals.grep.rg_glob_fn(opts, query) + end + local glob_args = nil local search_query, glob_str = query:match("(.*)"..opts.glob_separator.."(.*)") for _, s in ipairs(utils.strsplit(glob_str, "%s")) do - glob_args = glob_args .. ("%s %s ") + glob_args = (glob_args or "") .. ("%s %s ") :format(opts.glob_flag, vim.fn.shellescape(s)) end return search_query, glob_args @@ -230,9 +245,10 @@ M.preprocess = function(opts) -- live_grep replace pattern with last argument local argvz = "{argvz}" + local has_argvz = opts.cmd:match(argvz) -- save our last search argument for resume - if opts.argv_expr and opts.cmd:match(argvz) then + if opts.argv_expr and has_argvz then local query = argv(nil, opts.debug) set_config_section('__resume_data.last_query', query) if opts.__module__ then @@ -243,7 +259,7 @@ M.preprocess = function(opts) -- did the caller request rg with glob support? -- mannipulation needs to be done before the argv hack - if opts.rg_glob then + if opts.rg_glob and has_argvz then local query = argv() local search_query, glob_args = M.glob_parse(opts, query) if glob_args then diff --git a/lua/fzf-lua/providers/grep.lua b/lua/fzf-lua/providers/grep.lua index f6c8c7b5..caf0d7ec 100644 --- a/lua/fzf-lua/providers/grep.lua +++ b/lua/fzf-lua/providers/grep.lua @@ -138,7 +138,11 @@ M.grep = function(opts) -- can call the same search again set_last_search(opts, opts.search, no_esc or opts.no_esc) - local contents = core.mt_cmd_wrapper(opts) + local contents = core.mt_cmd_wrapper(vim.tbl_deep_extend("force", opts, + -- query was already parsed for globs inside 'get_grep_cmd' + -- no need for our external headless instance to parse again + { rg_glob = false })) + -- by redirecting the error stream to stdout -- we make sure a clear error message is displayed -- when the user enters bad regex expressions @@ -247,6 +251,14 @@ M.live_grep_mt = function(opts) opts.__module__ = opts.__module__ or 'grep' opts.prompt = set_live_grep_prompt(opts.prompt) + -- when using glob parsing we must use the external + -- headless instance for processing the query, this + -- prevents 'file|git_icons=false' from overriding + -- processing inside 'core.mt_cmd_wrapper' + if opts.rg_glob then + opts.requires_processing = true + end + assert(opts.multiprocess) local no_esc = false @@ -381,7 +393,6 @@ M.live_grep_glob_mt = function(opts) -- 'make_entry.preprocess', only supported with multiprocess opts = opts or {} opts.rg_glob = true - opts.requires_processing = true return M.live_grep_mt(opts) end