Initial commit

main
ibhagwan 3 years ago committed by bhagwan
commit 36d850b29b

@ -0,0 +1,2 @@
# fzf-lua
Improved fzf.vim written in lua

@ -0,0 +1,144 @@
local M = {}
-- return fzf '--expect=' string from actions keyval tbl
M.expect = function(actions)
if not actions then return '' end
local keys = {}
for k, v in pairs(actions) do
if k ~= "default" and v ~= false then
table.insert(keys, k)
end
end
if #keys > 0 then
return table.concat(keys, ',')
end
return nil
end
M.act = function(actions, action, selected)
if not actions or not action then return end
if #action == 0 then action = "default" end
if actions[action] then
actions[action](selected)
end
end
M.vimcmd = function(vimcmd, selected)
if not selected or #selected < 2 then return end
for i = 2, #selected do
vim.cmd(vimcmd .. " " .. vim.fn.fnameescape(selected[i]))
end
end
M.vimcmd_file = function(vimcmd, selected)
if not selected or #selected < 2 then return end
for i = 2, #selected do
-- check if the file contains line
local file, line = selected[i]:match("^([^ :]+):(%d+)")
if file and line then
vim.cmd(string.format("%s +%s %s", vimcmd, line, vim.fn.fnameescape(file)))
else
vim.cmd(vimcmd .. " " .. vim.fn.fnameescape(selected[i]))
end
end
end
-- file actions
M.file_edit = function(selected)
local vimcmd = "e"
M.vimcmd_file(vimcmd, selected)
end
M.file_split = function(selected)
local vimcmd = "new"
M.vimcmd_file(vimcmd, selected)
end
M.file_vsplit = function(selected)
local vimcmd = "vnew"
M.vimcmd_file(vimcmd, selected)
end
M.file_tabedit = function(selected)
local vimcmd = "tanbew"
M.vimcmd_file(vimcmd, selected)
end
M.file_sel_to_qf = function(selected)
if not selected or #selected < 2 then return end
local qf_list = {}
for i = 2, #selected do
-- check if the file contains line
local file, line, col, text = selected[i]:match("^([^ :]+):(%d+):(%d+):(.*)")
if file and line and col then
table.insert(qf_list, {filename = file, lnum = line, col = col, text = text})
else
table.insert(qf_list, {filename = selected[i], lnum = 1, col = 1})
end
end
vim.fn.setqflist(qf_list)
vim.cmd 'copen'
end
-- buffer actions
M.buf_edit = function(selected)
local vimcmd = "b"
M.vimcmd(vimcmd, selected)
end
M.buf_split = function(selected)
local vimcmd = "split | b"
M.vimcmd(vimcmd, selected)
end
M.buf_vsplit = function(selected)
local vimcmd = "vertical split | b"
M.vimcmd(vimcmd, selected)
end
M.buf_tabedit = function(selected)
local vimcmd = "tab split | b"
M.vimcmd(vimcmd, selected)
end
M.buf_del = function(selected)
local vimcmd = "bd"
M.vimcmd(vimcmd, selected)
end
M.colorscheme = function(selected)
if not selected or #selected < 2 then return end
vim.cmd("colorscheme " .. selected[2])
end
M.help = function(selected)
local vimcmd = "help"
M.vimcmd(vimcmd, selected)
end
M.help_vert = function(selected)
local vimcmd = "vert help"
M.vimcmd(vimcmd, selected)
end
M.help_tab = function(selected)
local vimcmd = "tab help"
M.vimcmd(vimcmd, selected)
end
M.man = function(selected)
local vimcmd = "Man"
M.vimcmd(vimcmd, selected)
end
M.man_vert = function(selected)
local vimcmd = "vert Man"
M.vimcmd(vimcmd, selected)
end
M.man_tab = function(selected)
local vimcmd = "tab Man"
M.vimcmd(vimcmd, selected)
end
return M

