local core = require "fzf-lua.core" local path = require "fzf-lua.path" local utils = require "fzf-lua.utils" local shell = require "fzf-lua.shell" local config = require "fzf-lua.config" local make_entry = require "fzf-lua.make_entry" local M = {} M.commands = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.commands) if not opts then return end local global_commands = vim.api.nvim_get_commands {} local buf_commands = vim.api.nvim_buf_get_commands(0, {}) local commands = vim.tbl_extend('force', {}, global_commands, buf_commands) local prev_act = shell.action(function (args) local cmd = args[1] if commands[cmd] then cmd = vim.inspect(commands[cmd]) end return cmd end, nil, opts.debug) local entries = {} for k, _ in pairs(global_commands) do table.insert(entries, utils.ansi_codes.magenta(k)) end for k, v in pairs(buf_commands) do if type(v) == 'table' then table.insert(entries, utils.ansi_codes.green(k)) end end table.sort(entries, function(a, b) return a", "", "execute") history(opts, "cmd") end M.search_history = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.search_history) if not opts then return end opts.fzf_opts['--header'] = arg_header("", "", "search") history(opts, "search") end M.changes = function(opts) opts = opts or {} opts.cmd = "changes" opts.prompt = opts.prompt or "Changes> " return M.jumps(opts) end M.jumps = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.jumps) if not opts then return end local jumps = vim.fn.execute(opts.cmd) jumps = vim.split(jumps, "\n") local entries = {} for i = #jumps-1, 3, -1 do local jump, line, col, text = jumps[i]:match("(%d+)%s+(%d+)%s+(%d+)%s+(.*)") table.insert(entries, string.format("%-15s %-15s %-15s %s", utils.ansi_codes.yellow(jump), utils.ansi_codes.blue(line), utils.ansi_codes.green(col), text)) end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.tagstack = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.tagstack) if not opts then return end local tagstack = vim.fn.gettagstack().items local tags = {} for i = #tagstack, 1, -1 do local tag = tagstack[i] tag.bufnr = tag.from[1] if vim.api.nvim_buf_is_valid(tag.bufnr) then tags[#tags + 1] = tag tag.filename = vim.fn.bufname(tag.bufnr) tag.lnum = tag.from[2] tag.col = tag.from[3] tag.text = vim.api.nvim_buf_get_lines(tag.bufnr, tag.lnum - 1, tag.lnum, false)[1] or "" end end if vim.tbl_isempty(tags) then utils.info("No tagstack available") return end local entries = {} for i, tag in ipairs(tags) do local bufname = path.HOME_to_tilde( path.relative(tag.filename, vim.loop.cwd())) local buficon, hl if opts.file_icons then local filename = path.tail(bufname) local extension = path.extension(filename) buficon, hl = make_entry.get_devicon(filename, extension) if opts.color_icons then buficon = utils.ansi_codes[hl](buficon) end end -- table.insert(entries, ("%s)%s[%s]%s%s%s%s:%s:%s: %s %s"):format( table.insert(entries, ("%s)%s%s%s%s:%s:%s: %s %s"):format( utils.ansi_codes.yellow(tostring(i)), utils.nbsp, -- utils.ansi_codes.yellow(tostring(tag.bufnr)), -- utils.nbsp, buficon or '', buficon and utils.nbsp or '', utils.ansi_codes.magenta(#bufname>0 and bufname or "[No Name]"), utils.ansi_codes.green(tostring(tag.lnum)), tag.col, utils.ansi_codes.red("["..tag.tagname.."]"), tag.text)) end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.marks = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.marks) if not opts then return end local marks = vim.fn.execute("marks") marks = vim.split(marks, "\n") --[[ local prev_act = shell.action(function (args, fzf_lines, _) local mark = args[1]:match("[^ ]+") local bufnr, lnum, _, _ = unpack(vim.fn.getpos("'"..mark)) if vim.api.nvim_buf_is_loaded(bufnr) then return vim.api.nvim_buf_get_lines(bufnr, lnum, fzf_lines+lnum, false) else local name = vim.fn.expand(args[1]:match(".* (.*)")) if vim.fn.filereadable(name) ~= 0 then return vim.fn.readfile(name, "", fzf_lines) end return "UNLOADED: " .. name end end) ]] local entries = {} for i = #marks, 3, -1 do local mark, line, col, text = marks[i]:match("(.)%s+(%d+)%s+(%d+)%s+(.*)") table.insert(entries, string.format("%-15s %-15s %-15s %s", utils.ansi_codes.yellow(mark), utils.ansi_codes.blue(line), utils.ansi_codes.green(col), text)) end table.sort(entries, function(a, b) return a ["\27"] = "^[", -- ["\18"] = "^R", -- } for k, v in pairs(gsub_map) do reg = reg:gsub(k, utils.ansi_codes.magenta(v)) end return not nl and reg or reg:gsub("\n", utils.ansi_codes.magenta("\\n")) end local prev_act = shell.action(function (args) local r = args[1]:match("%[(.*)%] ") local _, contents = pcall(vim.fn.getreg, r) return contents and register_escape_special(contents) or args[1] end, nil, opts.debug) local entries = {} for _, r in ipairs(registers) do -- pcall as this could fail with: -- E5108: Error executing lua Vim:clipboard: -- provider returned invalid data local _, contents = pcall(vim.fn.getreg, r) contents = register_escape_special(contents, true) if (contents and #contents > 0) or not opts.ignore_empty then table.insert(entries, string.format("[%s] %s", utils.ansi_codes.yellow(r), contents)) end end opts.fzf_opts['--no-multi'] = '' opts.fzf_opts['--preview'] = prev_act core.fzf_exec(entries, opts) end M.keymaps = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.keymaps) if not opts then return end local modes = { n = "blue", i = "red", c = "yellow" } local keymaps = {} local add_keymap = function(keymap) -- hijack fields local keymap_desc = keymap.desc == nil and keymap.rhs or keymap.desc keymap.str = string.format("%s │ %-40s │ %s", utils.ansi_codes[modes[keymap.mode] or "blue"](keymap.mode), keymap.lhs:gsub("%s", ""), keymap_desc .. ' ') local k = string.format("[%s:%s:%s]", keymap.buffer, keymap.mode, keymap.lhs) keymaps[k] = keymap end for mode, _ in pairs(modes) do local global = vim.api.nvim_get_keymap(mode) for _, keymap in pairs(global) do add_keymap(keymap) end local buf_local = vim.api.nvim_buf_get_keymap(0, mode) for _, keymap in pairs(buf_local) do add_keymap(keymap) end end local entries = {} for _, v in pairs(keymaps) do table.insert(entries, v.str) end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.spell_suggest = function(opts) -- if not vim.wo.spell then return false end opts = config.normalize_opts(opts, config.globals.nvim.spell_suggest) if not opts then return end local cursor_word = vim.fn.expand "" local entries = vim.fn.spellsuggest(cursor_word) if vim.tbl_isempty(entries) then return end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.filetypes = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.filetypes) if not opts then return end local entries = vim.fn.getcompletion('', 'filetype') if vim.tbl_isempty(entries) then return end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.packadd = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.packadd) if not opts then return end local entries = vim.fn.getcompletion('', 'packadd') if vim.tbl_isempty(entries) then return end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end M.menus = function(opts) opts = config.normalize_opts(opts, config.globals.nvim.menus) if not opts then return end -- @param prefix will be prepended to the entry name local function gen_menu_entries(prefix, entry) local name = prefix and ("%s.%s"):format(prefix, entry.name) or entry.name if entry.submenus then -- entry.submenus is a list of {} return vim.tbl_map( function(x) return gen_menu_entries(name, x) end, entry.submenus) else -- if we reached a leaf return name end end local entries = vim.tbl_flatten(vim.tbl_map( function (x) return gen_menu_entries(nil, x) end, vim.fn.menu_get(''))) if vim.tbl_isempty(entries) then utils.info("No menus available") return end opts.fzf_opts['--no-multi'] = '' core.fzf_exec(entries, opts) end return M