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.

220 lines
6.5 KiB
Lua

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

local fzf = require "fzf"
local path = require "fzf-lua.path"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local actions = require "fzf-lua.actions"
local M = {}
-- invisible unicode char as icon|git separator
-- this way we can split our string by space
-- this causes "invalid escape sequence" error
-- local nbsp = "\u{00a0}"
local nbsp = " "
M.get_devicon = function(file, ext)
local icon = ''
if not file or #file == 0 then return icon end
if config._has_devicons then
local devicon = require'nvim-web-devicons'.get_icon(file, ext)
if devicon then icon = devicon end
end
return icon
end
M.preview_cmd = function(opts, cfg)
opts = opts or {}
opts.filespec = opts.filespec or '{}'
opts.preview_cmd = opts.preview_cmd or cfg.preview_cmd
opts.preview_args = opts.preview_args or ''
opts.bat_opts = opts.bat_opts or cfg.bat_opts
local preview = nil
if not opts.cwd then opts.cwd = ''
elseif #opts.cwd > 0 then
opts.cwd = path.add_trailing(opts.cwd)
end
if opts.preview_cmd and #opts.preview_cmd > 0 then
preview = string.format("%s %s -- %s%s", opts.preview_cmd, opts.preview_args, opts.cwd, opts.filespec)
elseif vim.fn.executable("bat") == 1 then
preview = string.format("bat %s %s -- %s%s", opts.bat_opts, opts.preview_args, opts.cwd, opts.filespec)
else
preview = string.format("head -n $FZF_PREVIEW_LINES %s -- %s%s", opts.preview_args, opts.cwd, opts.filespec)
end
if preview ~= nil then
-- We use bash to do math on the environment variable, so
-- let's make sure this command runs in bash
-- preview = "bash -c " .. vim.fn.shellescape(preview)
preview = vim.fn.shellescape(preview)
end
return preview
end
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 ''
local cli = string.format(
[[ %s --layout=%s --bind=%s --prompt=%s]] ..
[[ --preview-window='%s%s' --preview=%s]] ..
[[ --expect=%s --ansi --info=inline]] ..
[[ %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,
vim.fn.shellescape(table.concat(cfg.fzf_binds, ','))),
vim.fn.shellescape(opts.prompt),
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)),
utils._if(opts.actions, actions.expect(opts.actions), 'ctrl-s,ctrl-v,ctrl-t'),
utils._if(opts.nomulti, '--no-multi', '--multi'),
utils._if(opts.cli_args, opts.cli_args, '')
)
-- print(cli)
return cli
end
local get_diff_files = function()
local diff_files = {}
local status = vim.fn.systemlist(config.files.git_diff_cmd)
if not utils.shell_error() then
for i = 1, #status do
local split = vim.split(status[i], " ")
diff_files[split[2]] = split[1]
end
end
return diff_files
end
local get_untracked_files = function()
local untracked_files = {}
local status = vim.fn.systemlist(config.files.git_untracked_cmd)
if vim.v.shell_error == 0 then
for i = 1, #status do
untracked_files[status[i]] = "?"
end
end
return untracked_files
end
local color_icon = function(icon, ext)
if ext then
return utils.ansi_codes[config.file_icon_colors[ext] or "dark_grey"](icon)
else
return utils.ansi_codes[config.git_icon_colors[icon] or "green"](icon)
end
end
local get_git_icon = function(file, diff_files, untracked_files)
if diff_files and diff_files[file] then
return config.git_icons[diff_files[file]]
end
if untracked_files and untracked_files[file] then
return config.git_icons[untracked_files[file]]
end
return nbsp
end
M.make_entry_lcol = function(_, entry)
if not entry then return nil end
local filename = entry.filename or vim.api.nvim_buf_get_name(entry.bufnr)
return string.format("%s:%s:%s:%s%s",
filename, --utils.ansi_codes.magenta(filename),
utils.ansi_codes.green(tostring(entry.lnum)),
utils.ansi_codes.blue(tostring(entry.col)),
utils._if(entry.text and entry.text:find("^\t"), "", "\t"),
entry.text)
end
M.make_entry_file = function(opts, x)
local icon
local prefix = ''
if opts.cwd and #opts.cwd > 0 then
x = path.relative(x, opts.cwd)
end
if opts.file_icons then
local extension = path.extension(x)
icon = M.get_devicon(x, extension)
if opts.color_icons then icon = color_icon(icon, extension) end
prefix = prefix .. icon
end
if opts.git_icons then
local filepath = x:match("^[^:]+")
icon = get_git_icon(filepath, opts.diff_files, opts.untracked_files)
if opts.color_icons then icon = color_icon(icon) end
prefix = prefix .. utils._if(#prefix>0, nbsp, '') .. icon
end
if #prefix > 0 then
x = prefix .. " " .. x
end
return x
end
local function trim_entry(string)
string = string:gsub("^[^ ]* ", "")
return string
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
if opts.cwd and #opts.cwd > 0 then
opts.cwd = vim.fn.expand(opts.cwd)
end
coroutine.wrap(function ()
if opts.git_icons then
opts.diff_files = get_diff_files()
opts.untracked_files = get_untracked_files()
end
local has_prefix = opts.file_icons or opts.git_icons
if not opts.filespec then
opts.filespec = utils._if(has_prefix, "{2}", "{1}")
end
local selected = fzf.fzf(opts.fzf_fn,
M.build_fzf_cli(opts),
config.winopts(opts.winopts))
if not selected then return end
if #selected > 1 then
for i = 2, #selected do
if has_prefix then
selected[i] = trim_entry(selected[i])
end
if opts.cwd and #opts.cwd>0 and
not path.starts_with_separator(selected[i]) then
selected[i] = path.join({opts.cwd, selected[i]})
end
if opts.cb_selected then
local cb_ret = opts.cb_selected(opts, selected[i])
if cb_ret then selected[i] = cb_ret end
end
end
end
-- dump fails after fzf for some odd reason
-- functions are still valid as can seen by pairs
-- _G.dump(opts.actions)
actions.act(opts.actions, selected[1], selected)
end)()
end
return M