@ -0,0 +1,294 @@
local utils = require "fzf-lua.utils"
local actions = require "fzf-lua.actions"
-- Clear the default command or it would interfere with our options
vim.env.FZF_DEFAULT_OPTS = ''
local M = {}
M.win_height = 0.85
M.win_width = 0.80
M.win_row = 0.30
M.win_col = 0.50
M.win_border = true
M.default_prompt = '> '
M.fzf_layout = 'reverse'
M.preview_cmd = nil -- auto detect head|bat
M.preview_border = 'border'
M.preview_wrap = 'nowrap'
M.preview_vertical = 'down:45%'
M.preview_horizontal = 'right:60%'
M.preview_layout = 'flex'
M.flip_columns = 120
M.bat_theme = nil
M.bat_opts = "--italic-text=always --style=numbers,changes --color always"
M.files = {
prompt = '> ',
cmd = nil, -- default: auto detect find|fd
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = true,
git_diff_cmd = "git diff --name-status --relative HEAD",
git_untracked_cmd = "git ls-files --exclude-standard --others",
find_opts = "-type f -printf '%P\n'",
fd_opts =
[[--color never --type f --hidden --follow ]] ..
[[--exclude .git --exclude node_modules --exclude '*.pyc']],
actions = {
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["ctrl-q"] = actions.file_sel_to_qf,
}
}
M.grep = {
prompt = 'Rg> ',
input_prompt = 'Grep For> ',
cmd = nil, -- default: auto detect rg|grep
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = true,
git_diff_cmd = M.files.git_diff_cmd,
git_untracked_cmd = M.files.git_untracked_cmd,
grep_opts = "--line-number --recursive --color=auto",
rg_opts = "--column --line-number --no-heading --color=always --smart-case",
actions = {
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["ctrl-q"] = actions.file_sel_to_qf,
}
}
M.oldfiles = {
prompt = 'History> ',
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = false,
git_diff_cmd = M.files.git_diff_cmd,
git_untracked_cmd = M.files.git_untracked_cmd,
actions = {
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["ctrl-q"] = actions.file_sel_to_qf,
}
}
M.quickfix = {
prompt = 'Quickfix> ',
separator = '',
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = false,
git_diff_cmd = M.files.git_diff_cmd,
git_untracked_cmd = M.files.git_untracked_cmd,
actions = {
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["ctrl-q"] = actions.file_sel_to_qf,
}
}
M.loclist = {
prompt = 'Locations> ',
separator = '',
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = false,
git_diff_cmd = M.files.git_diff_cmd,
git_untracked_cmd = M.files.git_untracked_cmd,
actions = {
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["ctrl-q"] = actions.file_sel_to_qf,
}
}
M.git = {
prompt = 'GitFiles> ',
cmd = "git ls-files --exclude-standard",
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
git_icons = true,
actions = M.files.actions,
}
M.buffers = {
prompt = 'Buffers> ',
file_icons = true and pcall(require, "nvim-web-devicons"),
color_icons = true,
sort_lastused = true,
show_all_buffers = true,
ignore_current_buffer = false,
cwd_only = false,
actions = {
["default"] = actions.buf_edit,
["ctrl-s"] = actions.buf_split,
["ctrl-v"] = actions.buf_vsplit,
["ctrl-t"] = actions.buf_tabedit,
["ctrl-x"] = actions.buf_del,
}
}
M.colorschemes = {
prompt = 'Colorschemes> ',
live_preview = true,
actions = {
["default"] = actions.colorscheme,
},
winopts = {
win_height = 0.55,
win_width = 0.50,
},
}
M.helptags = {
prompt = 'Help> ',
actions = {
["default"] = actions.help,
["ctrl-s"] = actions.help,
["ctrl-v"] = actions.help_vert,
["ctrl-t"] = actions.help_tab,
},
}
M.manpages = {
prompt = 'Man> ',
cmd = "man -k .",
actions = {
["default"] = actions.man,
["ctrl-s"] = actions.man,
["ctrl-v"] = actions.man_vert,
["ctrl-t"] = actions.man_tab,
},
}
-- <F2> toggle preview
-- <F3> toggle preview text wrap
-- <C-f>|<C-b> page down|up
-- <C-d>|<C-u> half page down|up
-- <S-d>|<S-u> preview page down|up
-- <C-a> toggle select-all
-- <C-u> clear query
-- <C-q> send selected to quicfix
-- <A-q> send all to quicfix
M.fzf_binds = {
'f2:toggle-preview',
'f3:toggle-preview-wrap',
'shift-down:preview-page-down',
'shift-up:preview-page-up',
'ctrl-d:half-page-down',
'ctrl-u:half-page-up',
'ctrl-f:page-down',
'ctrl-b:page-up',
'ctrl-a:toggle-all',
'ctrl-u:clear-query',
}
M.file_icon_colors = {
["lua"] = "blue",
["vim"] = "green",
["sh"] = "cyan",
["zsh"] = "cyan",
["bash"] = "cyan",
["py"] = "green",
["md"] = "yellow",
["c"] = "blue",
["cpp"] = "blue",
["h"] = "magenta",
["hpp"] = "magenta",
["js"] = "blue",
["ts"] = "cyan",
["tsx"] = "cyan",
["css"] = "magenta",
["yml"] = "yellow",
["yaml"] = "yellow",
["json"] = "yellow",
["toml"] = "yellow",
["conf"] = "yellow",
["build"] = "red",
["txt"] = "white",
["gif"] = "green",
["jpg"] = "green",
["png"] = "green",
["svg"] = "green",
["sol"] = "red",
["desktop"] = "magenta",
}
M.git_icons = {
["M"] = "M",
["D"] = "D",
["A"] = "A",
["?"] = "?"
}
M.git_icon_colors = {
["M"] = "yellow",
["D"] = "red",
["A"] = "green",
["?"] = "magenta"
}
M.window_on_create = function()
-- Set popup background same as normal windows
vim.cmd("set winhl=Normal:Normal")
end
M.winopts = function(opts)
opts = M.getopts(opts, M, {
"win_height", "win_width",
"win_row", "win_col", "border",
"window_on_create",
})
local height = math.floor(vim.o.lines * opts.win_height)
local width = math.floor(vim.o.columns * opts.win_width)
local row = math.floor((vim.o.lines - height) * opts.win_row)
local col = math.floor((vim.o.columns - width) * opts.win_col)
return {
-- style = 'minimal',
height = height, width = width, row = row, col = col,
border = opts.win_border,
window_on_create = opts.window_on_create
}
end
M.preview_window = function()
local preview_veritcal = string.format('%s:%s:%s', M.preview_border, M.preview_wrap, M.preview_vertical)
local preview_horizontal = string.format('%s:%s:%s', M.preview_border, M.preview_wrap, M.preview_horizontal)
if M.preview_layout == "vertical" then
return preview_veritcal
elseif M.preview_layout == "flex" then
return utils._if(vim.o.columns>M.flip_columns, preview_horizontal, preview_veritcal)
else
return preview_horizontal
end
end
-- called to merge caller opts and default config
-- before calling a provider method
function M.getopts(opts, cfg, keys)
if not opts then opts = {} end
if keys then
for _, k in ipairs(keys) do
if opts[k] == nil then opts[k] = cfg[k] end
end
end
return opts
end
return M

