Skip to content

Commit

Permalink
refactor: better handling of default opt overrides (#967)
Browse files Browse the repository at this point in the history
backward compat: disable nvim-web-devicons below neovim
version 0.8 (requires 0.7 but still raises error W18)
  • Loading branch information
ibhagwan committed Dec 26, 2023
1 parent 6991c1f commit f2083c5
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 101 deletions.
164 changes: 87 additions & 77 deletions lua/fzf-lua/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ local path = require "fzf-lua.path"
local utils = require "fzf-lua.utils"
local actions = require "fzf-lua.actions"

-- Clear the default command or it would interfere with our options
-- not needed anymore, we are pretty much overriding all options
-- with our cli args, in addition this could conflict with fzf.vim
-- vim.env.FZF_DEFAULT_OPTS = ''

local M = {}

M._has_devicons, M._devicons = pcall(require, "nvim-web-devicons")
if utils.__HAS_DEVICONS then
M._has_devicons, M._devicons = pcall(require, "nvim-web-devicons")

-- get the devicons module path
M._devicons_path = M._has_devicons and M._devicons and M._devicons.setup
and debug.getinfo(M._devicons.setup, "S").source:gsub("^@", "")
-- get the devicons module path
M._devicons_path = M._has_devicons and M._devicons and M._devicons.setup
and debug.getinfo(M._devicons.setup, "S").source:gsub("^@", "")
end

-- get icons proxy for the headless instance
M._devicons_geticons = function()
Expand Down Expand Up @@ -84,18 +81,6 @@ M.get_pid = function()
return M.__pid
end

-- Reset globals to default
function M.reset_defaults()
local m = require("fzf-lua.defaults")
M.DEFAULTS = m
M.defaults = m.defaults
M.globals = utils.deepcopy(M.defaults)
m.globals = M.globals
end

-- Call once so we aren't dependent on calling setup()
M.reset_defaults()

M.resume_get = function(what, opts)
assert(opts.__resume_key)
if type(opts.__resume_get) == "function" then
Expand Down Expand Up @@ -138,7 +123,69 @@ function M.resume_opts(opts)
return opts
end

function M.normalize_opts(opts, defaults, __resume_key)
-- proxy table (with logic) for accessing the global config
M.setup_opts = {}
M.globals = setmetatable({}, {
__index = function(_, index)
-- bind tables are logical exception, if specified, do not merge with defaults
-- normalize all binds as lowercase or we can have duplicate keys (#654)
if index == "actions" then
return {
files = utils.map_tolower(
utils.map_get(M.setup_opts, "actions.files") or M.defaults.actions.files),
buffers = utils.map_tolower(
utils.map_get(M.setup_opts, "actions.buffers") or M.defaults.actions.buffers),
}
elseif index == "keymap" then
return {
fzf = utils.map_tolower(
utils.map_get(M.setup_opts, "keymap.fzf") or M.defaults.keymap.fzf),
builtin = utils.map_tolower(
utils.map_get(M.setup_opts, "keymap.builtin") or M.defaults.keymap.builtin),
}
end
-- build normalized globals, option priority below:
-- (1) provider specific globals (post-setup)
-- (2) generic global-defaults (post-setup), i.e. `setup({ defaults = { ... } })`
-- (3) fzf-lua's true defaults (pre-setup, static)
local fzflua_default = utils.map_get(M.defaults, index)
local setup_default = utils.map_get(M.setup_opts.defaults, index)
local setup_value = utils.map_get(M.setup_opts, index)
-- values that aren't tables can't get merged, return highest priority
if setup_value ~= nil and type(setup_value) ~= "table" then
return setup_value
elseif setup_default ~= nil and type(setup_default) ~= "table" then
return setup_default
elseif fzflua_default ~= nil and type(fzflua_default) ~= "table" then
return fzflua_default
elseif fzflua_default == nil and setup_value == nil then
return
end
-- (1) use fzf-lua's true defaults (pre-setup) as our options base
local ret = utils.tbl_deep_clone(fzflua_default) or {}
if (fzflua_default and fzflua_default.prompt) or (setup_value and setup_value.prompt) then
-- (2) the existence of the `prompt` key implies we're dealing with a provider
-- override global provider defaults supplied by the user's setup `defaults` table
ret = vim.tbl_deep_extend("force", ret, M.setup_opts.defaults or {})
end
-- (3) override with the specific provider options from the users's `setup` option
ret = vim.tbl_deep_extend("force", ret, utils.map_get(M.setup_opts, index) or {})
return ret
end,
__newindex = function(_, index, _)
assert(false, string.format("modifying globals directly isn't allowed [index: %s]", index))
end
})

do
-- store circular refs for globals/defaults
-- used by M.globals.__index and M.defaults.<provider>._actions
local m = require("fzf-lua.defaults")
M.defaults = m.defaults
m.globals = M.globals
end

function M.normalize_opts(opts, globals, __resume_key)
if not opts then opts = {} end

-- opts can also be a function that returns an opts table
Expand All @@ -162,22 +209,26 @@ function M.normalize_opts(opts, defaults, __resume_key)
-- __FNCREF2__ will use the 2nd function ref in the stack (calling fn)
opts.__resume_key = __resume_key
or opts.__resume_key
or (type(defaults) == "string" and defaults)
or (type(defaults) == "table" and defaults.__resume_key)
or (type(globals) == "string" and globals)
or (type(globals) == "table" and globals.__resume_key)
or utils.__FNCREF2__()

-- if defaults is string retrieve from M.globals
if type(defaults) == "string" then
defaults = utils.map_get(M.globals, defaults)
assert(type(defaults) == "table")
if type(globals) == "string" then
-- globals is a string, generate provider globals
globals = M.globals[globals]
assert(type(globals) == "table")
else
-- backward compat: globals sent directly as table
-- merge with setup options "defaults" table
globals = vim.tbl_deep_extend("keep", globals, M.setup_opts.defaults or {})
end

-- merge current opts with revious __call_opts on resume
if opts.resume then
opts = M.resume_opts(opts)
end

-- ignore case for keybinds or conflicts may occur (#654)
-- normalize all binds as lowercase or we can have duplicate keys (#654)
local keymap_tolower = function(m)
return m and {
fzf = utils.map_tolower(m.fzf),
Expand All @@ -186,34 +237,16 @@ function M.normalize_opts(opts, defaults, __resume_key)
end
opts.keymap = keymap_tolower(opts.keymap)
opts.actions = utils.map_tolower(opts.actions)
defaults.keymap = keymap_tolower(defaults.keymap)
defaults.actions = utils.map_tolower(defaults.actions)
if M.globals.actions then
M.globals.actions.files = utils.map_tolower(M.globals.actions.files)
M.globals.actions.buffers = utils.map_tolower(M.globals.actions.buffers)
end
M.globals.keymap = keymap_tolower(M.globals.keymap)
globals.keymap = keymap_tolower(globals.keymap)
globals.actions = utils.map_tolower(globals.actions)

-- inherit from globals.actions?
if type(defaults._actions) == "function" then
defaults.actions = vim.tbl_deep_extend("keep",
defaults.actions or {},
defaults._actions())
end

-- First merge with the users' "provider-global" defaults
if type(M.globals.defaults) == "table" then
opts = vim.tbl_deep_extend("keep", opts, utils.tbl_deep_clone(M.globals.defaults))
elseif type(M.globals.defaults) == "function" then
opts = vim.tbl_deep_extend("keep", opts, utils.tbl_deep_clone(M.globals.defaults()))
if type(globals._actions) == "function" then
globals.actions = vim.tbl_deep_extend("keep", globals.actions or {}, globals._actions())
end

-- Then merge with provider defaults "keeping" provider-globals
-- we must clone the 'defaults' tbl, otherwise 'opts.actions.default'
-- overrides 'config.globals.lsp.actions.default' in neovim 6.0
-- which then prevents the default action of all other LSP providers
-- https://github.com/ibhagwan/fzf-lua/issues/197
opts = vim.tbl_deep_extend("keep", opts, utils.tbl_deep_clone(defaults))
-- merge with provider defaults from globals (defaults + setup options)
opts = vim.tbl_deep_extend("keep", opts, utils.tbl_deep_clone(globals))

-- Merge required tables from globals
for _, k in ipairs({
Expand Down Expand Up @@ -246,7 +279,7 @@ function M.normalize_opts(opts, defaults, __resume_key)
-- Merge arrays from globals|defaults, can't use 'vim.tbl_xxx'
-- for these as they only work for maps, ie. '{ key = value }'
for _, k in ipairs({ "file_ignore_patterns" }) do
for _, m in ipairs({ defaults, M.globals }) do
for _, m in ipairs({ globals, M.globals }) do
if m[k] then
for _, item in ipairs(m[k]) do
if not opts[k] then opts[k] = {} end
Expand Down Expand Up @@ -285,29 +318,6 @@ function M.normalize_opts(opts, defaults, __resume_key)
end
end

local function get_opt(o, t1, t2)
if t1[o] ~= nil then
return t1[o]
else
return t2[o]
end
end

-- DEPRECATED: use `defaults` (provider-defaults) table
-- global option overrides. If exists, these options will
-- be used in a "LOGICAL AND" against the local option (#188)
-- e.g.:
-- git_icons = TRUE
-- global_git_icons = FALSE
-- the resulting 'git_icons' would be:
-- git_icons = TRUE && FALSE (==FALSE)
for _, o in ipairs({ "file_icons", "git_icons", "color_icons" }) do
local g_opt = get_opt("global_" .. o, opts, M.globals)
if g_opt ~= nil then
opts[o] = opts[o] and g_opt
end
end

-- backward compatibility, rhs overrides lhs
-- (rhs being the "old" option)
local backward_compat = {
Expand Down
2 changes: 1 addition & 1 deletion lua/fzf-lua/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local previewers = require "fzf-lua.previewer"

local M = {}

M._has_devicons = pcall(require, "nvim-web-devicons")
M._has_devicons = utils.__HAS_DEVICONS

function M._default_previewer_fn()
local previewer = M.globals.default_previewer or M.globals.winopts.preview.default
Expand Down
39 changes: 16 additions & 23 deletions lua/fzf-lua/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function M.setup_highlights(override)
-- reset any invalid hl, this will cause our 'winhighlight'
-- string to look something akin to `Normal:,FloatBorder:`
-- which uses terminal fg|bg colors instead
utils.map_set(config.globals, "__HLS." .. opt_name, "")
utils.map_set(config.setup_opts, "__HLS." .. opt_name, "")
end
end
end
Expand Down Expand Up @@ -125,37 +125,30 @@ function M.setup(opts, do_not_reset_defaults)
opts = vim.tbl_deep_extend("keep", opts, profile_opts)
end
end
-- Reset to defaults and merge with user options
if not do_not_reset_defaults then
config.reset_defaults()
if do_not_reset_defaults then
-- no defaults reset requested, merge with previous setup options
opts = vim.tbl_deep_extend("keep", opts, config.setup_opts or {})
end
-- Make sure opts is a table or override
local globals = vim.tbl_deep_extend("keep", opts, config.globals)
-- do not merge, override the bind tables
for t, v in pairs({
["keymap"] = { "fzf", "builtin" },
["actions"] = { "files", "buffers" },
}) do
for _, k in ipairs(v) do
if opts[t] and opts[t][k] then
globals[t][k] = opts[t][k]
end
-- backward compat `global_{git|gile|color}_icons`
-- converts `global_file_icons` to `defaults.file_icons`, etc
for _, o in ipairs({ "file_icons", "git_icons", "color_icons" }) do
local gopt = "global_" .. o
if opts[gopt] ~= nil then
opts.defaults = opts.defaults or {}
opts.defaults[o] = opts[gopt]
opts[gopt] = nil
end
end
-- set lua_io if caller requested
utils.set_lua_io(globals.lua_io)
utils.set_lua_io(opts.lua_io)
-- set custom &nbsp if caller requested
if globals.nbsp then utils.nbsp = globals.nbsp end
-- reset our globals based on user opts
-- this doesn't happen automatically
config.globals = globals
config.DEFAULTS.globals = globals
if opts.nbsp then utils.nbsp = opts.nbsp end
-- store the setup options
config.setup_opts = opts
-- setup highlights
M.setup_highlights()
end

M.defaults = config.globals

M.redraw = function()
local winobj = require "fzf-lua".win.__SELF()
if winobj then
Expand Down
7 changes: 7 additions & 0 deletions lua/fzf-lua/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ M.__HAS_NVIM_08 = vim.fn.has("nvim-0.8") == 1
M.__HAS_NVIM_09 = vim.fn.has("nvim-0.9") == 1
M.__HAS_NVIM_010 = vim.fn.has("nvim-0.10") == 1


-- limit devicons support to nvim >=0.8, although official support is >=0.7
-- running setup on 0.7 errs with "W18: Invalid character in group name"
if M.__HAS_NVIM_08 then
M.__HAS_DEVICONS = pcall(require, "nvim-web-devicons")
end

function M.__FILE__() return debug.getinfo(2, "S").source end

function M.__LINE__() return debug.getinfo(2, "l").currentline end
Expand Down

0 comments on commit f2083c5

Please sign in to comment.