251129.19 · 18fe1aa95a5c9da55ac99620cf4ed8a1653c35e3
chore!: fix nil check, param mismatch
reading declarations
@@ -11,8 +11,6 @@ "enable": true, "disable": [ "unnecessary-if",- "param-type-not-match",- "need-check-nil", "await-in-sync", "global-in-non-module" ]
@@ -29,6 +29,7 @@ "different-requires", "unknown-diag-code", "unknown-cast-variable",+ "luadoc-miss-local-name", ], "unusedLocalExclude": ["_*"], },@@ -47,6 +48,9 @@ "checkThirdParty": false, "maxPreload": 2000, "preloadFileSize": 1000,+ "ignoreDir": [+ "lua/fzf-lua/actions.lua"+ ], }, "type": { "weakNilCheck": true,
@@ -48,7 +48,9 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim) -- dependencies = { "nvim-mini/mini.icons" }, ---@module "fzf-lua" ---@type fzf-lua.Config|{}+ ---@diagnostics disable: missing-fields opts = {}+ ---@diagnostics enable: missing-fields } ```
@@ -1,14 +1,21 @@+---@diagnostic disable: need-check-nil ---@diagnostic disable-next-line: deprecated local uv = vim.uv or vim.loop local utils = require "fzf-lua.utils" local path = require "fzf-lua.path" local libuv = require "fzf-lua.libuv" +---@class fzf-lua.actions+---@field expect fun(actions: table, opts: fzf-lua.config.Resolved|{}):string[]?, string[]?+---@field [string] fun(items: string[], opts: fzf-lua.config.Resolved|{}):any local M = {} -- return fzf '--expect=' string from actions keyval tbl -- on fzf >= 0.53 add the `prefix` key to the bind flags -- https://github.com/junegunn/fzf/issues/3829#issuecomment-2143235993+---@param actions table+---@param opts table+---@return string[]?, string[]? M.expect = function(actions, opts) if not actions then return nil end local expect = {}@@ -54,12 +61,15 @@ M.expect = function(actions, opts) return #expect > 0 and expect or nil, #binds > 0 and binds or nil end +---@param selected string[]+---@param opts fzf-lua.config.Resolved|{}+---@return string?, string[]? M.normalize_selected = function(selected, opts) -- The below separates the keybind from the item(s) -- and makes sure 'selected' contains only item(s) or {} -- so it can always be enumerated safely if not selected then return end- local actions = opts.actions+ local actions = assert(opts.actions) -- Backward compat, "default" action trumps "enter" if actions.default then actions.enter = actions.default end if utils.has(opts, "fzf", { 0, 53 }) or utils.has(opts, "sk", { 0, 14 }) then@@ -102,7 +112,7 @@ M.act = function(selected, opts) local actions = opts.actions local keybind, entries = M.normalize_selected(selected, opts) -- fzf >= 0.53 and `--exit-0`- if not keybind then return end+ if not keybind or not entries then return end local action = actions[keybind] -- Backward compat, was action defined as "default" if not action and keybind == "enter" then@@ -121,7 +131,7 @@ M.act = function(selected, opts) end elseif type(action) == "function" then action(entries, opts)- elseif type(action) == "string" then+ elseif type(action) == "string" then ---@cast action string vim.cmd(action) else utils.warn(("unsupported action: '%s', type:%s"):format(keybind, type(action)))@@ -152,7 +162,7 @@ local edit_entry = function(entry, fullpath, will_replace_curbuf, opts) -- We normalize the path or Windows will fail with directories starting -- with special characters, for example "C:\app\(web)" will be translated -- by neovim to "c:\app(web)" (#1082)- local relpath = path.normalize(path.relative_to(fullpath, uv.cwd()))+ local relpath = path.normalize(path.relative_to(fullpath, utils.cwd())) local bufnr0 = vim.fn.bufadd(relpath) if bufnr0 == 0 and not opts.silent then utils.warn("Unable to add buffer %s", relpath)@@ -187,7 +197,7 @@ end ---@param vimcmd string ---@param selected string[]----@param opts fzf-lua.Config+---@param opts fzf-lua.config.Resolved|{} ---@param bufedit boolean? ---@return string? M.vimcmd_entry = function(vimcmd, selected, opts, bufedit)@@ -205,7 +215,7 @@ M.vimcmd_entry = function(vimcmd, selected, opts, bufedit) if not path.is_absolute(fullpath) then -- cwd priority is first user supplied, then original call cwd -- technically we should never get to the `uv.cwd()` fallback- fullpath = path.join({ opts.cwd or opts._cwd or uv.cwd(), fullpath })+ fullpath = path.join({ opts.cwd or opts._cwd or utils.cwd(), fullpath }) end -- Can't be called from term window (for example, "reload" actions) due to -- nvim_exec2(): Vim(normal):Can't re-enter normal mode from terminal mode@@ -243,7 +253,7 @@ M.vimcmd_entry = function(vimcmd, selected, opts, bufedit) return end else- local relpath = path.normalize(path.relative_to(fullpath, uv.cwd()))+ local relpath = path.normalize(path.relative_to(fullpath, utils.cwd())) vim.cmd(("%s %s"):format(vimcmd, relpath)) end -- Reload actions from fzf's (buf/arg del, etc) window end here@@ -263,10 +273,8 @@ M.vimcmd_entry = function(vimcmd, selected, opts, bufedit) elseif not opts.no_action_set_cursor and entry.line > 0 or entry.col > 0 then -- Make sure we have valid line/column -- e.g. qf lists from files (no line/col), dap_breakpoints- pcall(vim.api.nvim_win_set_cursor, 0, {- math.max(1, entry.line),- math.max(1, entry.col) - 1- })+ ---@diagnostic disable-next-line: param-type-mismatch+ pcall(vim.api.nvim_win_set_cursor, 0, { math.max(1, entry.line), math.max(1, entry.col) - 1 }) end -- Only "zz" after the last entry is loaded into the origin buffer if i == #selected and not opts.no_action_zz and not utils.is_term_buffer(0) then@@ -302,7 +310,7 @@ local sel_to_qf = function(selected, opts, is_loclist) local qf_list = {} for i = 1, #selected do local file = path.entry_to_file(selected[i], opts)- local text = file.stripped:match(":%d+:%d?%d?%d?%d?:?(.*)$")+ local text = assert(file.stripped):match(":%d+:%d?%d?%d?%d?:?(.*)$") table.insert(qf_list, { bufnr = file.bufnr, filename = file.bufname or file.path or file.uri,@@ -355,7 +363,7 @@ local sel_to_qf = function(selected, opts, is_loclist) end M.list_del = function(selected, opts)- local winid = opts.__CTX.winid+ local winid = assert(opts.__CTX.winid) local list = opts.is_loclist and vim.fn.getloclist(winid) or vim.fn.getqflist() local buf_del = (function()@@ -400,9 +408,9 @@ end M.file_switch = function(selected, opts) if not selected[1] then return false end -- If called from `:FzfLua tabs` switch to requested tab/win- local tabh, winid = selected[1]:match("(%d+)\t(%d+)%)")- tabh = utils.tointeger(tabh)- winid = utils.tointeger(winid)+ local tabh0, winid0 = selected[1]:match("(%d+)\t(%d+)%)")+ local tabh = utils.tointeger(tabh0)+ local winid = utils.tointeger(winid0) if tabh and winid then vim.api.nvim_set_current_tabpage(tabh) vim.api.nvim_set_current_win(winid)@@ -411,9 +419,9 @@ M.file_switch = function(selected, opts) local entry = path.entry_to_file(selected[1], opts) if not entry.bufnr then -- Search for the current entry's filepath in buffer list- local fullpath = entry.path+ local fullpath = assert(entry.path) if not path.is_absolute(fullpath) then- fullpath = path.join({ opts.cwd or uv.cwd(), fullpath })+ fullpath = path.join({ opts.cwd or utils.cwd(), fullpath }) end for _, b in ipairs(vim.api.nvim_list_bufs()) do local bname = vim.api.nvim_buf_get_name(b)@@ -429,11 +437,8 @@ M.file_switch = function(selected, opts) winid = utils.winid_from_tabh(0, entry.bufnr) if not winid then return false end vim.api.nvim_set_current_win(winid)- if entry.line > 0 or entry.col > 0 then- pcall(vim.api.nvim_win_set_cursor, 0, {- math.max(1, entry.line),- math.max(1, entry.col) - 1- })+ if entry.line > 0 or entry.col > 0 then ---@diagnostic disable-next-line: param-type-mismatch+ pcall(vim.api.nvim_win_set_cursor, 0, { math.max(1, entry.line), math.max(1, entry.col) - 1 }) end if not utils.is_term_buffer(0) and not opts.no_action_zz then vim.cmd("norm! zvzz") end return true@@ -478,7 +483,7 @@ local function arg_exec(cmd, selected, opts) assert(relpath, "entry doesn't contain filepath") if not relpath then return end if path.is_absolute(relpath) then- relpath = path.relative_to(relpath, vim.uv.cwd())+ relpath = path.relative_to(relpath, utils.cwd()) end vim.cmd(cmd .. " " .. string.gsub(relpath, " ", [[\ ]])) end)()@@ -496,7 +501,7 @@ M.arg_del = function(selected, opts) end M.colorscheme = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local dbkey, idx = selected[1]:match("^(.-):(%d+):") if dbkey then opts._apply_awesome_theme(dbkey, idx, opts)@@ -506,6 +511,8 @@ M.colorscheme = function(selected, opts) end end +---@param selected string[]+---@param opts fzf-lua.config.AwesomeColorschemes|{} M.cs_delete = function(selected, opts) for _, s in ipairs(selected) do local dbkey = s:match("^(.-):%d+:")@@ -513,6 +520,8 @@ M.cs_delete = function(selected, opts) end end +---@param selected string[]+---@param opts fzf-lua.config.AwesomeColorschemes|{} M.cs_update = function(selected, opts) local dedup = {} for _, s in ipairs(selected) do@@ -531,20 +540,20 @@ M.toggle_bg = function(_, _) end M.hi = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local hl = selected[1]:match("^[^%s]+") vim.cmd("hi " .. hl) vim.cmd("echo") end M.run_builtin = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local method = selected[1] pcall(require "fzf-lua"[method]) end M.ex_run = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local cmd = selected[1] vim.cmd("stopinsert") vim.fn.feedkeys(string.format(":%s", cmd), "nt")@@ -552,21 +561,21 @@ M.ex_run = function(selected) end M.ex_run_cr = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local cmd = selected[1] vim.cmd(cmd) vim.fn.histadd("cmd", cmd) end M.exec_menu = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local cmd = selected[1] vim.cmd("emenu " .. cmd) end M.search = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local query = selected[1] vim.cmd("stopinsert") vim.fn.feedkeys(@@ -580,9 +589,9 @@ M.search_cr = function(selected, opts) end M.goto_mark = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local mark = selected[1]- mark = mark:match("[^ ]+")+ mark = assert(mark:match("[^ ]+")) vim.cmd("stopinsert") vim.cmd("normal! `" .. mark) -- vim.fn.feedkeys(string.format("'%s", mark))@@ -608,7 +617,7 @@ M.mark_del = function(selected) local buf = utils.CTX().bufnr vim.api.nvim_win_call(win, function() vim.tbl_map(function(s)- local mark = s:match "[^ ]+"+ local mark = assert(s:match("[^ ]+")) local ok, res = pcall(vim.api.nvim_buf_del_mark, buf, mark) if ok and res then return end return vim.cmd.delm(mark)@@ -617,7 +626,7 @@ M.mark_del = function(selected) end M.goto_jump = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end if opts.jump_using_norm then local jump, _, _, _ = selected[1]:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)") if tonumber(jump) then@@ -642,14 +651,17 @@ M.goto_jump = function(selected, opts) end M.keymap_apply = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end -- extract lhs in the keymap. The lhs can't contain a whitespace.- local key = selected[1]:match("[│]%s+([^%s]*)%s+[│]")+ local key = (assert(selected[1]:match("[│]%s+([^%s]*)%s+[│]"), selected[1])) vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, false, true), "t", true) end for _, fname in ipairs({ "edit", "split", "vsplit", "tabedit" }) do+ ---@param selected string[]+ ---@param opts fzf-lua.config.Resolved M["keymap_" .. fname] = function(selected, opts)+ if not selected[1] then return end local entry = path.keymap_to_entry(selected[1], opts) if entry.path then M["file_" .. fname]({ entry.stripped }, opts)@@ -692,7 +704,7 @@ local nvim_opt_edit = function(selected, opts, scope) end end - local parts = vim.split(selected[1], opts.separator)+ local parts = vim.split(selected[1], opts.separator) ---@cast parts [string, string] local option = vim.trim(parts[1]) local old = vim.trim(parts[2]) local info = vim.api.nvim_get_option_info2(option, {})@@ -733,6 +745,7 @@ M.spell_suggest = function(selected, opts) end M.set_filetype = function(selected)+ if not selected[1] then return end vim.bo.filetype = selected[1]:match("[^" .. utils.nbsp .. "]+$") end @@ -758,12 +771,12 @@ local function helptags(s, opts) end M.help = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end vim.cmd("help " .. helptags(selected, opts)[1]) end M.help_curwin = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local helpcmd local is_shown = false local current_win_number = 1@@ -786,12 +799,12 @@ M.help_curwin = function(selected, opts) end M.help_vert = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end vim.cmd("vert help " .. helptags(selected, opts)[1]) end M.help_tab = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end -- vim.cmd("tab help " .. helptags(selected, opts)[1]) utils.with({ go = { splitkeep = "cursor" } }, function() vim.cmd("tabnew | setlocal bufhidden=wipe | help " .. helptags(selected, opts)[1] .. " | only")@@ -803,17 +816,17 @@ local function mantags(s) end M.man = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end vim.cmd("Man " .. mantags(selected)[1]) end M.man_vert = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end vim.cmd("vert Man " .. mantags(selected)[1]) end M.man_tab = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end utils.with({ go = { splitkeep = "cursor" } }, function() vim.cmd("tabnew | setlocal bufhidden=wipe | Man " .. mantags(selected)[1] .. " | only") end)@@ -822,7 +835,6 @@ end M.git_switch = function(selected, opts) if not selected[1] then return end local cmd = path.git_cwd({ "git", "checkout" }, opts)- ---@cast cmd -string local git_ver = utils.git_version() -- git switch was added with git version 2.23 if git_ver and git_ver >= 2.23 then@@ -830,6 +842,7 @@ M.git_switch = function(selected, opts) end -- remove anything past space local marker, branch = selected[1]:match("%s-([%+%*]?)%s+([^ ]+)")+ if not branch then return utils.error("unable to parse branch: '%s'", selected[1]) end -- do nothing for active branch if marker == "*" then utils.warn("already on branch '%s'", branch)@@ -844,6 +857,7 @@ M.git_switch = function(selected, opts) end table.insert(cmd, branch) local output, rc = utils.io_systemlist(cmd)+ assert(output[1]) if marker == "+" then assert(rc ~= 0) -- should err with worktree path local worktree_path = output[1]:match([[([^']+)'$]])@@ -858,6 +872,8 @@ M.git_switch = function(selected, opts) end end +---@param selected string[]+---@param opts fzf-lua.config.GitBranches|{} M.git_branch_add = function(selected, opts) -- "reload" actions (fzf version >= 0.36) use field_index = "{q}" -- so the prompt input will be found in `selected[1]`@@ -867,6 +883,7 @@ M.git_branch_add = function(selected, opts) if type(branch) ~= "string" or #branch == 0 then utils.warn("Branch name cannot be empty, use prompt for input.") else+ assert(opts.cmd_add) local cmd_add_branch = path.git_cwd(opts.cmd_add, opts) table.insert(cmd_add_branch, branch) local output, rc = utils.io_systemlist(cmd_add_branch)@@ -879,8 +896,9 @@ M.git_branch_add = function(selected, opts) end M.git_branch_del = function(selected, opts)- if #selected == 0 then return end- local cmd_del_branch = path.git_cwd(opts.cmd_del, opts)+ if not selected[1] then return end+ ---@diagnostic disable-next-line: param-type-mismatch, assign-type-mismatch+ local cmd_del_branch = path.git_cwd(opts.cmd_del, opts) ---@type string[] local cmd_cur_branch = path.git_cwd({ "git", "rev-parse", "--abbrev-ref", "HEAD" }, opts) local branch = selected[1]:match("^[%*+]*[%s]*[(]?([^%s)]+)") local cur_branch = utils.io_systemlist(cmd_cur_branch)[1]@@ -901,11 +919,11 @@ end M.git_worktree_cd = function(selected, opts) if not selected[1] then return end- local cwd = selected[1]:match("^[^%s]+")+ local cwd = assert(selected[1]:match("^[^%s]+")) if not path.is_absolute(cwd) then- cwd = path.join({ uv.cwd(), cwd })+ cwd = path.join({ utils.cwd(), cwd }) end- if cwd == vim.uv.cwd() then+ if cwd == utils.cwd() then utils.warn(("cwd already set to '%s'"):format(cwd)) return end@@ -921,22 +939,22 @@ end M.git_worktree_add = function(selected, opts) local branch = selected[1] or opts.last_query+ if not branch then return end branch = branch:match("[^%*%+%s]+") if type(branch) ~= "string" or #branch == 0 then utils.warn("Branch name cannot be empty, use prompt for input.") else local worktree_path = path.join({ "..", branch }) + ---@type string[] local cmd_branch_check = path.git_cwd( { "git", "show-ref", "--verify", "--quiet", "refs/heads/" .. branch }, opts) local _, check_rc = utils.io_systemlist(cmd_branch_check) - local cmd_add- if check_rc == 0 then- cmd_add = path.git_cwd({ "git", "worktree", "add", worktree_path, branch }, opts)- else- cmd_add = path.git_cwd({ "git", "worktree", "add", worktree_path, "-b", branch }, opts)- end+ ---@type string[]+ local cmd_add = check_rc == 0+ and path.git_cwd({ "git", "worktree", "add", worktree_path, branch }, opts)+ or path.git_cwd({ "git", "worktree", "add", worktree_path, "-b", branch }, opts) local output, rc = utils.io_systemlist(cmd_add) if rc ~= 0 then@@ -948,15 +966,15 @@ M.git_worktree_add = function(selected, opts) end M.git_worktree_del = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local worktree_path = selected[1]:match("^[^%s]+")- local current_cwd = vim.uv.cwd()- if worktree_path == current_cwd or path.normalize(worktree_path) == path.normalize(current_cwd) then+ local current_cwd = utils.cwd()+ if not worktree_path or worktree_path == current_cwd or path.normalize(worktree_path) == path.normalize(current_cwd) then utils.warn("Cannot delete current worktree '%s'", worktree_path) return end if vim.fn.confirm("Delete worktree " .. worktree_path .. "?", "&Yes\n&No") == 1 then- local cmd_del = path.git_cwd({ "git", "worktree", "remove", worktree_path }, opts)+ local cmd_del = path.git_cwd({ "git", "worktree", "remove", worktree_path }, opts) ---@type string[] local output, rc = utils.io_systemlist(cmd_del) if rc ~= 0 then utils.error(table.concat(output, "\n"))@@ -1018,7 +1036,7 @@ end local git_exec = function(selected, opts, cmd, silent) local success for _, e in ipairs(selected) do- local file = path.relative_to(path.entry_to_file(e, opts).path, opts.cwd)+ local file = path.relative_to(assert(path.entry_to_file(e, opts).path), opts.cwd) local _cmd = vim.deepcopy(cmd) table.insert(_cmd, file) local output, rc = utils.io_systemlist(_cmd)@@ -1100,9 +1118,9 @@ M.git_stash_apply = function(selected, opts) end M.git_buf_edit = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local cmd = path.git_cwd({ "git", "show" }, opts)- local git_root = path.git_root(opts, true)+ local git_root = assert(path.git_root(opts, true), "not in git directory") local win = vim.api.nvim_get_current_win() local buffer_filetype = vim.bo.filetype local file = path.relative_to(path.normalize(vim.fn.expand("%:p")), git_root)@@ -1136,7 +1154,7 @@ M.git_buf_vsplit = function(selected, opts) end M.git_goto_line = function(selected, _)- if #selected == 0 then return end+ if not selected[1] then return end local line = utils.tointeger(selected[1]:match("^.-(%d+)%)")) if line then vim.api.nvim_win_set_cursor(0, { line, 0 }) end end@@ -1167,13 +1185,15 @@ M.toggle_flag = function(_, opts) -- command without the search argument cmd = utils.toggle_cmd_flag(assert(opts._cmd or opts.cmd), assert(opts.toggle_flag)), resume = true- }, opts.__call_opts)+ }, opts.__call_opts or {}) opts.__call_fn(o) end +---@param opts fzf-lua.config.Resolved|{}+---@param opt_name string M.toggle_opt = function(opts, opt_name) -- opts.__call_opts[opt_name] = not opts[opt_name]- local o = vim.tbl_deep_extend("keep", { resume = true }, opts.__call_opts)+ local o = vim.tbl_deep_extend("keep", { resume = true }, opts.__call_opts or {}) o[opt_name] = not opts[opt_name] opts.__call_fn(o) end@@ -1191,7 +1211,7 @@ M.toggle_follow = function(_, opts) end M.tmux_buf_set_reg = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local buf = selected[1]:match("^%[(.-)%]") local data, rc = utils.io_system({ "tmux", "show-buffer", "-b", buf }) if rc == 0 and data and #data > 0 then@@ -1206,29 +1226,25 @@ M.tmux_buf_set_reg = function(selected, opts) end M.paste_register = function(selected)- if #selected == 0 then return end+ if not selected[1] then return end local reg = selected[1]:match("%[(.-)%]")- local ok, data = pcall(vim.fn.getreg, reg)- if ok and #data > 0 then- vim.api.nvim_paste(data, false, -1)- end+ local data = vim.fn.getreg(reg)+ if #data > 0 then vim.api.nvim_paste(data, false, -1) end end M.set_qflist = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local nr = selected[1]:match("[(%d+)]") vim.cmd(string.format("%d%s", tonumber(nr), opts._is_loclist and "lhistory" or "chistory")) vim.cmd(opts._is_loclist and "lopen" or "copen") end ----@param selected string[]----@param opts table M.apply_profile = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local entry = path.entry_to_file(selected[1])- local fname = entry.path- local profile = entry.stripped:sub(#fname + 2):match("[^%s]+")+ local fname = assert(entry.path)+ local profile = assert(entry.stripped):sub(#fname + 2):match("[^%s]+") local ok = utils.load_profile_fname(fname, profile, opts.silent) if ok then require("fzf-lua").setup({ profile })@@ -1238,7 +1254,7 @@ end ---@param selected string[] ---@param opts fzf-lua.config.Resolved M.complete = function(selected, opts)- if #selected == 0 then+ if not selected[1] then if opts.__CTX.mode == "i" then vim.cmd [[noautocmd lua vim.api.nvim_feedkeys('i', 'n', true)]] end@@ -1286,15 +1302,17 @@ M.dap_bp_del = function(selected, opts) end end +---@param selected string[]+---@param opts fzf-lua.config.Zoxide|{} M.zoxide_cd = function(selected, opts)- if #selected == 0 then return end+ if not selected[1] then return end local cwd = selected[1]:match("[^\t]+$") or selected[1] if opts.cwd then cwd = path.join({ opts.cwd, cwd }) end local git_root = opts.git_root and path.git_root({ cwd = cwd }, true) or nil cwd = git_root or cwd- if cwd == vim.uv.cwd() then+ if cwd == utils.cwd() then utils.warn(("cwd already set to '%s'"):format(cwd)) return end@@ -1315,7 +1333,7 @@ local parse_entry = function(e) return e and e:match("%((.-)%)") or nil end M.serverlist_kill = function(sel) vim.iter(sel):map(parse_entry):each(function(addr) local ok, err = utils.rpcexec(addr, "nvim_exec2", "qa!", {})- assert(ok or err and err:match("Invalid channel"), err)+ assert(ok or tostring(err):match("Invalid channel"), err) end) end
@@ -1,7 +1,7 @@ local builtin = require "fzf-lua" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils"-local defaults = require "fzf-lua.defaults".defaults+local defaults = require "fzf-lua.defaults".defaults ---@type table local serpent = require "fzf-lua.lib.serpent" local M = {}@@ -104,7 +104,7 @@ function M._candidates(line, cmp_items) local commands = utils.tbl_flatten({ builtin_list }) table.sort(commands) - commands = vim.tbl_filter(function(val)+ commands = vim.tbl_filter(function(val) ---@diagnostic disable-next-line: param-type-mismatch return vim.startswith(val, l[2]) end, commands) @@ -175,7 +175,7 @@ function M._candidates(line, cmp_items) table.sort(opts) - opts = vim.tbl_filter(function(val)+ opts = vim.tbl_filter(function(val) ---@diagnostic disable-next-line: param-type-mismatch return vim.startswith(val, l[#l]) end, opts)
@@ -1,4 +1,4 @@----@diagnostic disable: unused+---@diagnostic disable local Src = {} Src.new = function(_)
@@ -1,5 +1,4 @@ ---@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local core = require "fzf-lua.core" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils"@@ -28,14 +27,14 @@ local function find_toplevel_cwd(maybe_cwd, postfix, orig_cwd) local disp_cwd, cwd = maybe_cwd, libuv.expand(maybe_cwd) -- returned cwd must be full path if path.has_cwd_prefix(cwd) then- cwd = uv.cwd() .. (#cwd > 1 and cwd:sub(2) or "")+ cwd = utils.cwd() .. (#cwd > 1 and cwd:sub(2) or "") -- inject "./" only if original path started with it -- otherwise ignore the "." retval from fnamemodify if #orig_cwd > 0 and orig_cwd:sub(1, 1) ~= "." then disp_cwd = nil end elseif not path.is_absolute(cwd) then- cwd = path.join({ uv.cwd(), cwd })+ cwd = path.join({ utils.cwd(), cwd }) end return disp_cwd, cwd, postfix end@@ -52,7 +51,7 @@ local set_cmp_opts_path = function(opts) local match = opts.word_pattern or "[^%s\"']*" local line = vim.api.nvim_get_current_line() local col = vim.api.nvim_win_get_cursor(0)[2] + 1- local before = col > 1 and line:sub(1, col - 1):reverse():match(match):reverse() or ""+ local before = col > 1 and assert(line:sub(1, col - 1):reverse():match(match)):reverse() or "" local after = line:sub(col):match(match) or "" -- special case when the cursor is on the left surrounding char if #before == 0 and #after == 0 and #line > col then@@ -62,13 +61,14 @@ local set_cmp_opts_path = function(opts) local cwd opts._cwd, cwd, opts.query = find_toplevel_cwd(before .. after, nil, nil) opts.prompt = path.add_trailing(opts._cwd or ".")- opts.cwd = cwd or opts.cwd or uv.cwd()+ opts.cwd = cwd or opts.cwd or utils.cwd() -- completion function rebuilds the line with the full path opts.complete = function(selected, o, l, _) -- query fuzzy matching is empty if #selected == 0 then return end local replace_at = col - #before- local relpath = path.relative_to(path.entry_to_file(selected[1], o).path, opts.cwd)+ local path0 = assert(path.entry_to_file(selected[1], o).path)+ local relpath = path.relative_to(path0, opts.cwd) local before_path = replace_at > 1 and l:sub(1, replace_at - 1) or "" local rest_of_line = #l >= (col + #after) and l:sub(col + #after) or "" local resolved_path = opts._cwd and path.join({ opts._cwd, relpath }) or relpath@@ -120,6 +120,7 @@ M.file = function(opts) end end)() opts = set_cmp_opts_path(opts)+ ---@diagnostic disable-next-line: param-type-mismatch return core.fzf_exec(opts.cmd, opts) end @@ -135,6 +136,7 @@ M.line = function(opts) return vim.trim(line:sub(1, col)) end)() opts.complete = function(selected, _, _, _)+ if not selected[1] then return end local newline = selected[1]:match(" (.-)$") return newline, #newline end
@@ -6,6 +6,9 @@ local libuv = require "fzf-lua.libuv" local actions = require "fzf-lua.actions" local devicons = require "fzf-lua.devicons" +---@class fzf-lua.config+---@field globals fzf-lua.config.Defaults+---@field __HLS_STATE { colorscheme: string, bg: string } local M = {} -- set this so that make_entry won't get nil err when setting remotely@@ -60,8 +63,10 @@ function M.resume_opts(opts) end -- proxy table (with logic) for accessing the global config-M.setup_opts = {} ---@type fzf-lua.Config|{}+M.setup_opts = {}++---@type fzf-lua.config.Defaults M.globals = setmetatable({}, { __index = function(_, index) local function setup_opts()@@ -166,11 +171,11 @@ local normalize_tbl = function(opts) end ---@generic T table----@param opts T|{}|fun():T?+---@param opts T?|{}|(fun():T?) ---@param globals string|(fzf-lua.Config|{})? ---@param __resume_key string? ---@return T-function M.normalize_opts(opts, globals, __resume_key)+function M.normalize_opts(opts, globals, __resume_key) ---@diagnostic disable -- opts can also be a function that returns an opts table ---@type fzf-lua.config.Resolved opts = eval(opts) or {}@@ -624,7 +629,7 @@ function M.normalize_opts(opts, globals, __resume_key) -- other user scenarios which need to use `opts._cwd`, for -- exmaple, using the "hide" profile and resuming fzf-lua -- from another tab after a `:tcd <dir>` (#1854)- opts._cwd = uv.cwd()+ opts._cwd = utils.cwd() if opts.cwd and #opts.cwd > 0 then -- NOTE: on Windows, `expand` will replace all backslashes with forward slashes@@ -640,7 +645,7 @@ function M.normalize_opts(opts, globals, __resume_key) -- relative paths in cwd are inaccessible when using multiprocess -- as the external process have no awareness of our current working -- directory so we must convert to full path (#375)- opts.cwd = path.join({ uv.cwd(), opts.cwd })+ opts.cwd = path.join({ utils.cwd(), opts.cwd }) elseif utils.__IS_WINDOWS and opts.cwd:sub(2) == ":" then -- TODO: upstream bug? on Windows: starting jobs with `cwd = C:` (without separator) -- ignores the cwd argument and starts the job in the current working directory@@ -1005,16 +1010,15 @@ function M.normalize_opts(opts, globals, __resume_key) opts._normalized = true return opts-end+end ---@diagnostic enable -M.bytecode = function(s, datatype)+M.bytecode = function(s, _) local keys = utils.strsplit(s, "%.") local iter = M for i = 1, #keys do- ---@diagnostic disable-next-line: assign-type-mismatch iter = iter[keys[i]] if not iter then break end- if i == #keys and type(iter) == datatype then+ if i == #keys and type(iter) == "function" then -- string.dump (function [, strip]) -- Returns a string containing a binary representation (a binary chunk) of the given -- function, so that a later load on this string returns a copy of the function (but
@@ -1,5 +1,3 @@----@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local fzf = require "fzf-lua.fzf" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils"@@ -11,6 +9,8 @@ local shell = require "fzf-lua.shell" local M = {} +---@class fzf-lua.action_defs+---@field [function] table M.ACTION_DEFINITIONS = { -- list of supported actions with labels to be displayed in the headers -- no pos implies an append to header array@@ -154,7 +154,6 @@ end M.fzf_exec = function(contents, opts) if not contents then error("must supply contents") end ---@type fzf-lua.config.Resolved- ---@diagnostic disable-next-line: assign-type-mismatch opts = config.normalize_opts(opts or {}, {}) if not opts then return end if type(contents) == "table" and type(contents[1]) == "table" then@@ -210,10 +209,9 @@ end ---@param opts? fzf-lua.config.Resolved ---@return thread?, string?, table? M.fzf_live = function(contents, opts)+ ---@type fzf-lua.config.Resolved opts = opts or {} opts.is_live = true- ---@diagnostic disable-next-line: assign-type-mismatch- ---@type fzf-lua.config.Resolved opts = config.normalize_opts(opts, {}) if not opts then return end local fzf_field_index = M.fzf_field_index(opts)@@ -227,7 +225,7 @@ M.fzf_live = function(contents, opts) local mtcmd = shell.stringify_mt(contents, opts) if mtcmd then cmd = M.expand_query(mtcmd, fzf_field_index)- elseif not is_fnc and nop(opts) then+ elseif type(contents) == "string" and nop(opts) then cmd = M.expand_query(contents, fzf_field_index) else cmd = shell.stringify(cmd2fnc(contents), opts, fzf_field_index)@@ -277,7 +275,7 @@ M.fzf_wrap = function(cmd, opts, convert_actions) if type(opts.cb_co) == "function" then opts.cb_co(_co) end local selected, exit_code = M.fzf(cmd, opts) -- If aborted (e.g. unhide process kill), do nothing- if not tonumber(exit_code) then return end+ if not exit_code or not selected then return end -- Default fzf exit callback acts upon the selected items fn_selected = opts.fn_selected or actions.act if not fn_selected then return end@@ -296,7 +294,7 @@ M.fzf_wrap = function(cmd, opts, convert_actions) end ---@param contents string?----@param opts? fzf-lua.config.Resolved+---@param opts? fzf-lua.config.Resolved|{} ---@return string[]? ---@return integer? exit_code M.fzf = function(contents, opts)@@ -351,7 +349,7 @@ M.fzf = function(contents, opts) -- Attach the previewer to the windows fzf_win:attach_previewer(previewer) -- Setup --preview/--preview-window/--bind=zero:... etc- if type(previewer.setup_opts) == "function" then ---@diagnostic disable-next-line: assign-type-mismatch+ if type(previewer.setup_opts) == "function" then opts = previewer:setup_opts(opts) end elseif opts.preview and type(opts.preview) ~= "string" then@@ -471,19 +469,20 @@ local function translate_border(winopts, metadata) return border end ----@param o fzf-lua.config.Resolved+---@param o fzf-lua.config.Resolved|{} ---@param fzf_win fzf-lua.Win ---@return string M.preview_window = function(o, fzf_win) local layout+ local preview = assert(o.winopts and o.winopts.preview) local prefix = string.format("%s:%s%s",- o.winopts.preview.hidden and "hidden" or "nohidden",- o.winopts.preview.wrap and "wrap" or "nowrap",+ preview.hidden and "hidden" or "nohidden",+ preview.wrap and "wrap" or "nowrap", (function() local border = (function() local preview_str = fzf_win:fzf_preview_layout_str() local preview_pos = preview_str:match("[^:]+") or "right"- return translate_border(o.winopts.preview,+ return translate_border(preview, { type = "fzf", name = "prev", layout = preview_pos, opts = o }) end)() return border and string.format(":%s", border) or ""@@ -493,8 +492,9 @@ M.preview_window = function(o, fzf_win) -- fzf v0.45 added transform, v0.46 added resize event -- which we use for changing the layout on resize and not utils.has(o, "fzf", { 0, 46 })- and o.winopts.preview.layout == "flex"- and tonumber(o.winopts.preview.flip_columns) > 0+ and preview.layout == "flex"+ and preview.flip_columns+ and preview.flip_columns > 0 then -- Fzf's alternate layout calculates the available preview width in a horizontal split -- (left/right), for the "<%d" condition to trigger the width should be test against a@@ -503,17 +503,18 @@ M.preview_window = function(o, fzf_win) -- NOTE: sending `true` as first arg gets the no fullscreen width, otherwise we'll get -- incosstent behavior when starting fullscreen local columns = fzf_win:columns(true)- local fzf_prev_percent = tonumber(o.winopts.preview.horizontal:match(":(%d+)%%"))+ local hor = assert(preview.horizontal)+ local fzf_prev_percent = tonumber(hor:match(":(%d+)%%")) local fzf_prev_width = not fzf_prev_percent and- tonumber(o.winopts.preview.horizontal:match(":(%d+)")) or nil+ tonumber(hor:match(":(%d+)")) or nil fzf_prev_percent = fzf_prev_percent or 50 local fzf_main_width = fzf_prev_width and (columns - fzf_prev_width) or math.ceil(columns * (100 - fzf_prev_percent) / 100)- local horizontal_min_width = o.winopts.preview.flip_columns - fzf_main_width + 1+ local horizontal_min_width = preview.flip_columns - fzf_main_width + 1 if horizontal_min_width > 0 then layout = string.format("%s:%s,<%d(%s:%s)",- prefix, o.winopts.preview.horizontal, horizontal_min_width,- prefix, o.winopts.preview.vertical)+ prefix, hor, horizontal_min_width,+ prefix, preview.vertical) end end layout = layout or string.format("%s:%s", prefix, fzf_win:fzf_preview_layout_str())@@ -802,17 +803,17 @@ M.set_header = function(opts) ---@param cwd string ---@return string local function normalize_cwd(cwd)- if path.is_absolute(cwd) and not path.equals(cwd, uv.cwd()) then+ if path.is_absolute(cwd) and not path.equals(cwd, utils.cwd()) then -- since we're always converting cwd to full path -- try to convert it back to relative for display- cwd = path.relative_to(cwd, uv.cwd())+ cwd = path.relative_to(cwd, utils.cwd()) end -- make our home dir path look pretty return path.HOME_to_tilde(cwd) end if opts.cwd_prompt then- opts.prompt = normalize_cwd(opts.cwd or uv.cwd())+ opts.prompt = normalize_cwd(opts.cwd or utils.cwd()) if tonumber(opts.cwd_prompt_shorten_len) and #opts.prompt >= tonumber(opts.cwd_prompt_shorten_len) then opts.prompt = path.shorten(opts.prompt, tonumber(opts.cwd_prompt_shorten_val) or 1)@@ -833,10 +834,10 @@ M.set_header = function(opts) if opts.cwd_header == false or opts.cwd_prompt and opts.cwd_header == nil or opts.cwd_header == nil and- (not opts.cwd or path.equals(opts.cwd, uv.cwd())) then+ (not opts.cwd or path.equals(opts.cwd, utils.cwd())) then return end- return normalize_cwd(opts.cwd or uv.cwd())+ return normalize_cwd(opts.cwd or utils.cwd()) end }, search = {@@ -888,7 +889,6 @@ M.set_header = function(opts) local def, to = nil, type(v) == "table" and v.header or nil if not to and type(action) == "function" and defs[action] then def = defs[action]- ---@diagnostic disable-next-line: undefined-field to = def[1] end if to then
@@ -30,7 +30,7 @@ M.reset = function() ctx = {} end ---conditionally update the context if fzf-lua ---interface isn't open ---@param opts? { includeBuflist?: boolean, buf?: integer|string, bufnr?: integer|string }----@return fzf-lua.Ctx|{}+---@return fzf-lua.Ctx M.refresh = function(opts) opts = opts or {} -- save caller win/buf context, ignore when fzf@@ -83,11 +83,12 @@ M.refresh = function(opts) end end -- custom bufnr from caller? (#1757)- local bufnr = utils.tointeger(opts.buf) or utils.tointeger(opts.bufnr)+ local bufnr = utils.tointeger(opts.buf or opts.bufnr) if bufnr then ctx.bufnr = bufnr ctx.bname = vim.api.nvim_buf_get_name(bufnr) end+ ---@diagnostic disable-next-line: return-type-mismatch return ctx end
@@ -1,3 +1,4 @@+---@diagnostic disable: missing-fields local path = require "fzf-lua.path" local utils = require "fzf-lua.utils" local actions = require "fzf-lua.actions"@@ -40,7 +41,7 @@ function M._man_cmd_fn(bat_pager) end ---missing fields are injected later, not sure how to tell luals about it----@type fzf-lua.config.Defaults|{}+---@class fzf-lua.config.Defaults M.defaults = { nbsp = utils.nbsp, winopts = {@@ -153,6 +154,8 @@ M.defaults = { }, fzf_tmux_opts = { ["-p"] = "80%,80%", ["--margin"] = "0,0" }, ---@class fzf-lua.config.Previewers+ ---@field builtin fzf-lua.config.BuiltinPreviewer+ ---@field git_diff fzf-lua.config.GitDiffPreviewer ---@field [string] fzf-lua.config.Previewer previewers = { cat = {@@ -179,6 +182,7 @@ M.defaults = { args = nil, _ctor = previewers.fzf.head, },+ ---@class fzf-lua.config.GitDiffPreviewer: fzf-lua.config.Previewer git_diff = { pager = M._preview_pager_fn, cmd_deleted = "git diff --color HEAD --",@@ -202,6 +206,7 @@ M.defaults = { help_tags = { _ctor = previewers.builtin.help_tags }, help_native = { _ctor = previewers.fzf.help_tags }, swiper = { _ctor = previewers.swiper.default },+ ---@class fzf-lua.config.BuiltinPreviewer: fzf-lua.config.Previewer builtin = { syntax = true, syntax_delay = 0,@@ -221,6 +226,7 @@ M.defaults = { snacks_image = { enabled = true, render_inline = true }, _ctor = previewers.builtin.buffer_or_file, },+ ---@class fzf-lua.config.CodeActionPreviewer: fzf-lua.config.Previewer codeaction = { _ctor = previewers.builtin.codeaction, diff_opts = { ctxlen = 3 },@@ -445,6 +451,7 @@ M.defaults.global = vim.tbl_deep_extend("force", M.defaults.files, { ---@field worktrees fzf-lua.config.GitWorktrees ---@field tags fzf-lua.config.GitTags ---@field stash fzf-lua.config.GitStash+---@field icons table<string, fzf-lua.git.icons> -- Must construct our opts table in stages -- so we can reference 'M.globals.files' M.defaults.git = {@@ -632,6 +639,7 @@ M.defaults.git = { }, _headers = { "actions", "cwd", "search" }, },+ ---@class fzf-lua.git.icons icons = { ["M"] = { icon = "M", color = "yellow" }, ["D"] = { icon = "D", color = "red" },@@ -646,6 +654,7 @@ M.defaults.git = { ---@class fzf-lua.config.Grep: fzf-lua.config.Base ---@field cmd? string default: auto detect rg|grep ---@field rg_glob? boolean|integer+---@field rg_glob_fn? fun(query: string, opts: table): string, string ---@field raw_cmd? string ---@field search? string ---@field no_esc? integer|boolean@@ -678,7 +687,7 @@ M.defaults.grep = { _headers = { "actions", "cwd" }, } ----@diagnostic disable-next-line: assign-type-mismatch+---@diagnostic disable-next-line: param-type-mismatch ---@class fzf-lua.config.GrepCurbuf: fzf-lua.config.Grep,{} ---@field filename? string M.defaults.grep_curbuf = vim.tbl_deep_extend("force", M.defaults.grep, {@@ -867,7 +876,7 @@ M.defaults.lines = { -- restore the format to something that `path.entry_to_file` can handle local bufnr0, lnum, text = s:match("%[(%d+)%].-(%d+) (.+)$") local bufnr = tonumber(bufnr0)- if not bufnr then return "" end+ if not bufnr then return "" end ---@cast bufnr integer return string.format("[%s]%s%s:%s:%s", bufnr, utils.nbsp, path.tail(vim.api.nvim_buf_get_name(bufnr)),@@ -880,7 +889,7 @@ M.defaults.lines = { _ctx = { includeBuflist = true }, } ----@diagnostic disable-next-line: assign-type-mismatch+---@diagnostic disable-next-line: param-type-mismatch ---@class fzf-lua.config.Blines: fzf-lua.config.Lines M.defaults.blines = vim.tbl_deep_extend("force", M.defaults.lines, { show_bufname = false,@@ -1030,7 +1039,7 @@ M.defaults.highlights = { ---@field dbfile string ---@field icons table ---@field packpath fun():string----@field _adm table AsyncDownloadManager+---@field _adm fzf-lua.AsyncDownloadManager ---@field dl_status integer ---@field _apply_awesome_theme function M.defaults.awesome_colorschemes = {@@ -1280,6 +1289,8 @@ M.defaults.lsp.finder = { ---@field post_action_cb function ---@field context lsp.CodeActionContext ---@field filter fun(x: lsp.CodeAction|lsp.Command):boolean+---@field _ui_select? { kind: string }+---@field _items any[] M.defaults.lsp.code_actions = { async_or_timeout = 5000, previewer = "codeaction",@@ -1398,9 +1409,9 @@ M.defaults.tagstack = { } ---@class fzf-lua.config.Commands: fzf-lua.config.Base----@field flatten? table<string, boolean>----@field include_builtin? boolean----@field sort_lastused? boolean+---@field flatten table<string, boolean>+---@field include_builtin boolean+---@field sort_lastused boolean M.defaults.commands = { actions = { ["enter"] = actions.ex_run }, flatten = {},@@ -1543,6 +1554,7 @@ M.defaults.menus = { ---@class fzf-lua.config.Tmux ---@field buffers fzf-lua.config.TmuxBuffers+---@field cmd string M.defaults.tmux = { ---@class fzf-lua.config.TmuxBuffers: fzf-lua.config.Base buffers = {@@ -1554,11 +1566,11 @@ M.defaults.tmux = { } ---@class fzf-lua.config.DapBase: fzf-lua.config.Base----@field commands fzf-lua.config.DapCommands|{}----@field configurations fzf-lua.config.DapConfigurations|{}----@field variables fzf-lua.config.DapVariables|{}----@field frames fzf-lua.config.DapFrames|{}----@field breakpoints fzf-lua.config.DapBreakpoints|{}+---@field commands fzf-lua.config.DapCommands+---@field configurations fzf-lua.config.DapConfigurations+---@field variables fzf-lua.config.DapVariables+---@field frames fzf-lua.config.DapFrames+---@field breakpoints fzf-lua.config.DapBreakpoints ---@class fzf-lua.config.Dap M.defaults.dap = {@@ -1588,7 +1600,7 @@ M.defaults.dap = { } ---@class fzf-lua.config.CompletePath: fzf-lua.config.Base----@field cmd? string default: auto detect fd|rg|find+---@field cmd string default: auto detect fd|rg|find ---@field word_pattern? string M.defaults.complete_path = { file_icons = false, ---@type integer|boolean@@ -1602,7 +1614,7 @@ M.defaults.complete_path = { } ---@class fzf-lua.config.CompleteFile: fzf-lua.config.Base----@field cmd? string default: auto detect fd|rg|find+---@field cmd string default: auto detect fd|rg|find ---@field word_pattern? string M.defaults.complete_file = { multiprocess = 1, ---@type integer|boolean@@ -1653,6 +1665,7 @@ M.defaults.file_icon_padding = "" M.defaults.dir_icon = "" ---@class fzf-lua.config.HLS+---@field fzf fzf-lua.config.fzfHLS M.defaults.__HLS = { normal = "FzfLuaNormal", border = "FzfLuaBorder",@@ -1671,7 +1684,7 @@ M.defaults.__HLS = { scrollborder_e = "FzfLuaScrollBorderEmpty", scrollborder_f = "FzfLuaScrollBorderFull", scrollfloat_e = "FzfLuaScrollFloatEmpty",- scrollfloat_f = "FzfLuaScrollFloatFull",+ scrollfloat_f = "FzfLuaScrollFloatFull", ---@type string|false header_bind = "FzfLuaHeaderBind", header_text = "FzfLuaHeaderText", path_colnr = "FzfLuaPathColNr",@@ -1692,6 +1705,7 @@ M.defaults.__HLS = { cmd_ex = "FzfLuaCmdEx", cmd_buf = "FzfLuaCmdBuf", cmd_global = "FzfLuaCmdGlobal",+ ---@class fzf-lua.config.fzfHLS fzf = { normal = "FzfLuaFzfNormal", cursorline = "FzfLuaFzfCursorLine",
@@ -52,6 +52,7 @@ function NvimWebDevicons:load(do_not_lazy_load) then self._package_loaded, self._package = pcall(require, self._package_name) if self._package_loaded then+ ---@diagnostic disable-next-line: param-type-mismatch self._package_path = path.parent(path.parent(path.normalize( debug.getinfo(self._package.setup, "S").source:gsub("^@", "")))) end@@ -108,7 +109,7 @@ function NvimWebDevicons:load_icons(opts) -- something is wrong with devicons utils.error("devicons.get_icons() is nil or empty!") return- end+ end ---@cast all_devicons table local icons = { by_filename = self._package.get_icons_by_filename(), by_extension = self._package.get_icons_by_extension(),@@ -186,6 +187,7 @@ function MiniIcons:load(do_not_lazy_load) then self._package_loaded, self._package = pcall(require, self._package_name) if self._package_loaded then+ ---@diagnostic disable-next-line: param-type-mismatch self._package_path = path.parent(path.parent(path.parent(path.normalize( debug.getinfo(self._package.setup, "S").source:gsub("^@", ""))))) end
@@ -26,7 +26,7 @@ local function coroutine_callback(fn) if coroutine.status(co) == "suspended" then coroutine.resume(co, ...) else- local pid = unpack({ ... })+ local pid = unpack({ ... }) ---@cast pid integer M.process_kill(pid) end end@@ -71,7 +71,7 @@ if false then M.uv_spawn = uv.spawn end ---@field cmd string|table ---@field env? table ---@field cb_finish fun(code: integer, sig: integer, from: string, pid: integer)----@field cb_write? fun(data: string, cb: fun(err: any): nil): nil+---@field cb_write fun(data: string, cb: fun(err: any): nil): nil ---@field cb_write_lines? fun(lines: string[]) ---@field cb_err fun(data: string) ---@field cb_pid? fun(pid: integer)@@ -162,7 +162,6 @@ M.spawn = function(opts, fn_transform, fn_done) table.insert(args, tostring(opts.cmd)) end - ---@diagnostic disable-next-line: missing-fields handle, pid = M.uv_spawn(shell, { args = args, stdio = { nil, output_pipe, error_pipe },@@ -379,7 +378,7 @@ end ---@param b64? boolean ---@return table M.deserialize = function(str, b64)- local res = loadstring(str)()+ local res = assert(loadstring(str))() if type(res) == "table" then return res --[[@as table]] end -- ./scripts/headless_fd.sh res = b64 ~= false and base64.decode(res) or res -- safe=false enable call function@@ -388,7 +387,7 @@ M.deserialize = function(str, b64) return obj end ----@param fn_str string+---@param fn_str any ---@return function? M.load_fn = function(fn_str) if type(fn_str) ~= "string" then return end@@ -794,7 +793,7 @@ M.unescape_fzf = function(s, fzf_version, is_win) local ret = s:gsub("\\+[^\\]", function(x) local bslash_num = #x:match([[\+]]) return string.rep([[\]],- bslash_num == 1 and bslash_num or bslash_num / 2) .. x:sub(-1)+ bslash_num == 1 and bslash_num or math.floor(bslash_num / 2)) .. x:sub(-1) end) return ret end
@@ -6,7 +6,7 @@ local path = require "fzf-lua.path" local utils = require "fzf-lua.utils" local libuv = require "fzf-lua.libuv" local devicons = require "fzf-lua.devicons"-local config+local config ---@module 'fzf-lua.config' -- attempt to load the current config -- should fail if we're running headless@@ -21,7 +21,7 @@ local function load_config() if not _G._fzf_lua_server then return end local ok, res = utils.rpcexec(_G._fzf_lua_server, "nvim_exec_lua", [[return FzfLua.libuv.serialize(FzfLua.config)]], {})- if not ok then error(res) end+ if not ok then error(res) end ---@cast res string res = libuv.deserialize(res) return res end@@ -78,6 +78,7 @@ local opts2 = setmetatable({}, { if _G._fzf_lua_is_headless then local _config = load_config() or {} ---@module 'fzf-lua.config'+ ---@diagnostic disable-next-line: missing-fields _config.globals = { git = {}, files = {}, grep = {} } _config.globals.git.icons = load_config_section("globals.git.icons", "table") or {} _config.globals.files.git_status_cmd =@@ -103,20 +104,21 @@ if _G._fzf_lua_is_headless then devicons = devicons, } end+assert(config.globals, "missing gloabals in fzf-lua config") M.get_diff_files = function(opts) local diff_files = {}- local cmd = opts.git_status_cmd or config.globals.files.git_status_cmd+ local cmd = opts.git_status_cmd or config.globals.files.git_status_cmd ---@cast cmd string[] if not cmd then return {} end local start = uv.hrtime()- local ok, status, err = pcall(utils.io_systemlist, path.git_cwd(cmd, opts))+ local ok, status, err = pcall(utils.io_systemlist, path.git_cwd(cmd, opts)) ---@cast status string[] local seconds = (uv.hrtime() - start) / 1e9 if seconds >= 0.5 and opts.silent ~= true then local exec_str = string.format([[require"fzf-lua".utils.warn(]] .. [["'git status' took %.2f seconds, consider using `git_icons=false` in this repository or use `silent=true` to supress this message.")]] , seconds) if not _G._fzf_lua_is_headless then- loadstring(exec_str)()+ assert(loadstring(exec_str))() else ---@diagnostic disable-next-line: undefined-field local chan_id = vim.fn.sockconnect("pipe", _G._fzf_lua_server, { rpc = true })@@ -125,8 +127,7 @@ M.get_diff_files = function(opts) end end if ok and err == 0 then- for i = 1, #status do- local line = status[i]+ for _, line in ipairs(status) do local icon = line:match("[MUDARCT?]+") local file = line:match("[^ ]*$") if icon and file then@@ -232,8 +233,8 @@ M.lgrep = function(s, opts) end ---@param opts table----@param search_query string----@param no_esc boolean|number+---@param search_query? string+---@param no_esc? boolean|number ---@return string? M.get_grep_cmd = function(opts, search_query, no_esc) opts = _G._fzf_lua_is_headless and setmetatable(vim.deepcopy(opts), { __index = opts2 }) or opts@@ -286,6 +287,7 @@ M.get_grep_cmd = function(opts, search_query, no_esc) return new_cmd end elseif opts.rg_glob then+ search_query = search_query or "" local new_query, glob_args = M.glob_parse(search_query, opts) if glob_args then -- since the search string mixes both the query and@@ -319,7 +321,7 @@ M.get_grep_cmd = function(opts, search_query, no_esc) -- the cwd, this is by design for perf reasons as having to deal with full paths -- will result in more code rouets taken in `make_entry.file` for i, p in ipairs(search_paths) do- search_paths[i] = libuv.shellescape(path.relative_to(path.normalize(p), uv.cwd()))+ search_paths[i] = libuv.shellescape(path.relative_to(path.normalize(p), utils.cwd())) end search_path = table.concat(search_paths, " ") if is_grep then@@ -479,7 +481,7 @@ M.preprocess = function(opts) opts.cmd = M.fix_windows_cmd(opts.cmd) if opts.cwd_only and not opts.cwd then- opts.cwd = uv.cwd()+ opts.cwd = utils.cwd() end if opts.file_icons then@@ -498,7 +500,7 @@ M.preprocess = function(opts) if not opts._fmt.to then local _to = opts2._fmt._to if type(_to) == "string" then- opts._fmt.to = loadstring(_to)()+ opts._fmt.to = assert(loadstring(_to))() end end end@@ -564,6 +566,7 @@ M.file = function(x, opts) return utils.strip_ansi_coloring(file_part) end end)()+ ---@cast stripped_filepath-? local filepath = stripped_filepath -- fd v8.3 requires adding '--strip-cwd-prefix' to remove -- the './' prefix, will not work with '--color=always'@@ -577,16 +580,16 @@ M.file = function(x, opts) if opts.absolute_path then -- make path absolute if not path.is_absolute(filepath) then- filepath = path.join({ opts.cwd or uv.cwd(), filepath })+ filepath = path.join({ opts.cwd or utils.cwd(), filepath }) end else -- make path relative- filepath = path.relative_to(filepath, opts.cwd or uv.cwd())+ filepath = path.relative_to(filepath, opts.cwd or utils.cwd()) end if path.is_absolute(filepath) then -- filter for cwd only if opts.cwd_only then- if not path.is_relative_to(filepath, opts.cwd or uv.cwd()) then+ if not path.is_relative_to(filepath, opts.cwd or utils.cwd()) then return nil end end
@@ -270,6 +270,9 @@ function M.HOME_to_tilde(path) return path end +---@param str string+---@param start_idx integer+---@return integer? local function find_next_separator(str, start_idx) local SEPARATOR_BYTES = utils._if_win( { M.fslash_byte, M.bslash_byte }, { M.fslash_byte })@@ -282,6 +285,9 @@ local function find_next_separator(str, start_idx) end end +---@param s string+---@param i? integer+---@return integer? local function utf8_char_len(s, i) -- Get byte count of unicode character (RFC 3629) local c = string_byte(s, i or 1)@@ -298,6 +304,10 @@ local function utf8_char_len(s, i) end end +---@param s string+---@param from integer+---@param to? integer+---@return string local function utf8_sub(s, from, to) local ret = "" -- NOTE: this function is called from shorten right after finding the next@@ -311,7 +321,7 @@ local function utf8_sub(s, from, to) local byte_i, utf8_i = from, from -- Concat utf8 chars until "to" or end of string while byte_i <= #s and (not to or utf8_i <= to) do- local c_len = utf8_char_len(s, byte_i)+ local c_len = utf8_char_len(s, byte_i) ---@cast c_len-? local c = string_sub(s, byte_i, byte_i + c_len - 1) ret = ret .. c byte_i = byte_i + c_len@@ -335,7 +345,7 @@ function M.shorten(path, max_len, sep) repeat local i = find_next_separator(path, start_idx) local end_idx = i and start_idx + math.min(i - start_idx, max_len) - 1 or nil- local part = utf8_sub(path, start_idx, end_idx)+ local part = utf8_sub(path, start_idx, end_idx) ---@cast i-? if end_idx and part == "." and i - start_idx > 1 then part = utf8_sub(path, start_idx, end_idx + 1) end@@ -391,7 +401,7 @@ function M.entry_to_location(entry, opts) local uri, line0, col0 = entry:match("^(.*://.*):(%d+):(%d+):") local line = utils.tointeger(line0) or 1 local col = utils.tointeger(col0) or 1- if opts.path_shorten and uri:match("file://") then+ if opts.path_shorten and uri and uri:match("file://") then uri = "file://" .. M.lengthen(uri:sub(8)) end return {@@ -447,7 +457,7 @@ function M.entry_to_file(entry, opts, force_uri) local parts = utils.strsplit(entry, utils.nbsp) local idx0 = 1 for i = 1, #parts - 1 do- local s = parts[i]+ local s = parts[i] ---@cast s string if s:match(".-:%d+:") then break end@@ -544,9 +554,8 @@ function M.entry_to_file(entry, opts, force_uri) } end ----@param cmd string|string[]----@param opts table----@return string|string[]+---@overload fun(cmd: string, opts: table): string+---@overload fun(cmd: string[], opts: table): string[] function M.git_cwd(cmd, opts) local git_args = { { "cwd", "-C" },@@ -599,7 +608,7 @@ function M.git_root(opts, noerr) end ---@param str string----@param opts fzf-lua.Config+---@param opts fzf-lua.config.Resolved ---@return fzf-lua.path.Entry|fzf-lua.keymap.Entry function M.keymap_to_entry(str, opts) local valid_modes = {@@ -613,7 +622,7 @@ function M.keymap_to_entry(str, opts) if not mode or not keymap then return {} end mode, keymap = vim.trim(mode), vim.trim(keymap) mode = valid_modes[mode] and mode or "" -- only valid modes- local out+ local out ---@type string[] local vmap, cmd = nil, string.format("verbose %smap %s", mode, keymap) -- Run in the context of the originating buffer or keympas might return -- "No mapping found"
lua/fzf-lua/previewer/builtin.lua
@@ -14,7 +14,7 @@ local fn = vim.fn ---@field private _winids table<integer, integer> local TSContext = {} ----@param opts? TSContext.UserConfig+---@param opts TSContext.UserConfig ---@return boolean function TSContext.setup(opts) if TSContext._setup then return true end@@ -154,9 +154,9 @@ local Previewer = {} ---@field title any ---@field title_pos "center"|"left"|"right" ---@field title_fnamemodify fun(title: string, width: integer?): string----@field render_markdown table?----@field snacks_image table?----@field winopts fzf-lua.config.Winopts|{}?+---@field render_markdown table+---@field snacks_image table+---@field winopts fzf-lua.config.PreviewerWinopts ---@field syntax boolean ---@field syntax_delay integer ---@field syntax_limit_b integer@@ -286,7 +286,7 @@ end function Previewer.base:preview_is_terminal() if not self.win or not self.win:validate_preview() then return end- return utils.getwininfo(self.win.preview_winid).terminal == 1+ return (utils.getwininfo(self.win.preview_winid) or {}).terminal == 1 end ---@diagnostic disable-next-line: unused@@ -490,7 +490,7 @@ function Previewer.base:cmdline(_) -- convert empty string to nil if not tonumber(idx) then entry = nil end -- upvalue incase previewer was detached/re-attached (global picker)- self = self.win._previewer or self+ self = self.win._previewer or self ---@type fzf-lua.previewer.Builtin -- on windows, query may not be expanded to a string: #1887 self.opts._last_query = query or "" self:display_entry(entry)@@ -620,6 +620,7 @@ function Previewer.base:ts_ctx_inc_dec_maxlines(num) end ---@class fzf-lua.previewer.BufferOrFile : fzf-lua.previewer.Builtin,{}+---@field match_id? integer ---@field super fzf-lua.previewer.Builtin Previewer.buffer_or_file = Previewer.base:extend() @@ -644,22 +645,22 @@ end function Previewer.buffer_or_file:parse_entry(entry_str) ---@type fzf-lua.buffer_or_file.Entry local entry = self:entry_to_file(entry_str)- local loaded = entry.bufnr and api.nvim_buf_is_loaded(entry.bufnr)+ local buf = entry.bufnr+ local loaded = buf and api.nvim_buf_is_loaded(buf) if loaded then- entry.tick = vim.b[entry.bufnr].changedtick+ entry.tick = vim.b[buf].changedtick return entry end -- buffer is not loaded, can happen when calling "lines" with `set nohidden` -- or when starting nvim with an arglist, fix entry.path since it contains -- filename only- local valid = entry.bufnr and api.nvim_buf_is_valid(entry.bufnr)- if valid then- entry.path = path.relative_to(api.nvim_buf_get_name(entry.bufnr), uv.cwd())+ if buf and api.nvim_buf_is_valid(buf) then+ entry.path = path.relative_to(api.nvim_buf_get_name(buf), utils.cwd()) end if entry.path then- if entry.path:find("^fugitive://") then+ if entry.path:find("^fugitive://") and buf and api.nvim_buf_is_valid(buf) then entry.do_not_cache = true- api.nvim_buf_call(entry.bufnr,+ api.nvim_buf_call(buf, function() vim.cmd(("do fugitive BufReadCmd %s"):format(entry.path)) end) return entry end@@ -772,7 +773,7 @@ function Previewer.buffer_or_file:populate_terminal_cmd(tmpbuf, cmd, entry) height = wincfg.height - 2, scaler = self.ueberzug_scaler, path = path.is_absolute(entry.path) and entry.path or- path.join({ self.opts.cwd or uv.cwd(), entry.path }),+ path.join({ self.opts.cwd or utils.cwd(), entry.path }), } local json = vim.json.encode(params) -- both 'fs_open|write|close' and 'fn.system' work.@@ -872,7 +873,7 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str) self:preview_buf_post(entry) return end- if cached and not cached.invalid then+ if cached and not cached.invalid and cached.bufnr then self:set_preview_buf(cached.bufnr, cached.min_winopts) self:preview_buf_post(entry, cached.min_winopts) return@@ -896,11 +897,12 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str) -- terminal buffers use minimal window style (2nd arg) self:set_preview_buf(tmpbuf, entry.terminal) self:preview_buf_post(entry, entry.terminal)- elseif entry.uri then+ elseif entry.uri and entry.range then -- LSP 'jdt://' entries, see issue #195 -- https://github.com/ibhagwan/fzf-lua/issues/195 api.nvim_win_call(self.win.preview_winid, function()- local ok, res = pcall(utils.jump_to_location, entry, "utf-16", false)+ local loc = { uri = entry.uri, range = entry.range }+ local ok, res = pcall(utils.jump_to_location, loc, "utf-16", false) if ok then self.preview_bufnr = api.nvim_get_current_buf() else@@ -917,11 +919,10 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str) end) self:preview_buf_post(entry) else- assert(entry.path) -- not found in cache, attempt to load local tmpbuf = reuse_buf or self:get_tmp_buffer() if self.extensions and not utils.tbl_isempty(self.extensions) then- local ext = path.extension(entry.path)+ local ext = path.extension(assert(entry.path)) local cmd = ext and self.extensions[ext:lower()] if cmd and self:populate_terminal_cmd(tmpbuf, cmd, entry) then -- will return 'false' when cmd isn't executable.@@ -944,7 +945,7 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str) lines = entry.content else -- make sure the file is readable (or bad entry.path)- local fs_stat = entry.fs_stat or uv.fs_stat(entry.path)+ local fs_stat = entry.path and uv.fs_stat(entry.path) if not fs_stat then lines = { string.format("Unable to stat file %s", entry.path) } elseif fs_stat.size > 0 and utils.perl_file_is_binary(entry.path) then@@ -1148,7 +1149,7 @@ local filetype_detect = function(bufnr, filepath) return vim.bo[bufnr].filetype end ----@param entry? fzf-lua.buffer_or_file.Entry|{}+---@param entry? fzf-lua.buffer_or_file.Entry function Previewer.buffer_or_file:do_syntax(entry) if not self.preview_bufnr or not entry or not entry.path then return end local bufnr = self.preview_bufnr@@ -1320,15 +1321,13 @@ function Previewer.buffer_or_file:set_cursor_hl(entry) regex, reg) end if regex_start > 0 then- ---@diagnostic disable-next-line: assign-type-mismatch- self.match_id = fn.matchaddpos(hls.search, { { lnum, regex_start, regex_end } }, 11)+ self.match_id = fn.matchaddpos(hls.search, { { lnum, regex_start, regex_end } }, 11) ---@as integer end end -- Fallback to cursor hl, only if column exists if regex_start <= 0 and hls.cursor and col > 0 then- ---@diagnostic disable-next-line: assign-type-mismatch- self.match_id = fn.matchaddpos(hls.cursor, { { lnum, math.max(1, col) } }, 11)+ self.match_id = fn.matchaddpos(hls.cursor, { { lnum, math.max(1, col) } }, 11) ---@as integer end utils.zz()@@ -1412,9 +1411,10 @@ end function Previewer.help_tags:parse_entry(entry_str) local tag = entry_str:match("^[^%s]+") if not tag then- return {}+ return { stripped = entry_str } end local vimdoc = entry_str:match(string.format("[^%s]+$", utils.nbsp))+ assert(vimdoc and #vimdoc > 0, "help_tags entry missing vimdoc path") local hregex = (function() -- lazy.nvim can generate helptags from markdown -- https://github.com/folke/lazy.nvim/blob/56a34a825b55e0e30cd9df0e055e428a13afd4aa/lua/lazy/help.lua#L35@@ -1445,7 +1445,7 @@ function Previewer.help_tags:gen_winopts() end ---@class fzf-lua.help_tags.Entry: fzf-lua.buffer_or_file.Entry----@field hregex? string+---@field hregex string ---@param entry fzf-lua.help_tags.Entry function Previewer.help_tags:set_cursor_hl(entry)@@ -1543,7 +1543,7 @@ function Previewer.marks:parse_entry(entry_str) else filepath = res end- filepath = path.relative_to(filepath, uv.cwd())+ filepath = path.relative_to(filepath, utils.cwd()) end return { bufnr = bufnr,@@ -1564,11 +1564,11 @@ end function Previewer.jumps:parse_entry(entry_str) local bufnr = nil- local _, lnum, col, filepath = entry_str:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)")+ local _, lnum, col0, filepath = entry_str:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)") if filepath then local ok, res = pcall(libuv.expand, filepath) if ok then- filepath = path.relative_to(res, uv.cwd())+ filepath = path.relative_to(res, utils.cwd()) end if not uv.fs_stat(filepath) then -- file is not accessible,@@ -1577,11 +1577,12 @@ function Previewer.jumps:parse_entry(entry_str) filepath = api.nvim_buf_get_name(self.win.src_bufnr) end end+ local col = utils.tointeger(col0) return { bufnr = bufnr, path = filepath, line = utils.tointeger(lnum) or 1,- col = utils.tointeger(col) + 1 or 1,+ col = col and col + 1 or 1, } end @@ -1606,9 +1607,10 @@ function Previewer.tags:set_cursor_hl(entry) -- didn't reload the buffer (same file) api.nvim_win_set_cursor(0, { 1, 0 }) fn.clearmatches()- fn.search(entry.ctag, "W")+ local ctag = assert(entry.ctag)+ fn.search(ctag, "W") if self.win.hls.search then- fn.matchadd(self.win.hls.search, entry.ctag)+ fn.matchadd(self.win.hls.search, ctag) end self.orig_pos = api.nvim_win_get_cursor(0) utils.zz()@@ -1708,7 +1710,7 @@ function Previewer.quickfix:populate_preview_buf(entry_str) return end - local qf_list = self.opts._is_loclist+ local qf_list = self.opts.is_loclist and fn.getloclist(self.win.src_winid, { all = "", nr = tonumber(nr) }) or fn.getqflist({ all = "", nr = tonumber(nr) }) if utils.tbl_isempty(qf_list) or utils.tbl_isempty(qf_list.items) then@@ -1719,7 +1721,7 @@ function Previewer.quickfix:populate_preview_buf(entry_str) for _, e in ipairs(qf_list.items) do table.insert(lines, string.format("%s|%d col %d|%s", path.HOME_to_tilde(path.relative_to(- api.nvim_buf_get_name(e.bufnr), uv.cwd())),+ api.nvim_buf_get_name(e.bufnr), utils.cwd())), e.lnum, e.col, e.text)) end self.tmpbuf = self:get_tmp_buffer()@@ -1739,7 +1741,6 @@ function Previewer.autocmds:new(o, opts) return self end ----@return fzf-lua.config.Winopts|{}? function Previewer.autocmds:gen_winopts() if not self._is_vimL_command then return self.winopts@@ -1761,7 +1762,7 @@ function Previewer.autocmds:populate_preview_buf(entry_str) if entry.path == "<none>" then self._is_vimL_command = true entry.path = entry_str:match("[^:|]+│")- local viml = entry_str:match("[^│]+$")+ local viml = assert(entry_str:match("[^│]+$")) local lines = vim.split(viml, "\n") local tmpbuf = self:get_tmp_buffer() api.nvim_buf_set_lines(tmpbuf, 0, -1, false, lines)@@ -1794,7 +1795,8 @@ function Previewer.keymaps:populate_preview_buf(entry_str) if entry.vmap then -- keymap is vimL, there is no source file info -- so we display the vimL code instead- local lines = utils.strsplit(entry.vmap:match("[^%s]+$"), "\n")+ local vmap = assert(entry.vmap:match("[^%s]+$"))+ local lines = utils.strsplit(vmap, "\n") local tmpbuf = self:get_tmp_buffer() api.nvim_buf_set_lines(tmpbuf, 0, -1, false, lines) vim.bo[tmpbuf].filetype = "vim"@@ -1864,7 +1866,7 @@ function Previewer.nvim_options:get_help_text(tag) end function Previewer.nvim_options:parse_entry(entry_str)- local parts = vim.split(entry_str, self.opts.separator)+ local parts = vim.split(entry_str, self.opts.separator) ---@cast parts [string, string] local option = vim.trim(parts[1]) local value = vim.trim(parts[2]) return { name = option, value = value }
lua/fzf-lua/previewer/codeaction.lua
@@ -1,3 +1,4 @@+---@diagnostic disable: param-type-mismatch local utils = require "fzf-lua.utils" local shell = require "fzf-lua.shell" local native = require("fzf-lua.previewer.fzf")@@ -38,6 +39,7 @@ local function diff_text_edits(text_edits, bufnr, offset_encoding, diff_opts) table.concat(orig_lines, eol) .. eol, table.concat(new_lines, eol) .. eol, diff_opts)+ ---@cast diff string -- Windows: some LSPs use "\n" for EOL (e.g clangd) -- remove both "\n" and "\r\n" (#1172) return utils.strsplit(vim.trim(diff), "\r?\n")@@ -122,6 +124,10 @@ local function diff_workspace_edit(workspace_edit, offset_encoding, diff_opts) return diff end +---@param err { code: integer, message: string }?+---@param tuple [integer, lsp.CodeAction]+---@param diff_opts vim.text.diff.Opts+---@return string[] local function diff_tuple(err, tuple, diff_opts) if err then return {@@ -130,7 +136,7 @@ local function diff_tuple(err, tuple, diff_opts) end local action = tuple[2] if action.edit then- local client = utils.lsp_get_clients({ id = tuple[1] })[1]+ local client = assert(utils.lsp_get_clients({ id = tuple[1] })[1]) return diff_workspace_edit(action.edit, client.offset_encoding, diff_opts) else local command = type(action.command) == "table" and action.command or action@@ -145,6 +151,10 @@ local function diff_tuple(err, tuple, diff_opts) end -- https://github.com/neovim/neovim/blob/v0.9.4/runtime/lua/vim/lsp/buf.lua#L666+---@param self fzf-lua.previewer.CodeActionBuiltin+---@param idx integer+---@param callback? function+---@return string[] local function preview_action_tuple(self, idx, callback) local tuple = self.opts._items[idx] -- neovim changed the ui.select params with 0.10.0 (#947)@@ -156,7 +166,7 @@ local function preview_action_tuple(self, idx, callback) -- the error (we already alerted the user about it in `handle_resolved_response`) -- and display the default "unsupported" message from the original action if self._resolved_actions[idx] then- local resolved = self._resolved_actions[idx]+ local resolved = assert(self._resolved_actions[idx]) return diff_tuple(nil, not resolved.err and resolved.tuple or tuple, self.diff_opts) end -- Not found in cache, check if the client supports code action resolving@@ -220,7 +230,9 @@ local function preview_action_tuple(self, idx, callback) end end -+---@class fzf-lua.previewer.CodeActionBuiltin: fzf-lua.previewer.Builtin,{}+---@field super fzf-lua.previewer.Builtin+---@field diff_opts vim.text.diff.Opts M.builtin = builtin.base:extend() M.builtin.preview_action_tuple = preview_action_tuple @@ -265,19 +277,25 @@ function M.builtin:populate_preview_buf(entry_str) self.win:update_preview_scrollbar() end +---@class fzf-lua.previewer.CodeActionNative: fzf-lua.previewer.Fzf,{}+---@field super fzf-lua.previewer.Fzf M.native = native.base:extend() M.native.preview_action_tuple = preview_action_tuple -function M.native:new(o, opts, fzf_win)+---@param o fzf-lua.config.CodeActionPreviewer+---@param opts fzf-lua.config.LspCodeActions+---@return fzf-lua.previewer.CodeActionNative+function M.native:new(o, opts) assert(opts._ui_select and opts._ui_select.kind == "codeaction")- M.native.super.new(self, o, opts, fzf_win)+ M.native.super.new(self, o, opts) setmetatable(self, M.native)- self.pager = opts.preview_pager == nil and o.pager or opts.preview_pager- if type(self.pager) == "function" then- self.pager = self.pager()- end+ local pager = opts.preview_pager == nil and o.pager or opts.preview_pager+ if type(pager) == "function" then pager = assert(pager()) end+ local cmd = pager and pager:match("[^%s]+") or nil+ if cmd and vim.fn.executable(cmd) == 1 then self.pager = pager end self.diff_opts = o.diff_opts self._resolved_actions = {}+ ---@diagnostic disable-next-line: undefined-field for i, _ in ipairs(self.opts._items) do self._resolved_actions[i] = false end@@ -287,13 +305,14 @@ end function M.native:cmdline(o) o = o or {} local act = shell.stringify_data(function(entries, _, _)- local idx = tonumber(entries[1]:match("^%s*%d+%."))- assert(type(idx) == "number")+ if not entries[1] then return shell.nop() end+ local idx = utils.tointeger(entries[1]:match("^%s*%d+%."))+ assert(idx) local lines = self:preview_action_tuple(idx) return table.concat(lines, "\r\n") end, self.opts, "{}")- if self.pager and #self.pager > 0 and vim.fn.executable(self.pager:match("[^%s]+")) == 1 then- act = act .. " | " .. utils._if_win_normalize_vars(self.pager)+ if self.pager then+ act = act .. " | " .. self.pager end return act end
@@ -28,8 +28,8 @@ function Previewer.base:new(o, opts) return self end ----@param opts table----@return table+---@param opts fzf-lua.config.Resolved+---@return fzf-lua.config.Resolved function Previewer.base:setup_opts(opts) -- Set the preview command line opts.preview = self:cmdline()@@ -111,6 +111,7 @@ end function Previewer.cmd:action(o) o = o or {} local act = shell.stringify_data(function(items, _, _)+ if not items[1] then return utils.shell_nop() end local entry = path.entry_to_file(items[1], self.opts) return entry.bufname or entry.path end, self.opts, self.opts.field_index_expr or "{}")@@ -217,8 +218,9 @@ function Previewer.cmd_async:parse_entry_and_verify(entrystr) end local entry = path.entry_to_file(entrystr, self.opts)+ if not entry.stripped then return end -- make relative for bat's header display- local filepath = path.relative_to(entry.bufname or entry.path or "", uv.cwd())+ local filepath = path.relative_to(entry.bufname or entry.path or "", utils.cwd()) if self.opts._ctag then -- NOTE: override `entry.ctag` with the unescaped version entry.ctag = path.entry_to_ctag(entry.stripped, true)@@ -251,6 +253,7 @@ function Previewer.cmd_async:cmdline(o) local act = shell.stringify_cmd(function(items) self._last_query = items[2] or "" local filepath, _, errcmd = self:parse_entry_and_verify(items[1])+ if not filepath then return utils.shell_nop() end local cmd = errcmd or ("%s %s %s"):format( self.cmd, self.args, libuv.shellescape(filepath)) return cmd@@ -305,12 +308,13 @@ function Previewer.bat_async:cmdline(o) self._last_query = items[2] or "" if items[1] == "" then return utils.shell_nop() end local filepath, entry, errcmd = self:parse_entry_and_verify(items[1])+ if not filepath or not entry then return utils.shell_nop() end local line_range = ""- if entry.ctag then+ if entry.ctag and entry.line then -- this is a ctag without line numbers, since we can't -- provide the preview file offset to fzf via the field -- index expression we use '--line-range' instead- local start_line = utils.tointeger(math.max(1, entry.line - fzf_lines / 2))+ local start_line = math.floor((math.max(1, entry.line - fzf_lines / 2))) local end_line = start_line + fzf_lines - 1 line_range = ("--line-range=%d:%d"):format(start_line, end_line) end@@ -318,7 +322,7 @@ function Previewer.bat_async:cmdline(o) self.cmd, self.args, self.theme and string.format([[--theme="%s"]], type(self.theme) == "function" and tostring(self.theme()) or tostring(self.theme)) or "",- tonumber(entry.line) and tonumber(entry.line) > 0+ entry.line and entry.line > 0 and string.format("--highlight-line=%d", entry.line) or "", line_range, libuv.shellescape(filepath))@@ -331,14 +335,21 @@ end ---@field super fzf-lua.previewer.Fzf,{} Previewer.git_diff = Previewer.base:extend() +---@param o fzf-lua.config.GitDiffPreviewer+---@param opts fzf-lua.config.Resolved+---@return fzf-lua.previewer.GitDiff function Previewer.git_diff:new(o, opts) Previewer.git_diff.super.new(self, o, opts) self.cmd_deleted = path.git_cwd(o.cmd_deleted, opts) self.cmd_modified = path.git_cwd(o.cmd_modified, opts) self.cmd_untracked = path.git_cwd(o.cmd_untracked, opts)- self.pager = opts.preview_pager == nil and o.pager or opts.preview_pager- if type(self.pager) == "function" then- self.pager = self.pager()+ local pager = opts.preview_pager == nil and o.pager or opts.preview_pager+ if type(pager) == "function" then pager = assert(pager()) end+ local cmd = pager and pager:match("[^%s]+") or nil+ if cmd and vim.fn.executable(cmd) == 1 then+ -- style 2: as we are unable to use %var% within a "cmd /c" without !var! expansion+ -- https://superuser.com/questions/223104/setting-and-using-variable-within-same-command-line-in-windows-cmd-ex+ self.pager = "| " .. utils._if_win_normalize_vars(pager, 2) end do -- populate the icon mappings@@ -360,6 +371,7 @@ function Previewer.git_diff:cmdline(o) utils.warn("shell error while running preview action.") return utils.shell_nop() end+ if not items[1] then return utils.shell_nop() end local is_deleted = items[1]:match(self.git_icons["D"] .. utils.nbsp) ~= nil local is_modified = items[1]:match("[" .. self.git_icons["M"] ..@@ -374,7 +386,7 @@ function Previewer.git_diff:cmdline(o) local s = items[1] if s:match("%s%->%s") then -- for renames, we take only the last part (#864)- s = s:match("%s%->%s(.*)$")+ s = s:match("%s%->%s(.*)$") or s end local entry = path.entry_to_file(s, self.opts) local cmd = nil@@ -393,13 +405,6 @@ function Previewer.git_diff:cmdline(o) end if not cmd then return "" end if type(cmd) == "table" then return table.concat(cmd, " ") end- local pager = ""- if self.pager and #self.pager > 0 and- vim.fn.executable(self.pager:match("[^%s]+")) == 1 then- -- style 2: as we are unable to use %var% within a "cmd /c" without !var! expansion- -- https://superuser.com/questions/223104/setting-and-using-variable-within-same-command-line-in-windows-cmd-ex- pager = "| " .. utils._if_win_normalize_vars(self.pager, 2)- end -- with default commands we add the filepath at the end. -- If the user configured a more complex command, e.g.: -- git_diff = {@@ -407,6 +412,7 @@ function Previewer.git_diff:cmdline(o) -- } -- we use ':format' directly on the user's command, see -- issue #392 for more info (limiting diff output width)+ if not entry.path then return "" end local fname_escaped = libuv.shellescape(entry.path) if cmd:match("[<{]file[}>]") then cmd = cmd:gsub("[<{]file[}>]", fname_escaped)@@ -425,7 +431,7 @@ function Previewer.git_diff:cmdline(o) -- env vars in `stringify:libuv.spawn` -- local setenv = utils.shell_setenv_str(env) -- cmd = string.format("%s %s %s", table.concat(setenv, " "), cmd, pager)- cmd = string.format("%s %s", cmd, pager)+ cmd = string.format("%s %s", cmd, self.pager or "") return { cmd = cmd, env = env } end, self.opts, "{}") return act@@ -446,6 +452,7 @@ end function Previewer.man_pages:cmdline(o) o = o or {} local act = shell.stringify_cmd(function(items)+ if not items[1] then return utils.shell_nop() end local manpage = require("fzf-lua.providers.manpages").manpage_sh_arg(items[1]) local cmd = self.cmd:format(manpage) return cmd@@ -472,7 +479,9 @@ end function Previewer.help_tags:cmdline(o) o = o or {} local act = shell.stringify_cmd(function(items)+ if not items[1] then return utils.shell_nop() end local vimdoc = items[1]:match(string.format("[^%s]+$", utils.nbsp))+ assert(vimdoc, "help_tags previewer: unable to parse help file name") local tag = items[1]:match("^[^%s]+") local ext = path.extension(vimdoc) local cmd = self.cmd:format(libuv.shellescape(vimdoc))
lua/fzf-lua/previewer/init.lua
@@ -39,17 +39,14 @@ Previewer.swiper.default = function() return require "fzf-lua.previewer.swiper". ---@field cmd? string ---@field args? string ---@field preview_offset? string----@field cmd_deleted? string----@field cmd_modified? string----@field cmd_untracked? string ---@field theme? string ---@field pager? string|function----@field _fn_git_icons? table[]|fun():table+---@field _fn_git_icons? table[]|fun():table? ---builtin ---@field title_fnamemodify? function ---Instantiate previewer from spec----@param spec table+---@param spec? fzf-lua.config.Previewer|string ---@param opts table ---@return fzf-lua.previewer.Fzf|fzf-lua.previewer.Builtin? Previewer.new = function(spec, opts)
lua/fzf-lua/previewer/swiper.lua
@@ -6,9 +6,20 @@ local shell = require "fzf-lua.shell" local M = {} ----@class fzf-lua.previewer.SwiperBase: fzf-lua.Object,{}+---@class fzf-lua.previewer.SwiperBase: fzf-lua.Object, {}+---@field opts table+---@field ctx_winid integer+---@field ctx_bufnr integer+---@field ctx_winopts table+---@field ns integer+---@field picker string+---@field win fzf-lua.Win M.base = Object:extend() +---@diagnostic disable: param-type-not-match+---@param o fzf-lua.config.Previewer+---@param opts fzf-lua.config.Resolved+---@return fzf-lua.previewer.SwiperBase? function M.base:new(o, opts) o = o or {} self.opts = opts;@@ -79,7 +90,7 @@ function M.base:parse_lnum_col(line, is_entry) elseif self.picker == "treesitter" or self.picker == "lsp_document_symbols" then prefix = line:gsub("%s+$", ""):match("(.*%s+).+$") lnum, col = line:match("^.-(%d+):(%d+)%s")- off = col and (1 - assert(tonumber(col)) - 1)+ off = col and (1 - assert(utils.tointeger(col)) - 1) or nil end if lnum then -- use `vin.fn.strwidth` for proper adjustment of fzf's marker/pointer if unicode@@ -92,7 +103,7 @@ function M.base:preview_cmd() return shell.stringify_data(function(items, _, _) ---@type string?, string?, string? local entry, _, idx = unpack(items, 1, 3)- if not tonumber(idx) then return end+ if not entry or not tonumber(idx) then return end local lnum, col = self:parse_lnum_col(entry, true) if not lnum or lnum < 1 then return end vim.api.nvim_win_set_cursor(self.ctx_winid, { lnum, col or 0 })@@ -129,8 +140,9 @@ function M.base:highlight_matches() local hldef = vim.api.nvim_get_hl(0, { link = false, name = hlgroup }) return hldef and hldef.fg end)()- local fzf_win = utils.fzf_winobj().fzf_winid- local fzf_buf = utils.fzf_winobj().fzf_bufnr+ local win = assert(utils.fzf_winobj())+ local fzf_win = win.fzf_winid+ local fzf_buf = win.fzf_bufnr if not vim.api.nvim_win_is_valid(fzf_win) or not vim.api.nvim_buf_is_valid(fzf_buf) then return end@@ -144,7 +156,9 @@ function M.base:highlight_matches() for r = l_s, l_e do (function() local buf_lnum = r - l_s + 2- local lnum, _, col_valid_idx, col_off = self:parse_lnum_col(buf_lines[buf_lnum])+ local lnum, _, col_valid_idx, col_off = self:parse_lnum_col(assert(buf_lines[buf_lnum]))+ assert(col_valid_idx)+ assert(col_off) if not lnum or lnum < 1 then return end local state = { bytelen = 0 } for c = 1, max_columns do@@ -178,8 +192,10 @@ function M.base:close() -- on hide + change picker ctx will be nil local ctx = utils.__CTX() if not ctx then return end+ assert(ctx.winopts) vim.wo[ctx.winid].winhl = ctx.winopts.winhl vim.wo[ctx.winid].cursorline = ctx.winopts.cursorline+ ---@diagnostic disable-next-line: param-type-not-match vim.api.nvim_win_set_cursor(ctx.winid, ctx.cursor) utils.zz() end
@@ -23,6 +23,8 @@ return { return opts end opts.actions = opts.actions or {}+ assert(opts.keymap)+ assert(opts.keymap.builtin) if fzf.utils.has(opts, "sk") then -- `execute-silent` actions are bugged with skim -- Set esc to hide since we aren't using the custom callback@@ -94,6 +96,7 @@ return { FzfLua.win.on_SIGWINCH(opts, "win.unhide", function() if type(opts._contents) == "string" and (opts._resume_reload == true+ ---@diagnostic disable-next-line: need-check-nil or type(opts._resume_reload) == "function" and opts._resume_reload(opts)) then return string.format("reload:%s", opts._contents)
lua/fzf-lua/providers/buffers.lua
@@ -1,5 +1,3 @@----@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local core = require "fzf-lua.core" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils"@@ -62,7 +60,7 @@ local filter_buffers = function(opts, unfiltered) excluded[b] = true elseif opts.no_term_buffers and utils.is_term_buffer(b) then excluded[b] = true- elseif opts.cwd_only and not path.is_relative_to(vim.api.nvim_buf_get_name(b), uv.cwd()) then+ elseif opts.cwd_only and not path.is_relative_to(vim.api.nvim_buf_get_name(b), utils.cwd()) then excluded[b] = true elseif opts.cwd and not path.is_relative_to(vim.api.nvim_buf_get_name(b), opts.cwd) then excluded[b] = true@@ -73,7 +71,7 @@ local filter_buffers = function(opts, unfiltered) if buf_valid and vim.api.nvim_get_option_value("ft", { buf = b }) == "qf" then excluded[b] = not opts.show_quickfix and true or nil end- if not excluded[b] and b > max_bufnr then ---@diagnostic disable-next-line: assign-type-mismatch+ if not excluded[b] and b > max_bufnr then max_bufnr = b end return not excluded[b]@@ -164,7 +162,7 @@ local function gen_buffer_entry(opts, buf, max_bufnr, cwd, prefix) else bname = make_entry.lcol({ filename = bname, lnum = buf.info.lnum }, opts):gsub(":$", "") return make_entry.file(bname,- vim.tbl_extend("force", opts, { cwd = cwd or opts.cwd or uv.cwd() }))+ vim.tbl_extend("force", opts, { cwd = cwd or opts.cwd or utils.cwd() })) end end)() if buf.flag == "%" then@@ -198,7 +196,8 @@ M.buffers = function(opts) if not opts then return end local contents = function(cb)- local filtered, _, max_bufnr = filter_buffers(opts, utils.CTX().buflist)+ local buflist = assert(utils.CTX().buflist)+ local filtered, _, max_bufnr = filter_buffers(opts, buflist) if next(filtered) then local buffers = populate_buffer_entries(opts, filtered)@@ -260,7 +259,7 @@ M.buffer_lines = function(opts) local co = coroutine.running() local buffers = filter_buffers(opts,- opts.current_buffer_only and { utils.CTX().bufnr } or utils.CTX().buflist)+ opts.current_buffer_only and { utils.CTX().bufnr } or utils.CTX().buflist or {}) if opts.sort_lastused and utils.tbl_count(buffers) > 1 then table.sort(buffers, function(a, b)@@ -331,7 +330,6 @@ M.buffer_lines = function(opts) end end - assert(opts.hls) for i = 1, lines do local lnum = i + offset if lnum > lines then@@ -419,7 +417,6 @@ M.tabs = function(opts) return ret end)() - assert(opts.hls) for tabnr, tabh in ipairs(vim.api.nvim_list_tabpages()) do (function() if opts.current_tab_only and tabh ~= utils.CTX().tabh then return end@@ -429,7 +426,7 @@ M.tabs = function(opts) local title, fn_title_hl = opt_hl(tabnr, "tab_title", function(s) return string.format("%s%s#%d%s", s, utils.nbsp, tabnr,- (uv.cwd() == tab_cwd and "" or string.format(": %s", tab_cwd_tilde)))+ (utils.cwd() == tab_cwd and "" or string.format(": %s", tab_cwd_tilde))) end, utils.ansi_codes[opts.hls.tab_title]) @@ -582,7 +579,7 @@ M.treesitter = function(opts) local lnum, col, _, _ = ts.get_node_range(node.node) local node_text = ts.get_node_text(node.node, bufnr0) local node_kind = node.kind and utils.ansi_from_hl(kind2hl(node.kind), node.kind)- local hls = assert(opts.hls)+ local hls = opts.hls local entry = string.format("[%s]%s%s:%s:%s\t\t[%s] %s", utils.ansi_codes[hls.buf_nr](tostring(bufnr0)), utils.nbsp,@@ -620,7 +617,7 @@ M.spellcheck = function(opts) end -- Default to current buffer- local bufnr0 = tonumber(opts.bufnr) or vim.api.nvim_get_current_buf()+ local bufnr0 = utils.tointeger(opts.bufnr) or vim.api.nvim_get_current_buf() local bufname0 = path.basename(vim.api.nvim_buf_get_name(bufnr0)) if not bufname0 or #bufname0 == 0 then bufname0 = assert(utils.nvim_buf_get_name(bufnr0))@@ -695,13 +692,13 @@ M.spellcheck = function(opts) local _, lead = word:find("^" .. word_separator .. "+") local spell = vim.spell.check(trim(word))[1] if spell then- local hls = assert(opts.hls)+ local hls = opts.hls cb(string.format("[%s]%s%s:%s:%-26s\t\t%s", utils.ansi_codes[hls.buf_nr](tostring(bufnr0)), utils.nbsp, utils.ansi_codes[hls.buf_name](bufname0), utils.ansi_codes[hls.buf_linenr](tostring(lnum)),- utils.ansi_codes[hls.path_colnr](tostring(from + (lead or 0))),+ utils.ansi_codes[hls.path_colnr](tostring(assert(from) + (lead or 0))), trim(word) ), function(err) -- coroutine.resume(co)@@ -711,7 +708,7 @@ M.spellcheck = function(opts) -- coroutine.yield() end end- if from then from = to + 1 end+ if from then from = assert(to) + 1 end until not from end cb(nil)
lua/fzf-lua/providers/colorschemes.lua
@@ -69,7 +69,6 @@ M.colorschemes = function(opts) live = sel[1] vim.cmd("colorscheme " .. sel[1]) end- ---@diagnostic disable-next-line: param-type-mismatch end, opts, "{}") end @@ -155,8 +154,20 @@ M.highlights = function(opts) end +---@class fzf-lua.AsyncDownloadManager.Job+---@field plugin string+---@field args table++---@class fzf-lua.AsyncDownloadManager+---@field path string Path to the download directory+---@field dl_status number Download status code+---@field max_threads number Maximum number of concurrent threads+---@field job_ids table<integer, fzf-lua.AsyncDownloadManager.Job>+---@field job_stack fzf-lua.AsyncDownloadManager.Job[] Stack of jobs local AsyncDownloadManager = Object:extend() +---@param opts table+---@return fzf-lua.AsyncDownloadManager|nil function AsyncDownloadManager:new(opts) self.path = opts.path self.dl_status = tonumber(opts.dl_status)@@ -182,14 +193,15 @@ end function AsyncDownloadManager:destruct() for id, _ in pairs(self.job_ids) do- vim.fn.jobstop(tonumber(id))+ vim.fn.jobstop(id) end end +---@param co thread? function AsyncDownloadManager:jobwait_all(co) local jobs = {} for id, _ in pairs(self.job_ids) do- table.insert(jobs, tonumber(id))+ table.insert(jobs, id) end if #jobs > 0 then vim.fn.jobwait(jobs)@@ -197,6 +209,8 @@ function AsyncDownloadManager:jobwait_all(co) end end +---@param db table<string, table>+---@return boolean function AsyncDownloadManager:load_db(db) -- store db ref and update package params self.db = db@@ -244,6 +258,8 @@ function AsyncDownloadManager:load_db(db) return true end +---@param plugin string+---@return boolean? function AsyncDownloadManager:downloaded(plugin) local info = plugin and self.db[plugin] if not info then return end@@ -251,15 +267,21 @@ function AsyncDownloadManager:downloaded(plugin) return stat and stat.type == "directory" end +---@param plugin string+---@return integer|boolean? function AsyncDownloadManager:downloading(plugin) local info = plugin and self.db[plugin] return info and info.job_id end +---@param plugin string+---@return table function AsyncDownloadManager:get(plugin) return plugin and self.db[plugin] or nil end +---@param plugin string+---@param fn fun(...) function AsyncDownloadManager:set_once_on_exit(plugin, fn) if not plugin or not self.db[plugin] then return end self.db[plugin].on_exit = function(...)@@ -268,12 +290,14 @@ function AsyncDownloadManager:set_once_on_exit(plugin, fn) end end +---@param plugin string function AsyncDownloadManager:jobwait(plugin) local info = plugin and self.db[plugin] if not info or not info.job_id then return end vim.fn.jobwait({ info.job_id }) end +---@param plugin string? function AsyncDownloadManager:delete(plugin) if not plugin or not self.db[plugin] then return end if self:downloaded(plugin) then@@ -281,6 +305,8 @@ function AsyncDownloadManager:delete(plugin) end end +---@param plugin string+---@param job_args table function AsyncDownloadManager:queue(plugin, job_args) if utils.tbl_count(self.job_ids) < self.max_threads then self:jobstart(plugin, job_args)@@ -298,12 +324,14 @@ function AsyncDownloadManager:dequeue() end end +---@param plugin string+---@param job_args table function AsyncDownloadManager:jobstart(plugin, job_args) if not plugin then return end local info = plugin and self.db[plugin] local msg = string.format("%s %s (%s)", job_args[1][2] == "clone" and "Cloning" or "Updating", info.disp_name, info.dir)- local job_id+ local job_id ---@type integer job_args[2] = vim.tbl_extend("keep", job_args[2] or {}, { on_exit = function(_, rc, _)@@ -312,7 +340,7 @@ function AsyncDownloadManager:jobstart(plugin, job_args) -- this calls `coroutine.resume` and resumes fzf's reload input stream info.on_exit(_, rc, _) end- self.job_ids[tostring(job_id)] = nil+ self.job_ids[job_id] = nil self.db[plugin].job_id = nil -- dequeue the next job self:dequeue()@@ -326,11 +354,13 @@ function AsyncDownloadManager:jobstart(plugin, job_args) else -- job started successfully utils.info("%s [path:%s] [job_id:%d]...", msg, path.HOME_to_tilde(assert(info.path)), job_id)- self.job_ids[tostring(job_id)] = { plugin = plugin, args = job_args }+ self.job_ids[job_id] = { plugin = plugin, args = job_args } self.db[plugin].job_id = job_id end end +---@param plugin string+---@return nil function AsyncDownloadManager:update(plugin) local info = plugin and self.db[plugin] if not info then return end@@ -365,7 +395,7 @@ M.apply_awesome_theme = function(dbkey, idx, opts) end if cs.vim then ok, out = pcall(vim.api.nvim_exec2, cs.vim, { output = true })- elseif cs.lua then+ elseif cs.lua then ---@diagnostic disable-next-line: need-check-nil ok, out = pcall(function() loadstring(cs.lua)() end) else ok, out = pcall(function() vim.cmd("colorscheme " .. cs.name) end)@@ -400,7 +430,7 @@ M.awesome_colorschemes = function(opts) if not ok then utils.error(string.format("Json decode failed: %s", json_db)) return- end+ end ---@cast json_db table -- save a ref for action opts._apply_awesome_theme = M.apply_awesome_theme@@ -476,7 +506,7 @@ M.awesome_colorschemes = function(opts) opts.fzf_opts["--preview-window"] = "nohidden:right:0" opts.preview = shell.stringify_data(function(sel) if opts.live_preview and sel and sel[1] then- local dbkey, idx = sel[1]:match("^(.-):(%d+):")+ local dbkey, idx = assert(sel[1]:match("^(.-):(%d+):")) if opts._adm:downloaded(dbkey) then -- some colorschemes choose a different theme based on dark|light bg -- restore to the original background when interface was opened
@@ -1,6 +1,3 @@----@diagnostic disable: undefined-field----@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local core = require "fzf-lua.core" local utils = require "fzf-lua.utils" local config = require "fzf-lua.config"@@ -102,7 +99,7 @@ M.breakpoints = function(opts) end -- display relative paths by default- if opts.cwd == nil then opts.cwd = uv.cwd() end+ if opts.cwd == nil then opts.cwd = utils.cwd() end local contents = function(cb) coroutine.wrap(function()@@ -144,6 +141,10 @@ M.variables = function(opts) utils.info("No active DAP session.") return end+ if not session.current_frame then+ utils.info("No current frame.")+ return+ end local entries = {} for _, s in pairs(session.current_frame.scopes or {}) do@@ -206,11 +207,11 @@ M.frames = function(opts) return {} end if f.source.path then- local path = f.source.path- local fs_stat = vim.uv.fs_stat(path)+ local path0 = f.source.path+ local fs_stat = vim.uv.fs_stat(path0) if fs_stat and fs_stat.type == "file" then return {- path = path,+ path = path0, line = utils.tointeger(f.line), -- col = f.column, }
lua/fzf-lua/providers/diagnostic.lua
@@ -1,5 +1,4 @@----@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop+---@diagnostic disable: need-check-nil, param-type-mismatch local core = require "fzf-lua.core" local utils = require "fzf-lua.utils" local config = require "fzf-lua.config"@@ -41,7 +40,7 @@ M.diagnostics = function(opts) -- required for relative paths presentation if not opts.cwd or #opts.cwd == 0 then- opts.cwd = uv.cwd()+ opts.cwd = utils.cwd() else opts.cwd_only = true end@@ -81,7 +80,6 @@ M.diagnostics = function(opts) if type(sign_confs) ~= "table" or utils.tbl_isempty(sign_confs) then sign_confs = nil end signs0[v.severity].text = (not opts.diag_icons or not sign_confs or not sign_confs.text or not sign_confs.text[level])- ---@diagnostic disable-next-line: need-check-nil and v.default or vim.trim(sign_confs.text[level]) signs0[v.severity].texthl = v.name else
lua/fzf-lua/providers/files.lua
@@ -24,7 +24,7 @@ M.get_files_cmd = function(opts) -- will result in more code routes taken in `make_entry.file` if type(paths) == "table" then for i, p in ipairs(paths) do- paths[i] = libuv.shellescape(path.relative_to(path.normalize(p), uv.cwd()))+ paths[i] = libuv.shellescape(path.relative_to(path.normalize(p), utils.cwd())) end return table.concat(paths, " ") end@@ -80,7 +80,7 @@ M.files = function(opts) if opts.ignore_current_file then local curbuf = vim.api.nvim_buf_get_name(0) if #curbuf > 0 then- curbuf = path.relative_to(curbuf, opts.cwd or uv.cwd())+ curbuf = path.relative_to(curbuf, opts.cwd or utils.cwd()) opts.file_ignore_patterns = opts.file_ignore_patterns or {} table.insert(opts.file_ignore_patterns, "^" .. utils.lua_regex_escape(curbuf) .. "$")@@ -91,7 +91,7 @@ M.files = function(opts) -- `dir` command returns absolute paths with ^M for EOL -- `make_entry.file` will strip the ^M -- set `opts.cwd` for relative path display- opts.cwd = uv.cwd()+ opts.cwd = utils.cwd() end opts = core.set_title_flags(opts, { "cmd" }) return core.fzf_exec(opts.cmd, opts)
@@ -98,9 +98,8 @@ M.diff = function(opts) utils.warn("Invalid git ref %s", opts.ref) return end- for _, k in ipairs({ "cmd", "preview" }) do- opts[k] = opts[k]:gsub("[<{]ref[}>]", opts.ref)- end+ opts.cmd = opts.cmd:gsub("[<{]ref[}>]", opts.ref)+ opts.preview = opts.preview:gsub("[<{]ref[}>]", opts.ref) opts = set_git_cwd_args(opts) if not opts.cwd then return end opts.preview = git_preview(opts, "{-1}")@@ -205,7 +204,8 @@ M.branches = function(opts) -- * branch -- remotes/origin/branch -- (HEAD detached at origin/branch)- local branch = items[1]:match("^[%*+]*[%s]*[(]?([^%s)]+)")+ if not items[1] then return utils.shell_nop() end+ local branch = assert(items[1]:match("^[%*+]*[%s]*[(]?([^%s)]+)")) return (preview:gsub("{.*}", branch)) end, opts, "{}") end@@ -221,6 +221,7 @@ M.worktrees = function(opts) if opts.preview then local preview_cmd = opts.preview opts.preview = shell.stringify_cmd(function(items)+ if not items[1] then return utils.shell_nop() end local cwd = items[1]:match("^[^%s]+") local cmd = path.git_cwd(preview_cmd, { cwd = cwd }) return cmd
lua/fzf-lua/providers/grep.lua
@@ -1,3 +1,4 @@+---@diagnostic disable: param-type-mismatch ---@diagnostic disable-next-line: deprecated local uv = vim.uv or vim.loop local path = require "fzf-lua.path"@@ -135,6 +136,7 @@ end ---@param opts fzf-lua.config.Grep|{}? ---@return thread?, string?, table? M.live_grep = function(opts)+ ---@type fzf-lua.config.Grep opts = normalize_live_grep_opts(opts) if not opts then return end @@ -245,7 +247,7 @@ end ---@param opts fzf-lua.config.Grep|{}? ---@return thread?, string?, table? M.grep_cword = function(opts)- if not opts then opts = {} end+ opts = opts or {} opts.no_esc = true -- match whole words only (#968) opts.search = [[\b]] .. utils.rg_escape(vim.fn.expand("<cword>")) .. [[\b]]@@ -263,7 +265,7 @@ end ---@param opts fzf-lua.config.Grep|{}? ---@return thread?, string?, table? M.grep_visual = function(opts)- if not opts then opts = {} end+ opts = opts or {} opts.search = utils.get_visual_selection() return M.grep(opts) end@@ -271,8 +273,8 @@ end ---@param opts fzf-lua.config.Grep|{}? ---@return thread?, string?, table? M.grep_project = function(opts)- if not opts then opts = {} end- if not opts.search then opts.search = "" end+ opts = opts or {}+ opts.search = opts.search or "" -- by default, do not include filename in search opts.fzf_opts = opts.fzf_opts or {} if opts.fzf_opts["--delimiter"] == nil then@@ -300,7 +302,7 @@ M.grep_curbuf = function(opts, lgrep) utils.info("Rg current buffer requires file on disk") return else- opts.filename = path.relative_to(opts.filename, uv.cwd())+ opts.filename = path.relative_to(opts.filename, utils.cwd()) end -- Persist call options so we don't revert to global grep on `grep_lgrep`
@@ -1,5 +1,3 @@----@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local core = require "fzf-lua.core" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils"@@ -90,7 +88,7 @@ local regex_filter_fn = function(regex_filter) end local function location_handler(opts, cb, _, result, ctx, _)- local encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding+ local encoding = assert(vim.lsp.get_client_by_id(ctx.client_id)).offset_encoding result = utils.tbl_islist(result) and result or { result } -- HACK: make sure target URI is valid for buggy LSPs (#1317) for i, x in ipairs(result) do@@ -115,7 +113,7 @@ local function location_handler(opts, cb, _, result, ctx, _) end if opts.ignore_current_line then local uri = vim.uri_from_bufnr(utils.CTX().bufnr)- local cursor_line = utils.CTX().cursor[1] - 1+ local cursor_line = assert(utils.CTX().cursor[1]) - 1 result = vim.tbl_filter(function(l) if (l.uri and l.uri == uri@@ -160,7 +158,7 @@ local function location_handler(opts, cb, _, result, ctx, _) end local function call_hierarchy_handler(opts, cb, _, result, ctx, _)- local encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding+ local encoding = assert(vim.lsp.get_client_by_id(ctx.client_id)).offset_encoding for _, call_hierarchy_call in pairs(result) do --- "from" for incoming calls and "to" for outgoing calls local call_hierarchy_item = call_hierarchy_call.from or call_hierarchy_call.to@@ -233,7 +231,7 @@ local function symbol_handler(opts, cb, _, result, ctx, _) items = symbols_to_items(opts, result, utils.CTX().bufnr, opts.child_prefix == true and string.rep(" ", 2) or opts.child_prefix) else- local encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding+ local encoding = assert(vim.lsp.get_client_by_id(ctx.client_id)).offset_encoding items = vim.lsp.util.symbols_to_items(result, utils.CTX().bufnr, encoding) end if opts.regex_filter and opts._regex_filter_fn == nil then@@ -434,6 +432,7 @@ local function gen_lsp_contents(opts) if x and jump1 == nil then jump1 = { result = x.result, encoding = x.encoding } end table.insert(results, text) end+ ---@diagnostic disable-next-line: param-type-mismatch for client_id, response in pairs(lsp_results) do if response.result then local context = { client_id = client_id }@@ -591,6 +590,7 @@ local function gen_lsp_contents_hierarchy(opts) if err then utils.error(("Error executing '%s': %s"):format(opts.lsp_handler.prep, err)) else+ ---@diagnostic disable-next-line: param-type-mismatch local _, response = next(res) if not response or not response.result or not response.result[1] then if not opts.silent then@@ -618,7 +618,7 @@ local normalize_lsp_opts = function(opts, cfg, __resume_key) -- required for relative paths presentation if not opts.cwd or #opts.cwd == 0 then- opts.cwd = uv.cwd()+ opts.cwd = utils.cwd() elseif opts.cwd_only == nil then opts.cwd_only = true end
lua/fzf-lua/providers/meta.lua
@@ -42,7 +42,7 @@ local function ls(dir, fn) -- HACK: type is not always returned due to a bug in luv, -- so fecth it with fs_stat instead when needed. -- see https://github.com/folke/lazy.nvim/issues/306- fn(fname, name, t or uv.fs_stat(fname).type)+ fn(fname, name, t or (uv.fs_stat(fname) or {}).type) end end @@ -96,10 +96,10 @@ end M.combine = function(t) t = t or {} - local pickers = type(t.pickers) == "table" and t.pickers- or type(t.pickers) == "string" and utils.strsplit(t.pickers, "[,;]")- or nil+ local pickers = (type(t.pickers) == "table" and t.pickers+ or type(t.pickers) == "string" and utils.strsplit(t.pickers, "[,;]")) + assert(pickers) local opts = t t.pickers = nil @@ -149,6 +149,7 @@ end ---@param opts fzf-lua.config.Global|{}? ---@return thread?, string?, table? M.global = function(opts)+ -- -@type fzf-lua.config.GlobalStrict ---@type fzf-lua.config.Global opts = config.normalize_opts(opts, "global") if not opts then return end@@ -259,6 +260,7 @@ M.global = function(opts) cur_sub = new_sub cur_picker = new_picker end+ if not q or not cur_sub then return end return reload .. string.format("search(%s)", q:sub(cur_sub)) end, opts, "{q}") end
lua/fzf-lua/providers/nvim.lua
@@ -76,7 +76,6 @@ M.commands = function(opts) end opts.flatten = opts.flatten or {}- assert(opts.hls) for k, _ in pairs(global_commands) do table.insert(entries, utils.ansi_codes[opts.hls.cmd_global](k)) add_subcommand(k, utils.ansi_codes[opts.hls.cmd_global])@@ -182,7 +181,8 @@ M.changes_or_jumps = function(opts) local entries = {} for i = #jumps - 1, 3, -1 do- local jump, line, col, text = jumps[i]:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)")+ local entry = jumps[i] ---@cast entry -?+ local jump, line, col, text = entry:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)") table.insert(entries, string.format(" %16s %15s %15s %s", utils.ansi_codes.yellow(jump), utils.ansi_codes.blue(line),@@ -203,6 +203,17 @@ M.changes_or_jumps = function(opts) return core.fzf_exec(entries, opts) end ++---@class fzf-lua.TagStackItem+---@field bufnr integer+---@field from [integer, integer, integer] # {line, col, ...}+---@field matchnr integer+---@field tagname string+---@field filename? string+---@field lnum? integer+---@field col? integer+---@field text? string+ ---@param opts fzf-lua.config.Tagstack|{}? ---@return thread?, string?, table? M.tagstack = function(opts)@@ -210,6 +221,7 @@ M.tagstack = function(opts) opts = config.normalize_opts(opts, "tagstack") if not opts then return end + ---@type fzf-lua.TagStackItem[] local tagstack = vim.fn.gettagstack().items local tags = {}@@ -233,7 +245,7 @@ M.tagstack = function(opts) local entries = {} for i, tag in ipairs(tags) do- local bufname = path.HOME_to_tilde(path.relative_to(tag.filename, uv.cwd()))+ local bufname = path.HOME_to_tilde(path.relative_to(tag.filename, utils.cwd())) local buficon, hl if opts.file_icons then buficon, hl = devicons.get_devicon(bufname)@@ -267,7 +279,6 @@ M.marks = function(opts) opts = config.normalize_opts(opts, "marks") if not opts then return end - assert(opts.hls) local contents = function(cb) local buf = utils.CTX().bufnr local entries = {}@@ -290,7 +301,7 @@ M.marks = function(opts) -- global marks for _, m in ipairs(vim.fn.getmarklist()) do local mark, bufnr, lnum, col, file = m.mark:sub(2, 2), m.pos[1], m.pos[2], m.pos[3], m.file- file = path.relative_to(file, uv.cwd())+ file = path.relative_to(file, utils.cwd()) if path.is_absolute(file) then file = path.HOME_to_tilde(file) end@@ -587,7 +598,7 @@ M.spell_suggest = function(opts) local match = opts.word_pattern or "[^%s\"'%(%)%.%%%+%-%*%?%[%]%^%$:#,]*" local line = vim.api.nvim_get_current_line() local col = vim.api.nvim_win_get_cursor(0)[2] + 1- local before = col > 1 and line:sub(1, col - 1):reverse():match(match):reverse() or ""+ local before = col > 1 and assert(line:sub(1, col - 1):reverse():match(match)):reverse() or "" local after = line:sub(col):match(match) or "" -- special case when the cursor is on the left surrounding char if #before == 0 and #after == 0 and #line > col then@@ -781,7 +792,7 @@ M.serverlist = function(opts) -- Don't list servers twice if not vim.list_contains(listed, socket) then local ok, chan = pcall(vim.fn.sockconnect, "pipe", socket, { rpc = true })- if ok and chan then+ if ok and chan then ---@cast chan integer -- Check that the server is responding if vim.fn.rpcrequest(chan, "nvim_get_chan_info", 0).id then table.insert(found, socket)@@ -804,7 +815,7 @@ M.serverlist = function(opts) end ) :map(function(p)- ----@type boolean, string?+ ---@type boolean, string? local ok, cwd = utils.rpcexec(p, "nvim_exec_lua", "return vim.uv.cwd()", {}) if not ok or not cwd then return end cwd = FzfLua.path.normalize(cwd)
lua/fzf-lua/providers/oldfiles.lua
@@ -36,7 +36,7 @@ M.oldfiles = function(opts) if opts.include_current_session then for _, buffer in ipairs(vim.split(vim.fn.execute(":buffers! t"), "\n")) do- local bufnr = tonumber(buffer:match("%s*(%d+)"))+ local bufnr = utils.tointeger(buffer:match("%s*(%d+)")) if bufnr then local file = vim.api.nvim_buf_get_name(bufnr) local fs_stat = stat_fn(file)@@ -86,7 +86,7 @@ M.oldfiles = function(opts) end -- for 'file_ignore_patterns' to work on relative paths- opts.cwd = opts.cwd or uv.cwd()+ opts.cwd = opts.cwd or utils.cwd() return core.fzf_exec(contents, opts) end
lua/fzf-lua/providers/quickfix.lua
@@ -1,5 +1,4 @@ ---@diagnostic disable-next-line: deprecated-local uv = vim.uv or vim.loop local core = require "fzf-lua.core" local utils = require "fzf-lua.utils" local config = require "fzf-lua.config"@@ -11,7 +10,7 @@ local quickfix_run = function(opts, cfg) opts = config.normalize_opts(opts, cfg) if not opts then return end - if not opts.cwd then opts.cwd = uv.cwd() end+ opts.cwd = opts.cwd or utils.cwd() local function getlist() if opts.__locations then return nil end
lua/fzf-lua/providers/tags.lua
@@ -26,7 +26,7 @@ local function get_tags_cmd(opts) -- tags use relative paths, by now we should -- have the correct cwd from `get_ctags_cwd` query = libuv.shellescape(- utils.rg_escape(path.relative_to(opts.filename, opts.cwd or uv.cwd())))+ utils.rg_escape(path.relative_to(opts.filename, opts.cwd or utils.cwd()))) elseif opts.search and #opts.search > 0 then filter = ([[%s -v "^!"]]):format(bin) query = libuv.shellescape(opts.no_esc and opts.search or@@ -84,7 +84,7 @@ local function tags(opts) -- tags file should always resolve to an absolute path, already "expanded" by -- `get_ctags_file` we take care of the case where `opts.ctags_file = "tags"` if not path.is_absolute(opts._ctags_file) then- opts._ctags_file = path.join({ opts.cwd or uv.cwd(), opts.ctags_file })+ opts._ctags_file = path.join({ opts.cwd or utils.cwd(), opts.ctags_file }) end if not opts.ctags_autogen and not uv.fs_stat(opts._ctags_file) then
lua/fzf-lua/providers/tmux.lua
@@ -10,14 +10,13 @@ local M = {} M.buffers = function(opts) ---@type fzf-lua.config.TmuxBuffers opts = config.normalize_opts(opts, "tmux.buffers")- if not opts then return end- opts.fn_transform = function(x) local buf, data = x:match([[^(.-):%s+%d+%s+bytes: "(.*)"$]]) return string.format("[%s] %s", utils.ansi_codes.yellow(buf), data) end opts.fzf_opts["--preview"] = shell.stringify_cmd(function(items)+ if not items[1] then return utils.shell_nop() end local buf = items[1]:match("^%[(.-)%]") return string.format("tmux show-buffer -b %s", buf) end, opts, "{}")
lua/fzf-lua/providers/ui_select.lua
@@ -181,6 +181,7 @@ M.ui_select = function(items, ui_opts, on_choice) -- multiple keybinds trigger, sending `--expect` to fzf local previewer = _OPTS_ONCE.previewer _OPTS_ONCE.previewer = nil -- can't copy the previewer object+ ---@diagnostic disable-next-line: param-type-mismatch opts = vim.tbl_deep_extend(opts_merge_strategy, _OPTS_ONCE, opts) opts.actions = vim.tbl_deep_extend("force", opts.actions or {}, { ["enter"] = opts.actions.enter })
@@ -16,7 +16,7 @@ end local function new_pipe() local tmp = _is_win and windows_pipename() or vim.fn.tempname()- local socket = uv.new_pipe(false)+ local socket = assert(uv.new_pipe(false)) uv.pipe_bind(socket, tmp) return socket, tmp end
@@ -122,13 +122,13 @@ end ---@param fn function ---@param fzf_field_index string----@param debug boolean|integer+---@param debug? boolean|integer|"v"|"verbose" ---@return string, integer function M.pipe_wrap_fn(fn, fzf_field_index, debug) fzf_field_index = fzf_field_index or "{+}" local receiving_function = function(pipe_path, ...)- local pipe = uv.new_pipe(false)+ local pipe = assert(uv.new_pipe(false)) local args = { ... } -- unescape double backslashes on windows if utils.__IS_WINDOWS and type(args[1]) == "table" then@@ -176,8 +176,8 @@ M.check_upvalue = function(v, varname) require("fzf-lua.lib.base64").encode(string.dump(v, true))) -- Test the function once with nil value (imprefect?) -- to see if there's an issue with upvalue refs- local f = loadstring(str)()- local ok, err = pcall(f)+ local f = assert(loadstring(str))()+ local ok, err = pcall(f) ---@cast err string assert( ok or (not err:match("attempt to index upvalue") and not err:match("attempt to call upvalue")), string.format("multiprocess '%s' cannot have upvalue referecnces", varname))@@ -524,9 +524,10 @@ M.wrap_spawn_stdio = function(opts) local nvim_bin = os.getenv("FZF_LUA_NVIM_BIN") or vim.v.progpath -- TODO: should we check "cmd"? for _, k in ipairs({ "contents", "fn_transform", "fn_preprocess", "fn_postprocess" }) do- if type(opts[k]) == "function" then+ local v = opts[k]+ if type(v) == "function" then ---@cast v function -- opts[k] = M.check_upvalue(opts[k], "opts." .. k)- M.check_upvalue(opts[k], "opts." .. k)+ M.check_upvalue(v, "opts." .. k) end end local cmd_str = ("%s -u NONE -l %s %s"):format(
@@ -34,7 +34,7 @@ end -- global var indicating a headless instance _G._fzf_lua_is_headless = true ---@type fzf-lua.SpawnStdioOpts-local opts = require("fzf-lua.libuv").deserialize(_G.arg[1])+local opts = require("fzf-lua.libuv").deserialize(assert(_G.arg[1])) local _, pid = require("fzf-lua.libuv").spawn_stdio(opts) -- while vim.uv.run() do end -- os.exit in spawn_stdio while uv.os_getpriority(pid) do
lua/fzf-lua/test/previewer.lua
@@ -1,3 +1,4 @@+---@diagnostic disable: param-type-mismatch local fzf = require("fzf-lua") local builtin = require("fzf-lua.previewer.builtin") local M = {}
@@ -3,40 +3,29 @@ error("Cannot require a meta file") _G.FzfLua = require("fzf-lua") ----@class fzf-lua.previewer.SwiperBase----@field new function----@field setup_opts function----@field zero_cmd function?----@field result_cmd function?----@field preview_cmd function?----@field highlight_matches function?----@field win fzf-lua.Win-----@class fzf-lua.previewer.BufferOrFile----@field match_id? integer ---@class fzf-lua.path.Entry ---@field stripped? string+---@field path? string ---@field bufnr? integer ---@field line? integer ---@field col? integer ---@field bufname? string ---@field terminal? boolean----@field path? string ---@field ctag? string ---@field uri? string ---@field range? lsp.Range ---@field debug? string debug information ----@class fzf-lua.buffer_or_file.Entry : fzf-lua.path.Entry, {}----@field do_not_cache boolean?----@field no_scrollbar boolean?----@field tick integer?----@field fs_stat uv.fs_stat.result?----@field no_syntax boolean?----@field cached fzf-lua.buffer_or_file.Bcache?----@field content string[]?----@field filetype string?+---@class fzf-lua.buffer_or_file.Entry : fzf-lua.path.Entry,{}+---@field do_not_cache? boolean+---@field no_scrollbar? boolean+---@field tick? integer+---@field fs_stat? uv.fs_stat.result+---@field no_syntax? boolean+---@field cached? fzf-lua.buffer_or_file.Bcache+---@field content? string[]+---@field filetype? string ---@class fzf-lua.keymap.Entry ---@field vmap string?@@ -155,7 +144,7 @@ _G.FzfLua = require("fzf-lua") ---@field scrollbar? string ---@field scrolloff? integer ---@field delay? integer----@field winopts? fzf-lua.config.PreviewerWinopts+---@field winopts fzf-lua.config.PreviewerWinopts ---@class fzf-lua.config.PreviewerWinopts ---@field number? boolean@@ -220,15 +209,15 @@ _G.FzfLua = require("fzf-lua") ---@class fzf-lua.config.Base ---@field cwd_prompt? boolean ---@field dir_icon? string----@field enrich? fun(opts: fzf-lua.config.Resolved):fzf-lua.config.Resolved|{}+---@field enrich? fun(opts: fzf-lua.config.Resolved|{}):fzf-lua.config.Resolved|{} ---@field fzf_bin? string ---@field fzf_colors? { [1]?: boolean, [string]: string? } ---@field fzf_opts? table<string, any>----@field fzf_tmux_opts? table<string, any>----@field hls? fzf-lua.config.HLS----@field winopts? fzf-lua.config.Winopts|(fun(opts: fzf-lua.Config):fzf-lua.config.Winopts)----@field keymap? fzf-lua.config.Keymap|(fun(opts: fzf-lua.Config):fzf-lua.config.Keymap)----@field actions? fzf-lua.config.Actions|(fun(opts: fzf-lua.Config):fzf-lua.config.Actions)+---@field fzf_tmux_opts table<string, any>+---@field hls fzf-lua.config.HLS+---@field winopts? fzf-lua.config.Winopts|(fun(opts: fzf-lua.config.Resolved):fzf-lua.config.Winopts)+---@field keymap? fzf-lua.config.Keymap|(fun(opts: fzf-lua.config.Resolved):fzf-lua.config.Keymap)+---@field actions? fzf-lua.config.Actions|(fun(opts: fzf-lua.config.Resolved):fzf-lua.config.Actions) ---@field no_header? boolean ---@field no_header_i? boolean ---@field prompt? string@@ -241,7 +230,7 @@ _G.FzfLua = require("fzf-lua") ---@field color_icons? boolean ---@field git_icons? boolean ---@field silent? boolean|integer----@field previewer? fun(...)|table|string+---@field previewer? fzf-lua.config.Previewer|string ---@field preview? string|function|table ---@field complete? (fun(s: string[], _o: fzf-lua.config.Resolved, l: string, c: integer):string?, integer?)|boolean ---@field header string@@ -260,14 +249,14 @@ _G.FzfLua = require("fzf-lua") ---@field cb_co fun(co: thread) ---@field cwd_only boolean ---@field cmd? string----@field debug? boolean|'v'|'verbose'+---@field debug? boolean|integer|'v'|'verbose' ---@field preview_offset? string ---@field _fzf_cli_args? string[]----@field __INFO? fzf-lua.Info----@field __CTX? fzf-lua.Ctx|{}+---@field __INFO fzf-lua.Info+---@field __CTX fzf-lua.Ctx ---@field _normalized? boolean----@field __call_fn? function----@field __call_opts? table+---@field __call_fn function+---@field __call_opts table ---@field is_live? boolean is "live" picker ---@field contents? fzf-lua.content|fzf-lua.shell.data2 ---@field _actions? fun():fzf-lua.config.Actions?@@ -281,6 +270,7 @@ _G.FzfLua = require("fzf-lua") ---@field field_index_expr? string ---@field _ctag? string ---@field preview_pager? string+---@field toggle_flag? string ---@class fzf-lua.config.Resolved: fzf-lua.config.Base ---@field PidObject? table@@ -305,11 +295,15 @@ _G.FzfLua = require("fzf-lua") ---@field __sigwinches? string[] ---@field __sigwinch_on_scope table<string, function> ---@field __sigwinch_on_any function[]---- ---@field process1? boolean ---@field profiler? boolean ---@field use_queue? boolean ---@field throttle? boolean ---@field env? boolean----@field winopts? fzf-lua.config.Winopts+---@field winopts fzf-lua.config.WinoptsResolved+---@field actions fzf-lua.config.Actions+---@field keymap fzf-lua.config.Keymap+---@field fzf_opts table<string, any> ---@field _resume_reload? boolean+---@field _fzf_cli_args string[]+---@field _uri? boolean
@@ -604,6 +604,11 @@ function M.tointeger(x) end end +---@return string+function M.cwd()+ return (assert(uv.cwd()))+end+ local function hex2rgb(hexcol) local r, g, b = hexcol:match("#(%x%x)(%x%x)(%x%x)") if not r or not g or not b then return end@@ -891,12 +896,13 @@ function M.fzf_exit() require("fzf-lua").win.win_leave() end +---@return fzf-lua.Win? function M.fzf_winobj() return require("fzf-lua").win.__SELF() end ---@param opts? { includeBuflist?: boolean, buf?: integer|string, bufnr?: integer|string }----@return fzf-lua.Ctx|{}+---@return fzf-lua.Ctx function M.CTX(opts) return require("fzf-lua.ctx").refresh(opts) end@@ -936,7 +942,7 @@ end ---@param fname string ---@param name string|nil----@param silent boolean|integer+---@param silent? boolean|integer ---@return table? function M.load_profile_fname(fname, name, silent) local profile = name or vim.fn.fnamemodify(fname, ":t:r") or "<unknown>"@@ -1015,7 +1021,7 @@ function M.is_term_buffer(bufnr) bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr local winid = vim.fn.bufwinid(bufnr) if tonumber(winid) > 0 and vim.api.nvim_win_is_valid(winid) then- return M.getwininfo(winid).terminal == 1+ return M.getwininfo(winid) --[[@cast -?]].terminal == 1 end local bufname = vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_get_name(bufnr) return M.is_term_bufname(bufname)@@ -1041,8 +1047,10 @@ function M.buffer_is_dirty(bufnr, warn, only_if_last_buffer) return false end +---@param bufnr integer+---@return boolean function M.save_dialog(bufnr)- bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf()+ bufnr = bufnr or vim.api.nvim_get_current_buf() local info = bufnr and M.getbufinfo(bufnr) if not info.name or #info.name == 0 then -- unnamed buffers can't be saved@@ -1085,7 +1093,7 @@ function M.buf_is_qf(bufnr, bufinfo) if bufinfo and bufinfo.variables and bufinfo.variables.current_syntax == "qf" and not M.tbl_isempty(bufinfo.windows) then- local window = bufinfo.windows[1]+ local window = bufinfo.windows --[[@cast -?]][1] ---@cast window integer return M.win_is_qf(window) end@@ -1143,6 +1151,9 @@ function M.nvim_buf_get_name(bufnr, bufinfo) end -- correctly handle virtual text, conceal lines+---@param win integer+---@param buf integer+---@return integer function M.line_count(win, buf) if vim.api.nvim_win_text_height then return vim.api.nvim_win_text_height(win, {}).all
@@ -120,6 +120,7 @@ end ---@field width integer ---@field row integer ---@field col integer+---@field zindex integer ---@field __winhls { main: [string, string][], prev: [string, string][] } ---@class fzf-lua.config.Winopts: vim.api.keyset.win_config@@ -127,7 +128,7 @@ end ---@field width? number ---@field row? number ---@field col? number----@field preview? fzf-lua.config.PreviewOpts+---@field preview fzf-lua.config.PreviewOpts ---@field split? string|function|false ---@field backdrop? number|boolean ---@field fullscreen? boolean@@ -141,20 +142,21 @@ end ---@field __winhls? { main: [string, string?][], prev: [string, string?][] } ---@class fzf-lua.Win----@field winopts fzf-lua.config.Winopts+---@field winopts fzf-lua.config.WinoptsResolved ---@field actions fzf-lua.config.Actions|{} ---@field layout fzf-lua.WinLayout+---@field fzf_bufnr integer+---@field fzf_winid integer ---@field preview_hidden? boolean ---@field preview_wrap? boolean ---@field km_winid? integer ---@field km_bufnr? integer ---@field fullscreen? boolean----@field hls? fzf-lua.config.HLS+---@field hls fzf-lua.config.HLS ---@field previewer? fun(...)|table|string----@field fzf_bufnr? integer ---@field _hidden_fzf_bufnr? integer ---@field toggle_behavior? "extend"|"default"----@field _previewer? fzf-lua.previewer.Builtin|fzf-lua.previewer.Fzf?+---@field _previewer? fzf-lua.previewer.Builtin|fzf-lua.previewer.Fzf ---@field _preview_pos_force? fzf-lua.win.previewPos ---@field _last_view? [integer, integer, integer]? ---@field _o fzf-lua.config.Resolved@@ -357,12 +359,14 @@ function FzfWin:generate_layout() height = wininfo.height, width = wininfo.width, split = winopts.split,+ row = 0,+ col = 0, } end local pwopts local row, col = winopts.row, winopts.col- local height, width = winopts.height, winopts.width ---@type integer, integer+ local height, width = winopts.height, winopts.width local preview_pos, preview_size = layout.pos, layout.size local pborder, ph, pw = self:normalize_border(self._o.winopts.preview.border, { type = "nvim", name = "prev", layout = preview and layout.pos, nwin = nwin, opts = self._o })@@ -479,7 +483,7 @@ function FzfWin:tmux_columns() -- fzf's defaults to "--tmux" is "center,50%" or "50%" for splits if is_popup or is_hsplit then local percent = type(opt_val) == "string" and tonumber(opt_val:match("(%d+)%%")) or 50- cols = math.floor(cols * percent / 100)+ cols = math.floor(assert(cols) * percent / 100) end return cols end@@ -507,7 +511,7 @@ end ---@field name? 'fzf'|'prev' ---@field nwin? integer ---@field layout? fzf-lua.win.previewPos----@field opts? fzf-lua.config.Resolved+---@field opts? fzf-lua.config.Resolved|{} ---@alias fzf-lua.winborder string[]|"none"|"single"|"double"|"rounded"|"solid"|"shadow" @@ -657,7 +661,7 @@ function FzfWin:normalize_winopts() -- convert cursor relative to absolute ('editor'), -- this solves the preview positioning seamlessly -- use the calling window context for correct pos- local winid = assert(utils.CTX().winid)+ local winid = utils.CTX().winid local pos = api.nvim_win_get_cursor(winid) local screenpos = fn.screenpos(winid, pos[1], pos[2]) winopts.row = math.floor((winopts.row or 0) + screenpos.row - 1)@@ -682,12 +686,14 @@ function FzfWin:reset_win_highlights(win) -- derive the highlights from the window type local key = "main" local hl- if win == self.preview_winid then+ if self._previewer+ and self._previewer.gen_winopts+ and win == self.preview_winid then key = "prev" hl = self._previewer:gen_winopts().winhl end if not hl then- for _, h in ipairs(assert(self.winopts.__winhls)[key]) do+ for _, h in ipairs(self.winopts.__winhls[key]) do if h[2] then hl = string.format("%s%s:%s", hl and hl .. "," or "", h[1], h[2]) end@@ -836,9 +842,7 @@ function FzfWin.new(o) self.winopts = self:normalize_winopts() -- Backward compat since removal of "border" scrollbar if self.winopts.preview.scrollbar == "border" then- ---@diagnostic disable-next-line: assign-type-mismatch self.hls.scrollfloat_f = false- ---@diagnostic disable-next-line: undefined-field -- Reverse "FzfLuaScrollBorderFull" color local scrollborder_f = self.hls.scrollborder_f if type(scrollborder_f) == "string" then@@ -915,8 +919,7 @@ end function FzfWin:validate_preview() return not self.closing- and tonumber(self.preview_winid)- and self.preview_winid > 0+ and self.preview_winid and api.nvim_win_is_valid(self.preview_winid) end @@ -924,6 +927,7 @@ function FzfWin:redraw_preview() if not self.previewer_is_builtin or self.preview_hidden then return end+ local previewer = self._previewer ---@cast previewer fzf-lua.previewer.Builtin -- Close the exisiting scrollbar self:close_preview_scrollbar()@@ -935,11 +939,11 @@ function FzfWin:redraw_preview() if self:validate_preview() then -- since `nvim_win_set_config` removes all styling, save backup -- of the current options and restore after the call (#813)- local style = self:get_winopts(self.preview_winid, self._previewer:gen_winopts())+ local style = self:get_winopts(self.preview_winid, previewer:gen_winopts()) api.nvim_win_set_config(self.preview_winid, self.layout.preview) self:set_winopts(self.preview_winid, style) else- local tmp_buf = self._previewer:get_tmp_buffer()+ local tmp_buf = previewer:get_tmp_buffer() -- No autocmds, can only be sent with 'nvim_open_win' self.preview_winid = api.nvim_open_win(tmp_buf, false, vim.tbl_extend("force", self.layout.preview, { noautocmd = true }))@@ -947,8 +951,8 @@ function FzfWin:redraw_preview() api.nvim_win_set_var(self.preview_winid, "fzf_lua_preview", true) end self:reset_win_highlights(self.preview_winid)- self._previewer:display_last_entry()- self._previewer:update_ts_context()+ previewer:display_last_entry()+ previewer:update_ts_context() end function FzfWin:validate()@@ -987,7 +991,8 @@ function FzfWin:redraw_main() if self:validate() then if self._previewer and self._previewer.clear_on_redraw- and self._previewer.clear_preview_buf then+ and self._previewer.clear_preview_buf+ and self._previewer.clear_cached_buffers then self._previewer:clear_preview_buf(true) self._previewer:clear_cached_buffers() end@@ -1447,7 +1452,7 @@ function FzfWin:was_hidden() end ---SIGWINCH/on_SIGWINCH is nop if fzf < v0.46----@param opts fzf-lua.config.Resolved+---@param opts fzf-lua.config.Resolved|{} ---@param scope string? nil means on any sigwinch ---@param cb function ---@return boolean?@@ -1568,8 +1573,9 @@ function FzfWin:update_preview_scrollbar() end local scrolloff = self.winopts.preview.scrollbar == "border"+ and self.layout.preview and self.layout.preview.border ~= "none" and 0- or tonumber(self.winopts.preview.scrolloff) or -1+ or self.winopts.preview.scrolloff or -1 local empty = { style = "minimal",@@ -1669,9 +1675,11 @@ function FzfWin:update_main_title(title) end function FzfWin:update_preview_title(title)- if type(title) ~= "string" and type(title) ~= "table" or not self.layout.preview then+ if type(title) ~= "string" and type(title) ~= "table" or not self.layout.preview+ or not self._previewer then return end+ assert(self._previewer.gen_winopts) -- since `nvim_win_set_config` removes all styling, save backup -- of the current options and restore after the call (#813) local style = self:get_winopts(self.preview_winid, self._previewer:gen_winopts())
@@ -7,6 +7,7 @@ if vim.fn.has("nvim-0.9") ~= 1 then end vim.api.nvim_create_user_command("FzfLua", function(opts)+ ---@diagnostic disable-next-line: param-type-mismatch require("fzf-lua.cmd").run_command(unpack(opts.fargs)) end, { nargs = "*",
@@ -39,7 +39,6 @@ for _, config_path in ipairs(config_paths) do end _G.fzf_jobstart = function(cmd, opts)- ---@diagnostic disable-next-line: missing-fields, missing-parameter FzfLua.libuv.uv_spawn(cmd[1], { cwd = opts.cwd, args = vim.list_slice(cmd, 2),
@@ -1,3 +1,4 @@+---@diagnostic disable: param-type-mismatch -- Add current directory to 'runtimepath' to be able to use 'lua' files vim.opt.runtimepath:append(vim.uv.cwd())
@@ -7,6 +7,7 @@ local eq = helpers.expect.equality local new_set = MiniTest.new_set local exec_lua = child.lua +---@diagnostic disable-next-line: param-type-mismatch local _devicons_path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "nvim-web-devicons") if not vim.uv.fs_stat(_devicons_path) then _devicons_path = vim.fs.joinpath("deps", "nvim-web-devicons")
@@ -61,6 +61,7 @@ T["files"]["previewer"] = new_set({ parametrize = { { false }, { true } } }) T["files"]["previewer"]["builtin"] = new_set({ parametrize = { { "ci" }, { "builtin" } } }, { function(icons, previewer) if icons then+ ---@diagnostic disable-next-line: param-type-mismatch local path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "mini.nvim") if not vim.uv.fs_stat(path) then path = vim.fs.joinpath("deps", "mini.nvim")@@ -102,6 +103,7 @@ T["files"]["icons"]["defaults"] = new_set({ parametrize = { { "+attrs" }, { "-at attrs = attrs == "+attrs" and true or false if attrs then helpers.SKIP_IF_WIN() end local plugin = icons == "mini" and "mini.nvim" or "nvim-web-devicons"+ ---@diagnostic disable-next-line: param-type-mismatch local path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", plugin) if not vim.uv.fs_stat(path) then path = vim.fs.joinpath("deps", plugin)
@@ -20,7 +20,7 @@ T["grep"]["search"]["regex"] = new_set({ parametrize = { { false }, { true }, { ignore_text = helpers.IS_WIN() and { 4, 6, 9, 10, 12, 18, 28 } or { 28 }, normalize_paths = helpers.IS_WIN() }- local path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "nvim-web-devicons")+ local path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "nvim-web-devicons") ---@diagnostic disable-line: param-type-mismatch if not vim.uv.fs_stat(path) then path = vim.fs.joinpath("deps", "nvim-web-devicons") end
@@ -6,11 +6,13 @@ local eq = helpers.expect.equality local new_set = MiniTest.new_set local exec_lua = child.lua +---@diagnostic disable-next-line: param-type-mismatch local _mini_path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "mini.nvim") if not vim.uv.fs_stat(_mini_path) then _mini_path = vim.fs.joinpath("deps", "mini.nvim") end +---@diagnostic disable-next-line: param-type-mismatch local _devicons_path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "nvim-web-devicons") if not vim.uv.fs_stat(_devicons_path) then _devicons_path = vim.fs.joinpath("deps", "nvim-web-devicons")
@@ -8,6 +8,7 @@ local new_set = MiniTest.new_set local exec_lua = child.lua -- Setup mini.icons locally+---@diagnostic disable-next-line: param-type-mismatch local _mini_path = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy", "mini.nvim") if not vim.uv.fs_stat(_mini_path) then _mini_path = vim.fs.joinpath("deps", "mini.nvim")
@@ -10,6 +10,7 @@ describe("Testing shell module", function() it("check_upvalue", function() ok(shell.check_upvalue, function() end, "no upvalue") + ---@diagnostic disable: need-check-nil err(shell.check_upvalue, (function() local upvalue ---@diagnostic disable-next-line: undefined-field@@ -20,5 +21,6 @@ describe("Testing shell module", function() local upvalue return function() return upvalue() end end)(), "call upvalue")+ ---@diagnostic enable: need-check-nil end) end)
@@ -111,7 +111,7 @@ T["win"]["hide"]["can resume after close CTX win (#1936)"] = function() -- can :wqa when there're hide job #1817 pcall(child.cmd, [[wqa]]) -- child.is_running() didn't work as expected- eq(vim.fn.jobwait({ child.job.id }, 1000)[1], 0)+ eq(vim.fn.jobwait({ assert(child.job).id }, 1000)[1], 0) end T["win"]["hide"]["actions on multi-select but zero-match #1961"] = function()@@ -133,7 +133,7 @@ end T["win"]["keymap"] = new_set({ n_retry = not helpers.IS_LINUX() and 5 or nil }) T["win"]["keymap"]["no error"] = new_set({- parametrize = vim.iter(assert(require("fzf-lua.defaults").defaults.keymap).builtin)+ parametrize = vim.iter(require("fzf-lua.defaults").defaults.keymap.builtin) :map(function(key, action) return { key, action } end) :totable() }, {