From 7b38d5c0b90b1777ac7957228fa5c0000d23afc5 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Fri, 5 Apr 2024 09:02:46 -0700 Subject: [PATCH] WIP --- .luarc.jsonc | 5 +- OPTIONS.md | 70 ++++++++++++++++ doc/fzf-lua-opts.txt | 120 ++++++++++++++++++++++++++ lua/fzf-lua/cmd.lua | 182 ++++++++++++++++++++++------------------ lua/fzf-lua/cmp_src.lua | 115 +++++++++++++++++++++++++ lua/fzf-lua/config.lua | 14 +++- lua/fzf-lua/init.lua | 6 +- plugin/fzf-lua.lua | 85 ++----------------- plugin/fzf-lua.vim | 21 ++--- 9 files changed, 438 insertions(+), 180 deletions(-) create mode 100644 OPTIONS.md create mode 100644 doc/fzf-lua-opts.txt create mode 100644 lua/fzf-lua/cmp_src.lua diff --git a/.luarc.jsonc b/.luarc.jsonc index 6bc8764e..8f4265dc 100644 --- a/.luarc.jsonc +++ b/.luarc.jsonc @@ -15,7 +15,10 @@ "$VIMRUNTIME/lua", "${3rd}/luv/library", "$XDG_DATA_HOME/nvim/lazy/plenary.nvim/lua", - "$LOCALAPPDATA/nvim-data/lazy/plenary.nvim/lua" + "$LOCALAPPDATA/nvim-data/lazy/plenary.nvim/lua", + // For "cmp_src.lua" type resolving + "$XDG_DATA_HOME/nvim/lazy/nvim-cmp/lua", + "$LOCALAPPDATA/nvim-data/lazy/nvim-cmp/lua" ], "checkThirdParty": false, "maxPreload": 2000, diff --git a/OPTIONS.md b/OPTIONS.md new file mode 100644 index 00000000..5c4d1690 --- /dev/null +++ b/OPTIONS.md @@ -0,0 +1,70 @@ +# Fzf-Lua Options + +## Setup + +## Globals + +#### winopts.row + +Type: `number`, Default: `0.35` + +Screen row where to place the fzf-lua float window, between 0-1 will represent precentage of `vim.o.lines` (0: top, 1: bottom), if >= 1 will attempt to place the float in the exact screen line. + +#### winopts.col + +Type: `number`, Default: `0.55` + +Screen column where to place the fzf-lua float window, between 0-1 will represent precentage of `vim.o.columns` (0: leftmost, 1: rightmost), if >= 1 will attempt to place the float in the exact screen column. + +#### winopts.preview.border + +Type: `string`, Default: `border` + +Applies only to fzf native previewers (i.e. `bat`, `git_status`), set to `noborder` to hide the preview border, consult `man fzf` for all vailable options. + +### Cmd: files + +Files picker, will enumrate the filesystem of the current working directory using `fd`, `rg` and `grep` or `dir.exe`. + +#### files.cwd + +Type: `string`, Default: `nil` + +Sets the current working directory. + +#### files.cwd_prompt + +Type: `boolean`, Default: `true` + +Display the current working directory in the prompt (`fzf.vim` style). + +#### files.cwd_prompt_shorten_len + +Type: `number`, Default: `32` + +Prompt over this length will be shortened, e.g. `~/.config/nvim/lua/` will be shortened to `~/.c/n/lua/` (for more info see `:help pathshorten`). + +*Requires `cwd_prompt=true` + +#### files.cwd_prompt_shorten_val + +Type: `number`, Default: `1` + +Length of shortened prompt path parts, e.g. set to `2`, `~/.config/nvim/lua/` will be shortened to `~/.co/nv/lua/` (for more info see `:help pathshorten`). + +*Requires `cwd_prompt=true` + +### Cmd: LSP commands + +#### lsp_references + +LSP references + +#### async_or_timeout + +Type: `number|boolean`, Default: `5000` + +Whether LSP calls are made block, set to `true` for asynchronous, otherwise defines the timeout +(ms) for the LPS request via `vim.lsp.buf_request_sync`. + + diff --git a/doc/fzf-lua-opts.txt b/doc/fzf-lua-opts.txt new file mode 100644 index 00000000..723a4a4b --- /dev/null +++ b/doc/fzf-lua-opts.txt @@ -0,0 +1,120 @@ +*fzf-lua-opts.txt* For Neovim >= 0.8.0 Last change: 2024 April 06 + +============================================================================== +Table of Contents *fzf-lua-opts-table-of-contents* + +Setup ................................................... |fzf-lua-opts-setup| +Globals ............................................... |fzf-lua-opts-globals| +Cmd: files ......................................... |fzf-lua-opts-cmd:-files| +Cmd: LSP commands ........................... |fzf-lua-opts-cmd:-lsp-commands| + +============================================================================== +FZF-LUA OPTIONS *fzf-lua-opts-fzf-lua-options* + + + +------------------------------------------------------------------------------ +SETUP *fzf-lua-opts-setup* + + + +------------------------------------------------------------------------------ +GLOBALS *fzf-lua-opts-globals* + + + + +winopts.row *fzf-lua-opts-winopts.row* + +Type: `number`, Default: `0.35` + +Screen row where to place the fzf-lua float window, between 0-1 will represent +precentage of `vim.o.lines` (0: top, 1: bottom), if >= 1 will attempt to place +the float in the exact screen line. + + + +winopts.col *fzf-lua-opts-winopts.col* + +Type: `number`, Default: `0.55` + +Screen column where to place the fzf-lua float window, between 0-1 will +represent precentage of `vim.o.columns` (0: leftmost, 1: rightmost), if >= 1 +will attempt to place the float in the exact screen column. + + + +winopts.preview.border *fzf-lua-opts-winopts.preview.border* + +Type: `string`, Default: `border` + +Applies only to fzf native previewers (i.e. `bat`, `git_status`), set to +`noborder` to hide the preview border, consult `man fzf` for all vailable +options. + + + +CMD: FILES *fzf-lua-opts-cmd:-files* + +Files picker, will enumrate the filesystem of the current working directory +using `fd`, `rg` and `grep` or `dir.exe`. + + + +files.cwd *fzf-lua-opts-files.cwd* + +Type: `string`, Default: `nil` + +Sets the current working directory. + + + +files.cwd_prompt *fzf-lua-opts-files.cwd_prompt* + +Type: `boolean`, Default: `true` + +Display the current working directory in the prompt (`fzf.vim` style). + + + +files.cwd_prompt_shorten_len *fzf-lua-opts-files.cwd_prompt_shorten_len* + +Type: `number`, Default: `32` + +Prompt over this length will be shortened, e.g. `~/.config/nvim/lua/` will be +shortened to `~/.c/n/lua/` (for more info see `:help pathshorten`). + +*Requires `cwd_prompt=true` + + + +files.cwd_prompt_shorten_val *fzf-lua-opts-files.cwd_prompt_shorten_val* + +Type: `number`, Default: `1` + +Length of shortened prompt path parts, e.g. set to `2`, `~/.config/nvim/lua/` +will be shortened to `~/.co/nv/lua/` (for more info see `:help pathshorten`). + +*Requires `cwd_prompt=true` + + + +CMD: LSP COMMANDS *fzf-lua-opts-cmd:-lsp-commands* + + + +lsp_references *fzf-lua-opts-lsp_references* + +LSP references + + + +async_or_timeout *fzf-lua-opts-async_or_timeout* + +Type: `number|boolean`, Default: `5000` + +Whether LSP calls are made block, set to `true` for asynchronous, otherwise +defines the timeout (ms) for the LPS request via `vim.lsp.buf_request_sync`. + + +vim:tw=78:ts=8:ft=help:norl: \ No newline at end of file diff --git a/lua/fzf-lua/cmd.lua b/lua/fzf-lua/cmd.lua index 5d18bc21..0cfa62e7 100644 --- a/lua/fzf-lua/cmd.lua +++ b/lua/fzf-lua/cmd.lua @@ -1,103 +1,123 @@ --- Modified from Telescope 'command.lua' local builtin = require "fzf-lua" local utils = require "fzf-lua.utils" -local command = {} - -local arg_value = { - ["nil"] = nil, - ['""'] = "", - ['"'] = "", -} - -local bool_type = { - ["false"] = false, - ["true"] = true, -} - --- convert command line string arguments to --- lua number boolean type and nil values -local function convert_user_opts(user_opts) - local _switch = { - ["boolean"] = function(key, val) - if val == "false" then - user_opts[key] = false - return - end - user_opts[key] = true - end, - ["number"] = function(key, val) - user_opts[key] = tonumber(val) - end, - ["string"] = function(key, val) - if arg_value[val] ~= nil then - user_opts[key] = arg_value[val] - return - end +local defaults = require "fzf-lua.defaults".defaults +local serpent = require "fzf-lua.lib.serpent" - if bool_type[val] ~= nil then - user_opts[key] = bool_type[val] - end - end, - } +local M = {} - local _switch_metatable = { - __index = function(_, k) - utils.info(string.format("Type of %s does not match", k)) - end, - } +function M.run_command(cmd, ...) + local args = { ... } + cmd = cmd or "builtin" - setmetatable(_switch, _switch_metatable) + if not builtin[cmd] then + utils.info(string.format("invalid command '%s'", cmd)) + return + end + + local opts = {} - for key, val in pairs(user_opts) do - _switch["string"](key, val) + for _, arg in ipairs(args) do + local key = arg:match("^[^=]+") + local val = arg:match("=") and arg:match("=(.*)$") + local ok, loaded = serpent.load(val or "true") + if ok and (type(loaded) ~= "table" or not vim.tbl_isempty(loaded)) then + opts[key] = loaded + else + opts[key] = val or true + end end + + builtin[cmd](opts) end --- receive the viml command args --- it should output a table value like --- { --- cmd = 'files', --- opts = { --- cwd = '***', --- } -local function run_command(args) - local user_opts = args or {} - if next(user_opts) == nil or not user_opts.cmd then - utils.info("missing command args") - return +function M._candidates(line, cmp_items) + local function to_cmp_items(t, data) + local cmp = require("cmp") + return vim.tbl_map(function(v) + return { + label = v, + filterText = v, + insertText = v, + kind = cmp.lsp.CompletionItemKind.Variable, + data = data, + } + end, t) end + local builtin_list = vim.tbl_filter(function(k) + return builtin._excluded_metamap[k] == nil + end, vim.tbl_keys(builtin)) - local cmd = user_opts.cmd - local opts = user_opts.opts or {} + local l = vim.split(line, "%s+") + local n = #l - 2 - if next(opts) ~= nil then - convert_user_opts(opts) - end + if n == 0 then + local commands = vim.tbl_flatten({ builtin_list }) + table.sort(commands) - if builtin[cmd] then - builtin[cmd](opts) - else - utils.info(string.format("invalid command '%s'", cmd)) + commands = vim.tbl_filter(function(val) + return vim.startswith(val, l[2]) + end, commands) + + return cmp_items and to_cmp_items(commands) or commands end -end -function command.load_command(cmd, ...) - local args = { ... } - if cmd == nil then - run_command { cmd = "builtin" } - return + -- Not all commands have their opts under the same key + local function cmd2key(cmd) + if not cmd then return end + local cmd2cfg = { + { + patterns = { "^git_", "^dap", "^tmux_" }, + transform = function(c) return c:gsub("_", ".") end + }, + { + patterns = { "^lsp_code_actions$" }, + transform = function(_) return "lsp.code_actions" end + }, + { patterns = { "^lsp_.*_symbols$" }, transform = function(_) return "lsp.symbols" end }, + { patterns = { "^lsp_" }, transform = function(_) return "lsp" end }, + { patterns = { "^diagnostics_" }, transform = function(_) return "dianostics" end }, + { patterns = { "^tags" }, transform = function(_) return "tags" end }, + { patterns = { "grep" }, transform = function(_) return "grep" end }, + { patterns = { "^complete_bline$" }, transform = function(_) return "complete_line" end }, + } + for _, v in pairs(cmd2cfg) do + for _, p in ipairs(v.patterns) do + if cmd:match(p) then return v.transform(cmd) end + end + end + return cmd end - local user_opts = {} - user_opts["cmd"] = cmd - user_opts.opts = {} + local cmd_cfg_key = cmd2key(l[2]) + local cmd_opts = utils.map_get(defaults, cmd_cfg_key) or {} + local opts = vim.tbl_filter(function(k) + return not k:match("^_") + end, vim.tbl_keys(utils.map_flatten(cmd_opts))) + + -- Add globals recursively, e.g. `winopts.fullscreen` + -- will be later retrieved using `utils.map_get(...)` + for k, v in pairs({ + winopts = false, + keymap = false, + fzf_opts = false, + fzf_tmux_opts = false, + __HLS = "hls", -- rename prefix + }) do + opts = vim.tbl_flatten({ opts, vim.tbl_keys(utils.map_flatten(defaults[k] or {}, v or k)) }) + end - for _, arg in ipairs(args) do - local param = vim.split(arg, "=") - user_opts.opts[param[1]] = param[2] + -- Add generic options that apply to all pickers + for _, o in ipairs({ "query" }) do + table.insert(opts, o) end - run_command(user_opts) + table.sort(opts) + + opts = vim.tbl_filter(function(val) + return vim.startswith(val, l[#l]) + end, opts) + + return cmp_items and to_cmp_items(opts, { cmd = cmd_cfg_key }) or opts end -return command +return M diff --git a/lua/fzf-lua/cmp_src.lua b/lua/fzf-lua/cmp_src.lua new file mode 100644 index 00000000..9b679cb9 --- /dev/null +++ b/lua/fzf-lua/cmp_src.lua @@ -0,0 +1,115 @@ +local Src = {} + +Src.new = function(_) + local self = setmetatable({}, { + __index = Src, + }) + return self +end + +---Return whether this source is available in the current context or not (optional). +---@return boolean +function Src:is_available() + local mode = vim.api.nvim_get_mode().mode:sub(1, 1) + return mode == "c" and vim.fn.getcmdtype() == ":" +end + +---Return the debug name of this source (optional). +---@return string +function Src:get_debug_name() + return "fzf-lua" +end + +---Invoke completion (required). +---@param params cmp.SourceCompletionApiParams +---@param callback fun(response: lsp.CompletionResponse|nil) +function Src:complete(params, callback) + if not params.context.cursor_before_line:match("FzfLua") then + return callback() + end + return callback(require("fzf-lua.cmd")._candidates(params.context.cursor_before_line, true)) +end + +---@param completion_item lsp.CompletionItem +---@return lsp.MarkupContent? +function Src:_get_documentation(completion_item) + -- Only attempt to load from file once, if failed we ditch the docs + if Src._options == nil then + local path = require "fzf-lua.path" + local utils = require "fzf-lua.utils" + local options_md = path.join({ vim.g.fzf_lua_root, "OPTIONS.md" }) + local lines = vim.split(utils.read_file(options_md), "\n") + if not vim.tbl_isempty(lines) then + Src._options = {} + local section + for _, l in ipairs(lines) do + if l:match("^#") or l:match(") +command! -nargs=* -complete=custom,s:fzflua_complete FzfLua + \ lua require("fzf-lua.cmd").run_command()