@ -0,0 +1,199 @@
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 = {}
M.get_devicon = function(file, ext)
local icon = nil
if #file > 0 and pcall(require, "nvim-web-devicons") then
icon = require'nvim-web-devicons'.get_icon(file, ext)
end
return utils._if(icon == nil, '', 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(
[[ --layout=%s --bind=%s --prompt=%s]] ..
[[ --preview-window='%s%s' --preview=%s]] ..
[[ --expect=%s --ansi --info=inline]] ..
[[ %s %s]],
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
-- invisible unicode char as icon|git separator
-- this way we can split our string by space
local nbsp = "\u{00a0}"
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_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
icon = get_git_icon(x, 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}", "{}")
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 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

@ -0,0 +1,192 @@
if not pcall(require, "fzf") then
return
end
local fzf = require "fzf"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local M = {}
local getopt = function(opts, key, expected_type, default)
if opts and opts[key] ~= nil then
if type(opts[key]) == expected_type then
return opts[key]
else
utils.info(
string.format("Expected '%s' for config option '%s', got '%s'",
expected_type, key, type(opts[key]))
)
end
elseif default ~= nil then
return default
else
return nil
end
end
local setopt = function(cfg, opts, key, type)
cfg[tostring(key)] = getopt(opts, key, type, cfg[tostring(key)])
end
local setopts = function(cfg, opts, tbl)
for k, v in pairs(tbl) do
setopt(cfg, opts, k, v)
end
end
local setopt_tbl = function(cfg, opts, key)
if opts and opts[key] then
for k, v in pairs(opts[key]) do
if not cfg[key] then cfg[key] = {} end
cfg[key][k] = v
end
end
end
function M.setup(opts)
setopts(config, opts, {
win_height = "number",
win_width = "number",
win_row = "number",
win_col = "number",
win_border = "boolean",
default_prompt = "string",
fzf_layout = "string",
fzf_binds = "table",
preview_cmd = "string",
preview_border = "string",
preview_wrap = "string",
preview_vertical = "string",
preview_horizontal = "string",
preview_layout = "string",
flip_columns = "number",
window_on_create = "function",
bat_theme = "string",
bat_opts = "string",
})
setopts(config.files, opts.files, {
prompt = "string",
cmd = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
fd_opts = "string",
find_opts = "string",
git_diff_cmd = "string",
git_untracked_cmd = "string",
})
setopts(config.grep, opts.grep, {
prompt = "string",
input_prompt = "string",
cmd = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
rg_opts = "string",
grep_opts = "string",
git_diff_cmd = "string",
git_untracked_cmd = "string",
})
setopts(config.oldfiles, opts.oldfiles, {
prompt = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
git_diff_cmd = "string",
git_untracked_cmd = "string",
cwd_only = "boolean",
include_current_session = "boolean",
})
setopts(config.quickfix, opts.quickfix, {
prompt = "string",
cwd = "string",
separator = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
git_diff_cmd = "string",
git_untracked_cmd = "string",
})
setopts(config.loclist, opts.loclist, {
prompt = "string",
cwd = "string",
separator = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
git_diff_cmd = "string",
git_untracked_cmd = "string",
})
setopts(config.git, opts.git, {
prompt = "string",
cmd = "string",
git_icons = "boolean",
file_icons = "boolean",
color_icons = "boolean",
})
setopts(config.buffers, opts.buffers, {
prompt = "string",
git_prompt = "string",
file_icons = "boolean",
color_icons = "boolean",
sort_lastused = "boolean",
show_all_buffers = "boolean",
ignore_current_buffer = "boolean",
cwd_only = "boolean",
})
setopts(config.colorschemes, opts.colorschemes, {
prompt = "string",
live_preview = "boolean",
post_reset_cb = "function",
})
setopts(config.manpages, opts.manpages, {
prompt = "string",
cmd = "string",
})
setopts(config.helptags, opts.helptags, {
prompt = "string",
})
-- table overrides without losing defaults
for _, k in ipairs({
"git", "files", "oldfiles", "buffers",
"grep", "quickfix", "loclist",
"colorschemes", "helptags", "manpages",
}) do
setopt_tbl(config[k], opts[k], "actions")
setopt_tbl(config[k], opts[k], "winopts")
end
setopt_tbl(config, opts, "git_icons")
setopt_tbl(config, opts, "git_icon_colors")
setopt_tbl(config, opts, "file_icon_colors")
-- override the bat preview theme if set by caller
if config.bat_theme and #config.bat_theme > 0 then
vim.env.BAT_THEME = config.bat_theme
end
-- reset default window opts if set by user
fzf.default_window_options = config.winopts()
end
-- we usually send winopts with every fzf.fzf call
-- but set default window options just in case
fzf.default_window_options = config.winopts()
M.fzf_files = require'fzf-lua.core'.fzf_files
M.files = require'fzf-lua.providers.files'.files
M.grep = require'fzf-lua.providers.grep'.grep
M.live_grep = require'fzf-lua.providers.grep'.live_grep
M.grep_last = require'fzf-lua.providers.grep'.grep_last
M.grep_cword = require'fzf-lua.providers.grep'.grep_cword
M.grep_cWORD = require'fzf-lua.providers.grep'.grep_cWORD
M.grep_visual = require'fzf-lua.providers.grep'.grep_visual
M.grep_curbuf = require'fzf-lua.providers.grep'.grep_curbuf
M.git_files = require'fzf-lua.providers.files'.git_files
M.oldfiles = require'fzf-lua.providers.oldfiles'.oldfiles
M.quickfix = require'fzf-lua.providers.quickfix'.quickfix
M.loclist = require'fzf-lua.providers.quickfix'.loclist
M.buffers = require'fzf-lua.providers.buffers'.buffers
M.help_tags = require'fzf-lua.providers.helptags'.helptags
M.man_pages = require'fzf-lua.providers.manpages'.manpages
M.colorschemes = require'fzf-lua.providers.colorschemes'.colorschemes
return M

@ -0,0 +1,99 @@
local M = {}
M.separator = function()
return '/'
end
M.tail = (function()
local os_sep = M.separator()
local match_string = '[^' .. os_sep .. ']*$'
return function(path)
return string.match(path, match_string)
end
end)()
function M.to_matching_str(path)
return path:gsub('(%-)', '(%%-)'):gsub('(%.)', '(%%.)'):gsub('(%_)', '(%%_)')
end
function M.join(paths)
return table.concat(paths, M.separator())
end
function M.split(path)
return path:gmatch('[^'..M.separator()..']+'..M.separator()..'?')
end
---Get the basename of the given path.
---@param path string
---@return string
function M.basename(path)
path = M.remove_trailing(path)
local i = path:match("^.*()" .. M.separator())
if not i then return path end
return path:sub(i + 1, #path)
end
function M.extension(path)
-- path = M.basename(path)
-- return path:match(".+%.(.*)")
-- search for the first dotten string part up to space
-- then match anything after the dot up to ':/\.'
path = path:match("(%.[^ :\t\x1b]+)")
if not path then return path end
return path:match("^.*%.([^ :\\/]+)")
end
---Get the path to the parent directory of the given path. Returns `nil` if the
---path has no parent.
---@param path string
---@param remove_trailing boolean
---@return string|nil
function M.parent(path, remove_trailing)
path = " " .. M.remove_trailing(path)
local i = path:match("^.+()" .. M.separator())
if not i then return nil end
path = path:sub(2, i)
if remove_trailing then
path = M.remove_trailing(path)
end
return path
end
---Get a path relative to another path.
---@param path string
---@param relative_to string
---@return string
function M.relative(path, relative_to)
local p, _ = path:gsub("^" .. M.to_matching_str(M.add_trailing(relative_to)), "")
return p
end
function M.add_trailing(path)
if path:sub(-1) == M.separator() then
return path
end
return path..M.separator()
end
function M.remove_trailing(path)
local p, _ = path:gsub(M.separator()..'$', '')
return p
end
function M.shorten(path, max_length)
if string.len(path) > max_length - 1 then
path = path:sub(string.len(path) - max_length + 1, string.len(path))
local i = path:match("()" .. M.separator())
if not i then
return "" .. path
end
return "" .. path:sub(i, -1)
else
return path
end
end
return M

@ -0,0 +1,148 @@
if not pcall(require, "fzf") then
return
end
local action = require("fzf.actions").action
local core = require "fzf-lua.core"
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 fn, api = vim.fn, vim.api
local M = {}
local function getbufnumber(line)
return tonumber(string.match(line, "%[(%d+)"))
end
local function getfilename(line)
return string.match(line, "%[.*%] (.+)")
end
M.buffers = function(opts)
opts = config.getopts(opts, config.buffers, {
"prompt", "actions", "winopts",
"file_icons", "color_icons", "sort_lastused",
"show_all_buffers", "ignore_current_buffer",
"cwd_only",
})
local act = action(function (items, fzf_lines, _)
-- only preview first item
local item = items[1]
local buf = getbufnumber(item)
if api.nvim_buf_is_loaded(buf) then
return api.nvim_buf_get_lines(buf, 0, fzf_lines, false)
else
local name = getfilename(item)
if fn.filereadable(name) ~= 0 then
return fn.readfile(name, "", fzf_lines)
end
return "UNLOADED: " .. name
end
end)
coroutine.wrap(function ()
local items = {}
local bufnrs = vim.tbl_filter(function(b)
if 1 ~= vim.fn.buflisted(b) then
return false
end
-- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil
if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(b) then
return false
end
if opts.ignore_current_buffer and b == vim.api.nvim_get_current_buf() then
return false
end
if opts.cwd_only and not string.find(vim.api.nvim_buf_get_name(b), vim.loop.cwd(), 1, true) then
return false
end
return true
end, vim.api.nvim_list_bufs())
if not next(bufnrs) then return end
local header_line = false
local buffers = {}
for _, bufnr in ipairs(bufnrs) do
local flag = bufnr == vim.fn.bufnr('') and '%' or (bufnr == vim.fn.bufnr('#') and '#' or ' ')
local element = {
bufnr = bufnr,
flag = flag,
info = vim.fn.getbufinfo(bufnr)[1],
}
if opts.sort_lastused and (flag == "#" or flag == "%") then
if flag == "%" then header_line = true end
local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1)
table.insert(buffers, idx, element)
else
table.insert(buffers, element)
end
end
for _, buf in pairs(buffers) do
-- local hidden = buf.info.hidden == 1 and 'h' or 'a'
local hidden = ''
local readonly = vim.api.nvim_buf_get_option(buf.bufnr, 'readonly') and '=' or ' '
local changed = buf.info.changed == 1 and '+' or ' '
local flags = hidden .. readonly .. changed
local leftbr = utils.ansi_codes.clear('[')
local rightbr = utils.ansi_codes.clear(']')
local bufname = string.format("%s:%s",
utils._if(#buf.info.name>0, path.relative(buf.info.name, vim.loop.cwd()), "[No Name]"),
utils._if(buf.info.lnum>0, buf.info.lnum, ""))
if buf.flag == '%' then
flags = utils.ansi_codes.red(buf.flag) .. flags
bufname = utils.ansi_codes.green(bufname)
leftbr = utils.ansi_codes.green('[')
rightbr = utils.ansi_codes.green(']')
elseif buf.flag == '#' then
flags = utils.ansi_codes.cyan(buf.flag) .. flags
else
flags = " " .. flags
end
local bufnrstr = string.format("%s%s%s", leftbr,
utils.ansi_codes.yellow(string.format(buf.bufnr)), rightbr)
local buficon = ''
if opts.file_icons then
local extension = path.extension(buf.info.name)
buficon = core.get_devicon(buf.info.name, extension)
if opts.color_icons then
buficon = utils.ansi_codes[config.file_icon_colors[extension] or "dark_grey"](buficon) .. " "
end
end
local item_str = string.format("%s%s %s %s%s",
utils._if(buf.bufnr>9, '' , ' '),
bufnrstr, flags, buficon, bufname)
table.insert(items, item_str)
end
local selected = require("fzf").fzf(items,
core.build_fzf_cli({
prompt = opts.prompt,
preview = act,
actions = opts.actions,
cli_args = utils._if(header_line and not opts.ignore_current_buffer,
'--header-lines=1', '')
}),
config.winopts(opts))
if not selected then return end
if #selected > 1 then
for i = 2, #selected do
selected[i] = tostring(getbufnumber(selected[i]))
end
end
actions.act(opts.actions, selected[1], selected)
end)()
end
return M

@ -0,0 +1,64 @@
if not pcall(require, "fzf") then
return
end
local fzf = require "fzf"
local action = require("fzf.actions").action
local core = require "fzf-lua.core"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local actions = require "fzf-lua.actions"
local function get_current_colorscheme()
if vim.g.colors_name then
return vim.g.colors_name
else
return 'default'
end
end
local M = {}
M.colorschemes = function(opts)
opts = config.getopts(opts, config.colorschemes, {
"prompt", "actions", "winopts", "live_preview", "post_reset_cb",
})
coroutine.wrap(function ()
local prev_act = action(function (args)
if opts.live_preview and args then
local colorscheme = args[1]
vim.cmd("colorscheme " .. colorscheme)
end
end)
local current_colorscheme = get_current_colorscheme()
local current_background = vim.o.background
local colors = vim.list_extend(opts.colors or {}, vim.fn.getcompletion('', 'color'))
local selected = fzf.fzf(colors,
core.build_fzf_cli({
prompt = opts.prompt,
preview = prev_act, preview_window = 'right:0',
actions = opts.actions,
nomulti = true,
}),
config.winopts(opts.winopts))
if not selected then
vim.o.background = current_background
vim.cmd("colorscheme " .. current_colorscheme)
vim.o.background = current_background
else
actions.act(opts.actions, selected[1], selected)
end
if opts.post_reset_cb then
opts.post_reset_cb()
end
end)()
end
return M

@ -0,0 +1,66 @@
if not pcall(require, "fzf") then
return
end
-- local fzf = require "fzf"
local fzf_helpers = require("fzf.helpers")
-- 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 M = {}
local get_files_cmd = function(opts)
if opts.cmd and #opts.cmd>0 then
return opts.cmd
end
local command = nil
if vim.fn.executable("fd") == 1 then
if not opts.cwd or #opts.cwd == 0 then
command = string.format('fd %s', opts.fd_opts)
else
command = string.format('fd %s . %s', opts.fd_opts,
vim.fn.shellescape(opts.cwd))
end
else
command = string.format('find %s %s',
utils._if(opts.cwd and #opts.cwd>0, vim.fn.shellescape(opts.cwd), '.'),
opts.find_opts)
end
return command
end
M.files = function(opts)
opts = config.getopts(opts, config.files, {
"cmd", "prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
"fd_opts", "find_opts",
})
local command = get_files_cmd(opts)
opts.fzf_fn = fzf_helpers.cmd_line_transformer(command,
function(x)
return core.make_entry_file(opts, x)
end)
return core.fzf_files(opts)
end
M.git_files = function(opts)
local output = vim.fn.systemlist("git status")
if utils.shell_error() then
utils.info(unpack(output))
return
end
opts = config.getopts(opts, config.git, {
"cmd", "prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
})
-- opts.cmd sets this to "git ls-files"
return M.files(opts)
end
return M

@ -0,0 +1,193 @@
if not pcall(require, "fzf") then
return
end
-- local fzf = require "fzf"
local fzf_helpers = require("fzf.helpers")
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 M = {}
local get_grep_cmd = function(opts)
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
local search_path = ''
if opts.filename and #opts.filename>0 then
search_path = vim.fn.shellescape(opts.filename)
elseif opts.cwd and #opts.cwd>0 then
search_path = vim.fn.shellescape(opts.cwd)
end
return string.format("%s -- %s %s", command,
utils._if(opts.last_search and #opts.last_search>0,
vim.fn.shellescape(opts.last_search), "{q}"),
search_path
)
end
M.grep = function(opts)
opts = config.getopts(opts, config.grep, {
"cmd", "prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
"search", "input_prompt",
"rg_opts", "grep_opts",
})
if opts.repeat_last_search == true then
opts.search = config.grep.last_search
end
-- save the next search as last_search so we
-- let the caller have an option to run the
-- same search again
-- print("1", opts.last_search, opts.search)
if not opts.search or #opts.search == 0 then
config.grep.last_search = vim.fn.input(opts.input_prompt)
else
config.grep.last_search = opts.search
end
opts.last_search = config.grep.last_search
if not opts.last_search or #opts.last_search == 0 then
utils.info("Please provider valid search string")
return
end
local command = get_grep_cmd(opts)
opts.fzf_fn = fzf_helpers.cmd_line_transformer(
command,
function(x)
return core.make_entry_file(opts, x)
end)
--[[ opts.cb_selected = function(_, x)
return x
end ]]
opts.cli_args = "--delimiter='[: ]'"
opts.preview_args = "--highlight-line={3}" -- bat higlight
--[[
# Preview with bat, matching line in the middle of the window below
# the fixed header of the top 3 lines
#
# ~3 Top 3 lines as the fixed header
# +{2} Base scroll offset extracted from the second field
# +3 Extra offset to compensate for the 3-line header
# /2 Put in the middle of the preview area
#
'--preview-window '~3:+{2}+3/2''
]]
opts.preview_offset = "+{3}-/2"
core.fzf_files(opts)
end
M.live_grep = function(opts)
opts = config.getopts(opts, config.grep, {
"cmd", "prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
"search", "input_prompt",
"rg_opts", "grep_opts",
})
-- resetting last_search will return
-- {q} placeholder in our command
opts.last_search = opts.search
local initial_command = get_grep_cmd(opts)
opts.last_search = nil
local reload_command = get_grep_cmd(opts) .. " || true"
--[[ local fzf_binds = utils.tbl_deep_clone(config.fzf_binds)
table.insert(fzf_binds, string.format("change:reload:%s", reload_command))
opts.fzf_binds = vim.fn.shellescape(table.concat(fzf_binds, ',')) ]]
opts.cli_args = "--delimiter='[: ]' " ..
string.format("--phony --query=%s --bind=%s",
utils._if(opts.search and #opts.search>0, opts.search, [['']]),
vim.fn.shellescape(string.format("change:reload:%s", reload_command)))
opts.preview_args = "--highlight-line={3}" -- bat higlight
--[[
# Preview with bat, matching line in the middle of the window below
# the fixed header of the top 3 lines
#
# ~3 Top 3 lines as the fixed header
# +{2} Base scroll offset extracted from the second field
# +3 Extra offset to compensate for the 3-line header
# /2 Put in the middle of the preview area
#
'--preview-window '~3:+{2}+3/2''
]]
opts.preview_offset = "+{3}-/2"
-- TODO:
-- this is not getting called past the initial command
-- until we fix that we cannot use icons as they interfere
-- with the extension parsing
opts.git_icons = false
opts.file_icons = false
opts.filespec = '{1}'
opts.preview_offset = "+{2}-/2"
opts.preview_args = "--highlight-line={2}" -- bat higlight
opts.fzf_fn = fzf_helpers.cmd_line_transformer(
initial_command,
function(x)
return core.make_entry_file(opts, x)
end)
core.fzf_files(opts)
end
M.grep_last = function(opts)
if not opts then opts = {} end
opts.repeat_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_curbuf = function(opts)
if not opts then opts = {} end
opts.rg_opts = config.grep.rg_opts .. " --with-filename"
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

@ -0,0 +1,125 @@
if not pcall(require, "fzf") then
return
end
local fzf = require "fzf"
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 actions = require "fzf-lua.actions"
local M = {}
local fzf_function = function (cb)
local opts = {}
opts.lang = config.helptags.lang or vim.o.helplang
opts.fallback = utils._if(config.helptags.fallback ~= nil, config.helptags.fallback, true)
local langs = vim.split(opts.lang, ',', true)
if opts.fallback and not vim.tbl_contains(langs, 'en') then
table.insert(langs, 'en')
end
local langs_map = {}
for _, lang in ipairs(langs) do
langs_map[lang] = true
end
local tag_files = {}
local function add_tag_file(lang, file)
if langs_map[lang] then
if tag_files[lang] then
table.insert(tag_files[lang], file)
else
tag_files[lang] = {file}
end
end
end
local help_files = {}
local all_files = vim.fn.globpath(vim.o.runtimepath, 'doc/*', 1, 1)
for _, fullpath in ipairs(all_files) do
local file = path.tail(fullpath)
if file == 'tags' then
add_tag_file('en', fullpath)
elseif file:match('^tags%-..$') then
local lang = file:sub(-2)
add_tag_file(lang, fullpath)
else
help_files[file] = fullpath
end
end
local add_tag = function(t, fzf_cb, co)
--[[ local tag = string.format("%-58s\t%s",
utils.ansi_codes.blue(t.name),
utils._if(t.name and #t.name>0, path.basename(t.name), '')) ]]
local tag = utils.ansi_codes.magenta(t.name)
fzf_cb(tag, function()
coroutine.resume(co)
end)
end
coroutine.wrap(function ()
local co = coroutine.running()
local tags_map = {}
local delimiter = string.char(9)
for _, lang in ipairs(langs) do
for _, file in ipairs(tag_files[lang] or {}) do
local lines = vim.split(utils.read_file(file), '\n', true)
for _, line in ipairs(lines) do
-- TODO: also ignore tagComment starting with ';'
if not line:match'^!_TAG_' then
local fields = vim.split(line, delimiter, true)
if #fields == 3 and not tags_map[fields[1]] then
add_tag({
name = fields[1],
filename = help_files[fields[2]],
cmd = fields[3],
lang = lang,
}, cb, co)
tags_map[fields[1]] = true
-- pause here until we call coroutine.resume()
coroutine.yield()
end
end
end
end
end
-- done
-- cb(nil)
end)()
end
M.helptags = function(opts)
opts = config.getopts(opts, config.helptags, {
"prompt", "actions", "winopts",
})
coroutine.wrap(function ()
-- local prev_act = action(function (args) end)
local selected = fzf.fzf(fzf_function,
core.build_fzf_cli({
prompt = opts.prompt,
-- preview = prev_act,
preview_window = 'right:0',
actions = opts.actions,
cli_args = "--nth 1",
nomulti = true,
}),
config.winopts(opts.winopts))
if not selected then return end
actions.act(opts.actions, selected[1], selected)
end)()
end
return M

@ -0,0 +1,63 @@
if not pcall(require, "fzf") then
return
end
local fzf = require "fzf"
local fzf_helpers = require("fzf.helpers")
local core = require "fzf-lua.core"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local actions = require "fzf-lua.actions"
local M = {}
local function getmanpage(line)
-- match until comma or space
return string.match(line, "[^, ]+")
end
M.manpages = function(opts)
opts = config.getopts(opts, config.manpages, {
"prompt", "actions", "winopts", "cmd",
})
coroutine.wrap(function ()
-- local prev_act = action(function (args) end)
local fzf_fn = fzf_helpers.cmd_line_transformer(opts.cmd, function(x)
-- split by first occurence of ' - ' (spaced hyphen)
local man, desc = x:match("^(.-) %- (.*)$")
return string.format("%-45s %s",
utils.ansi_codes.red(man), desc)
end)
local selected = fzf.fzf(fzf_fn,
core.build_fzf_cli({
prompt = opts.prompt,
-- preview = prev_act,
preview_window = 'right:0',
actions = opts.actions,
cli_args = "--tiebreak begin --nth 1,2",
nomulti = true,
}),
config.winopts(opts.winopts))
if not selected then return end
if #selected > 1 then
for i = 2, #selected do
selected[i] = getmanpage(selected[i])
print(selected[i])
end
end
actions.act(opts.actions, selected[1], selected)
end)()
end
return M

@ -0,0 +1,70 @@
if not pcall(require, "fzf") then
return
end
-- local fzf = require "fzf"
local fzf_helpers = require("fzf.helpers")
-- 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 M = {}
M.oldfiles = function(opts)
opts = config.getopts(opts, config.oldfiles, {
"prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
"include_current_session", "cwd_only",
})
local current_buffer = vim.api.nvim_get_current_buf()
local current_file = vim.api.nvim_buf_get_name(current_buffer)
local results = {}
if opts.include_current_session then
for _, buffer in ipairs(vim.split(vim.fn.execute(':buffers! t'), "\n")) do
local match = tonumber(string.match(buffer, '%s*(%d+)'))
if match then
local file = vim.api.nvim_buf_get_name(match)
if vim.loop.fs_stat(file) and match ~= current_buffer then
table.insert(results, file)
end
end
end
end
for _, file in ipairs(vim.v.oldfiles) do
if vim.loop.fs_stat(file) and not vim.tbl_contains(results, file) and file ~= current_file then
table.insert(results, file)
end
end
if opts.cwd_only then
opts.cwd = vim.loop.cwd()
local cwd = opts.cwd
cwd = cwd:gsub([[\]],[[\\]])
results = vim.tbl_filter(function(file)
return vim.fn.matchstrpos(file, cwd)[2] ~= -1
end, results)
end
opts.fzf_fn = function (cb)
for _, x in ipairs(results) do
x = core.make_entry_file(opts, x)
cb(x, function(err)
if err then return end
-- cb(nil) -- to close the pipe to fzf, this removes the loading
-- indicator in fzf
end)
end
end
--[[ opts.cb_selected = function(_, x)
print("o:", x)
end ]]
return core.fzf_files(opts)
end
return M

@ -0,0 +1,89 @@
if not pcall(require, "fzf") then
return
end
-- local fzf = require "fzf"
local fzf_helpers = require("fzf.helpers")
-- 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 M = {}
local quickfix_run = function(opts, cfg, locations)
if not locations then return {} end
local results = {}
for _, entry in ipairs(locations) do
local filename = entry.filename or vim.api.nvim_buf_get_name(entry.bufnr)
table.insert(results, string.format("%s:%s:%s:\t%s",
filename, --utils.ansi_codes.magenta(filename),
utils.ansi_codes.green(tostring(entry.lnum)),
utils.ansi_codes.blue(tostring(entry.col)),
entry.text))
end
opts = config.getopts(opts, cfg, {
"cwd", "prompt", "actions", "winopts",
"file_icons", "color_icons", "git_icons",
"separator"
})
opts.fzf_fn = function (cb)
for _, x in ipairs(results) do
x = core.make_entry_file(opts, x)
cb(x, function(err)
if err then return end
-- cb(nil) -- to close the pipe to fzf, this removes the loading
-- indicator in fzf
end)
end
end
--[[ opts.cb_selected = function(_, x)
return x
end ]]
opts.cli_args = "--delimiter='[: \\t]'"
opts.preview_args = "--highlight-line={3}" -- bat higlight
--[[
# Preview with bat, matching line in the middle of the window below
# the fixed header of the top 3 lines
#
# ~3 Top 3 lines as the fixed header
# +{2} Base scroll offset extracted from the second field
# +3 Extra offset to compensate for the 3-line header
# /2 Put in the middle of the preview area
#
'--preview-window '~3:+{2}+3/2''
]]
opts.preview_offset = "+{3}-/2"
return core.fzf_files(opts)
end
M.quickfix = function(opts)
local locations = vim.fn.getqflist()
if vim.tbl_isempty(locations) then
utils.info("Quickfix list is empty.")
return
end
return quickfix_run(opts, config.quickfix, locations)
end
M.loclist = function(opts)
local locations = vim.fn.getloclist(0)
for _, value in pairs(locations) do
value.filename = vim.api.nvim_buf_get_name(value.bufnr)
end
if vim.tbl_isempty(locations) then
utils.info("Location list is empty.")
return
end
return quickfix_run(opts, config.loclist, locations)
end
return M

@ -0,0 +1,174 @@
-- help to inspect results, e.g.:
-- ':lua _G.dump(vim.fn.getwininfo())'
function _G.dump(...)
local objects = vim.tbl_map(vim.inspect, { ... })
print(unpack(objects))
end
local M = {}
M._if = function(bool, a, b)
if bool then
return a
else
return b
end
end
function M._echo_multiline(msg)
for _, s in ipairs(vim.fn.split(msg, "\n")) do
vim.cmd("echom '" .. s:gsub("'", "''").."'")
end
end
function M.info(msg)
vim.cmd('echohl Directory')
M._echo_multiline("[Fzf-lua] " .. msg)
vim.cmd('echohl None')
end
function M.warn(msg)
vim.cmd('echohl WarningMsg')
M._echo_multiline("[Fzf-lua] " .. msg)
vim.cmd('echohl None')
end
function M.err(msg)
vim.cmd('echohl ErrorMsg')
M._echo_multiline("[Fzf-lua] " .. msg)
vim.cmd('echohl None')
end
function M.shell_error()
return vim.v.shell_error ~= 0
end
function M.is_git_repo()
vim.fn.system("git status")
return M._if(M.shell_error(), false, true)
end
M.read_file = function(filepath)
local fd = vim.loop.fs_open(filepath, "r", 438)
if fd == nil then return '' end
local stat = assert(vim.loop.fs_fstat(fd))
if stat.type ~= 'file' then return '' end
local data = assert(vim.loop.fs_read(fd, stat.size, 0))
assert(vim.loop.fs_close(fd))
return data
end
M.read_file_async = function(filepath, callback)
vim.loop.fs_open(filepath, "r", 438, function(err_open, fd)
if err_open then
print("We tried to open this file but couldn't. We failed with following error message: " .. err_open)
return
end
vim.loop.fs_fstat(fd, function(err_fstat, stat)
assert(not err_fstat, err_fstat)
if stat.type ~= 'file' then return callback('') end
vim.loop.fs_read(fd, stat.size, 0, function(err_read, data)
assert(not err_read, err_read)
vim.loop.fs_close(fd, function(err_close)
assert(not err_close, err_close)
return callback(data)
end)
end)
end)
end)
end
function M.tbl_deep_clone(t)
if not t then return end
local clone = {}
for k, v in pairs(t) do
if type(v) == "table" then
clone[k] = M.tbl_deep_clone(v)
else
clone[k] = v
end
end
return clone
end
function M.tbl_length(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function M.tbl_has(table, key)
return table[key] ~= nil
end
function M.tbl_or(key, tbl1, tbl2)
if tbl1[key] ~= nil then return tbl1[key]
else return tbl2[key] end
end
function M.tbl_concat(...)
local result = {}
local n = 0
for _, t in ipairs({...}) do
for i, v in ipairs(t) do
result[n + i] = v
end
n = n + #t
end
return result
end
function M.tbl_pack(...)
return {n=select('#',...); ...}
end
function M.tbl_unpack(t, i, j)
return unpack(t, i or 1, j or t.n or #t)
end
M.ansi_codes = {}
M.ansi_colors = {
clear = "\x1b[0m",
bold = "\x1b[1m",
black = "\x1b[0;30m",
red = "\x1b[0;31m",
green = "\x1b[0;32m",
yellow = "\x1b[0;33m",
blue = "\x1b[0;34m",
magenta = "\x1b[0;35m",
cyan = "\x1b[0;36m",
grey = "\x1b[0;90m",
dark_grey = "\x1b[0;97m",
white = "\x1b[0;98m",
}
for color, escseq in pairs(M.ansi_colors) do
M.ansi_codes[color] = function(string)
if string == nil or #string == 0 then return '' end
return escseq .. string .. M.ansi_colors.clear
end
end
function M.get_visual_selection()
-- must exit visual mode or program croaks
-- :visual leaves ex-mode back to normal mode
-- use 'gv' to reselect the text
vim.cmd[[visual]]
local _, csrow, cscol, _ = unpack(vim.fn.getpos("'<"))
local _, cerow, cecol, _ = unpack(vim.fn.getpos("'>"))
local lines = vim.fn.getline(csrow, cerow)
-- local n = cerow-csrow+1
local n = M.tbl_length(lines)
if n <= 0 then return '' end
lines[n] = string.sub(lines[n], 1, cecol)
lines[1] = string.sub(lines[1], cscol)
print(n, csrow, cscol, cerow, cecol, table.concat(lines, "\n"))
return table.concat(lines, "\n")
end
return M

@ -0,0 +1,8 @@
if exists('g:loaded_fzf_lua') | finish | endif
if !has('nvim-0.5')
echohl Error
echomsg "Fzf-lua is only available for Neovim versions 0.5 and above"
echohl clear
finish
endif
Loading…
Cancel
Save