diff --git a/README.md b/README.md index ecc4f76a..2aeaad81 100644 --- a/README.md +++ b/README.md @@ -803,6 +803,9 @@ require'fzf-lua'.setup { file_icons = true, -- show file icons? color_icons = true, -- colorize file|git icons -- path_shorten = 1, -- 'true' or number, shorten path? + -- Uncomment for custom vscode-like formatter where the filename is first: + -- e.g. "fzf-lua/previewer/fzf.lua" => "fzf.lua previewer/fzf-lua" + -- formatter = "path.filename_first", -- executed command priority is 'cmd' (if exists) -- otherwise auto-detect prioritizes `fd`:`rg`:`find` -- default options are controlled by 'fd|rg|find|_opts' diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 293ef951..8a4fe063 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -209,6 +209,22 @@ function M.normalize_opts(opts, globals, __resume_key) type(M.globals[k]) == "table" and utils.tbl_deep_clone(M.globals[k]) or {}) end + -- Setup formatter options + if opts.formatter then + local _fmt = M.globals["formatters." .. opts.formatter] + if _fmt then + opts._fmt = opts._fmt or {} + opts._fmt.to = opts._fmt.to or _fmt.to + opts._fmt.from = opts._fmt.to or _fmt.from + -- no support for `bat_native` with a formatter + if opts.previewer == "bat_native" then opts.previewer = "bat" end + -- no support of searching file begin (we can't guarantee no. of nbsp's) + opts._fzf_nth_devicons = false + else + utils.warn(("Invalid formatter '%s', ignoring."):format(opts.formatter)) + end + end + -- backward compat: no-value flags should be set to `true`, in the past these -- would be set to an empty string which would now translate into a shell escaped -- string as we automatically shell escape all fzf_opts diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index 6ec3667a..1fbc5e85 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -19,7 +19,7 @@ M.ACTION_DEFINITIONS = { [actions.toggle_ignore] = { function(o) local flag = o.toggle_ignore_flag or "--no-ignore" - if o.cmd:match(utils.lua_regex_escape(flag)) then + if o.cmd and o.cmd:match(utils.lua_regex_escape(flag)) then return "Respect .gitignore" else return "Disable .gitignore" @@ -660,6 +660,7 @@ M.mt_cmd_wrapper = function(opts) "stdout", "stderr", "stderr_to_stdout", + "formatter", "git_dir", "git_worktree", "git_icons", @@ -714,7 +715,9 @@ M.mt_cmd_wrapper = function(opts) and not opts.git_icons and not opts.file_icons and not opts.file_ignore_patterns - and not opts.path_shorten then + and not opts.path_shorten + and not opts.formatter + then -- command does not require any processing, we also reset `argv_expr` -- to keep `setup_fzf_interactive_flags::no_query_condi` in the command opts.argv_expr = nil diff --git a/lua/fzf-lua/defaults.lua b/lua/fzf-lua/defaults.lua index 848b2441..7600bab4 100644 --- a/lua/fzf-lua/defaults.lua +++ b/lua/fzf-lua/defaults.lua @@ -193,6 +193,35 @@ M.defaults = { pager = M._preview_pager_fn, }, }, + formatters = { + path = { + filename_first = { + -- NOT NEEDED: for perf reasons we format at the source in `make_entry.file` + -- to = function(s, _) + -- end, + from = function(s, o) + -- Which part should contain the parent folder? + -- files that are directly under cwd it will have no parent folder part + local parent_idx = (o._parent_idx or 2) + + (o.file_icons and 1 or 0) + + (o.git_icons and 1 or 0) + local parts = utils.strsplit(s, utils.nbsp) + if #parts == parent_idx then + local last = parts[parent_idx] + local filename = parts[parent_idx - 1] + local parent = last:match("^[^:]+") + local fullpath = path.join({ parent, filename }) + -- remove the last part (parent + rest of line) + table.remove(parts, parent_idx) + -- overwrite last part with restored fullpath + rest of line + parts[#parts] = fullpath .. last:sub(#parent + 1) + s = table.concat(parts, utils.nbsp) + end + return s + end + } + } + }, } M.defaults.files = { diff --git a/lua/fzf-lua/make_entry.lua b/lua/fzf-lua/make_entry.lua index c01b40a6..fff827ac 100644 --- a/lua/fzf-lua/make_entry.lua +++ b/lua/fzf-lua/make_entry.lua @@ -331,7 +331,7 @@ M.file = function(x, opts) if opts.git_icons then local diff_info = opts.diff_files and opts.diff_files[utils._if_win(path.normalize(origpath), origpath)] - local indicators = diff_info and diff_info[1] or utils.nbsp + local indicators = diff_info and diff_info[1] or " " for i = 1, #indicators do icon = indicators:sub(i, i) local git_icon = config.globals.git.icons[icon] @@ -355,10 +355,27 @@ M.file = function(x, opts) ret[#ret + 1] = icon ret[#ret + 1] = utils.nbsp end - ret[#ret + 1] = file_is_ansi > 0 - -- filename is ansi escape colored, replace the inner string (#819) - and file_part:gsub(utils.lua_regex_escape(stripped_filepath), filepath) - or filepath + -- convert to number type for faster condition testing next entry + if opts.formatter == "path.filename_first" then + opts.formatter = 1 + end + if tonumber(opts.formatter) == 1 then + -- skip ansi coloring restoration as we use our own hlgroup for the parent + local parent = path.parent(filepath) + if parent then + ret[#ret + 1] = path.tail(filepath) + ret[#ret + 1] = utils.nbsp + ret[#ret + 1] = utils.ansi_codes.grey(path.remove_trailing(parent)) + -- ret[#ret + 1] = path.remove_trailing(parent) + else + ret[#ret + 1] = filepath + end + else + ret[#ret + 1] = file_is_ansi > 0 + -- filename is ansi escape colored, replace the inner string (#819) + and file_part:gsub(utils.lua_regex_escape(stripped_filepath), filepath) + or filepath + end ret[#ret + 1] = rest_of_line return table.concat(ret) end diff --git a/lua/fzf-lua/providers/git.lua b/lua/fzf-lua/providers/git.lua index f49e7675..a48ec5c5 100644 --- a/lua/fzf-lua/providers/git.lua +++ b/lua/fzf-lua/providers/git.lua @@ -44,6 +44,9 @@ M.status = function(opts) -- as part of our `git status -s` opts.git_icons = false + -- set parent_idx base for `formatter=path.filename_first` + opts._parent_idx = 5 + -- we always require processing (can't send the raw command to fzf) opts.requires_processing = true diff --git a/lua/fzf-lua/providers/lsp.lua b/lua/fzf-lua/providers/lsp.lua index d407f1c3..825d5a64 100644 --- a/lua/fzf-lua/providers/lsp.lua +++ b/lua/fzf-lua/providers/lsp.lua @@ -665,6 +665,8 @@ M.finder = function(opts) return end opts = core.set_fzf_field_index(opts) + -- set parent_idx base for `formatter=path.filename_first` + opts._parent_idx = 3 return core.fzf_exec(contents, opts) end