new preview scrollbar option and highlights, win|preview options rework

main
bhagwan 3 years ago
parent e9c70a8d9f
commit 544d5eabc8

@ -217,16 +217,48 @@ require'fzf-lua'.setup {
-- "aboveleft vnew : split left
-- Only valid when using a float window
-- (i.e. when 'split' is not defined)
win_height = 0.85, -- window height
win_width = 0.80, -- window width
win_row = 0.30, -- window row position (0=top, 1=bottom)
win_col = 0.50, -- window col position (0=left, 1=right)
-- win_border = false, -- window border? or borderchars?
win_border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' },
hl_normal = 'Normal', -- window normal color
hl_border = 'Normal', -- change to 'FloatBorder' if exists
height = 0.85, -- window height
width = 0.80, -- window width
row = 0.35, -- window row position (0=top, 1=bottom)
col = 0.50, -- window col position (0=left, 1=right)
-- border argument passthrough to nvim_open_win(), also used
-- to manually draw the border characters around the preview
-- window, can be set to 'false' to remove all borders or to
-- 'none', 'single', 'double' or 'rounded' (default)
border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' },
fullscreen = false, -- start fullscreen?
window_on_create = function()
hl = {
normal = 'Normal', -- window normal color (fg+bg)
border = 'Normal', -- border color (try 'FloatBorder')
-- Only valid with the builtin previewer:
cursor = 'Cursor', -- cursor highlight (grep/LSP matches)
cursorline = 'CursorLine', -- cursor line
-- title = 'Normal', -- preview border title (file/buffer)
-- scrollbar_f = 'PmenuThumb', -- scrollbar "full" section highlight
-- scrollbar_e = 'PmenuSbar', -- scrollbar "empty" section highlight
},
preview = {
-- default = 'bat', -- override the default previewer?
-- default uses the 'builtin' previewer
border = 'border', -- border|noborder, applies only to
-- native fzf previewers (bat/cat/git/etc)
wrap = 'nowrap', -- wrap|nowrap
hidden = 'nohidden', -- hidden|nohidden
vertical = 'down:45%', -- up|down:size
horizontal = 'right:60%', -- right|left:size
layout = 'flex', -- horizontal|vertical|flex
flip_columns = 120, -- #cols to switch to horizontal on flex
-- Only valid with the builtin previewer:
title = true, -- preview border title (file/buf)?
scrollbar = 'float', -- `false` or string:'float|border'
-- float: in-window floating border
-- border: in-border chars (see below)
scrolloff = '-2', -- float scrollbar offset from right
-- applies only when scrollbar = 'float'
scrollchars = {'█', '' }, -- scrollbar chars ({ <full>, <empty> }
-- applies only when scrollbar = 'border'
},
on_create = function()
-- called once upon creation of the fzf main window
-- can be used to add custom fzf-lua mappings, e.g:
-- vim.api.nvim_buf_set_keymap(0, "t", "<C-j>", "<Down>",
@ -296,15 +328,6 @@ require'fzf-lua'.setup {
["header"] = { "fg", "Comment" },
["gutter"] = { "bg", "Normal" },
}, ]]
preview_border = 'border', -- border|noborder
preview_wrap = 'nowrap', -- wrap|nowrap
preview_opts = 'nohidden', -- hidden|nohidden
preview_vertical = 'down:45%', -- up|down:size
preview_horizontal = 'right:60%', -- right|left:size
preview_layout = 'flex', -- horizontal|vertical|flex
flip_columns = 120, -- #cols to switch to horizontal on flex
-- default_previewer = "bat", -- override the default previewer?
-- by default uses the builtin previewer
previewers = {
cat = {
cmd = "cat",
@ -327,14 +350,9 @@ require'fzf-lua'.setup {
builtin = {
delay = 100, -- delay(ms) displaying the preview
-- prevents lag on fast scrolling
title = true, -- preview title?
scrollbar = true, -- scrollbar?
scrollchar = '█', -- scrollbar character
syntax = true, -- preview syntax highlight?
syntax_limit_l = 0, -- syntax limit (lines), 0=nolimit
syntax_limit_b = 1024*1024, -- syntax limit (bytes), 0=nolimit
hl_cursor = 'Cursor', -- cursor highlight
hl_cursorline = 'CursorLine', -- cursor line highlight
},
},
-- provider setup
@ -469,8 +487,8 @@ require'fzf-lua'.setup {
["ctrl-y"] = function(selected) print(selected[1]) end,
},
winopts = {
win_height = 0.55,
win_width = 0.30,
height = 0.55,
width = 0.30,
},
post_reset_cb = function()
-- reset statusline highlights after

@ -251,16 +251,48 @@ Consult the list below for available settings:
-- "aboveleft vnew : split left
-- Only valid when using a float window
-- (i.e. when 'split' is not defined)
win_height = 0.85, -- window height
win_width = 0.80, -- window width
win_row = 0.30, -- window row position (0=top, 1=bottom)
win_col = 0.50, -- window col position (0=left, 1=right)
-- win_border = false, -- window border? or borderchars?
win_border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' },
hl_normal = 'Normal', -- window normal color
hl_border = 'Normal', -- change to 'FloatBorder' if exists
height = 0.85, -- window height
width = 0.80, -- window width
row = 0.35, -- window row position (0=top, 1=bottom)
col = 0.50, -- window col position (0=left, 1=right)
-- border argument passthrough to nvim_open_win(), also used
-- to manually draw the border characters around the preview
-- window, can be set to 'false' to remove all borders or to
-- 'none', 'single', 'double' or 'rounded' (default)
border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' },
fullscreen = false, -- start fullscreen?
window_on_create = function()
hl = {
normal = 'Normal', -- window normal color (fg+bg)
border = 'Normal', -- border color (try 'FloatBorder')
-- Only valid with the builtin previewer:
cursor = 'Cursor', -- cursor highlight (grep/LSP matches)
cursorline = 'CursorLine', -- cursor line
-- title = 'Normal', -- preview border title (file/buffer)
-- scrollbar_f = 'PmenuThumb', -- scrollbar "full" section highlight
-- scrollbar_e = 'PmenuSbar', -- scrollbar "empty" section highlight
},
preview = {
-- default = 'bat', -- override the default previewer?
-- default uses the 'builtin' previewer
border = 'border', -- border|noborder, applies only to
-- native fzf previewers (bat/cat/git/etc)
wrap = 'nowrap', -- wrap|nowrap
hidden = 'nohidden', -- hidden|nohidden
vertical = 'down:45%', -- up|down:size
horizontal = 'right:60%', -- right|left:size
layout = 'flex', -- horizontal|vertical|flex
flip_columns = 120, -- #cols to switch to horizontal on flex
-- Only valid with the builtin previewer:
title = true, -- preview border title (file/buf)?
scrollbar = 'float', -- `false` or string:'float|border'
-- float: in-window floating border
-- border: in-border chars (see below)
scrolloff = '-2', -- float scrollbar offset from right
-- applies only when scrollbar = 'float'
scrollchars = {'█', '' }, -- scrollbar chars ({ <full>, <empty> }
-- applies only when scrollbar = 'border'
},
on_create = function()
-- called once upon creation of the fzf main window
-- can be used to add custom fzf-lua mappings, e.g:
-- vim.api.nvim_buf_set_keymap(0, "t", "<C-j>", "<Down>",
@ -330,15 +362,6 @@ Consult the list below for available settings:
["header"] = { "fg", "Comment" },
["gutter"] = { "bg", "Normal" },
}, ]]
preview_border = 'border', -- border|noborder
preview_wrap = 'nowrap', -- wrap|nowrap
preview_opts = 'nohidden', -- hidden|nohidden
preview_vertical = 'down:45%', -- up|down:size
preview_horizontal = 'right:60%', -- right|left:size
preview_layout = 'flex', -- horizontal|vertical|flex
flip_columns = 120, -- #cols to switch to horizontal on flex
-- default_previewer = "bat", -- override the default previewer?
-- by default uses the builtin previewer
previewers = {
cat = {
cmd = "cat",
@ -361,14 +384,9 @@ Consult the list below for available settings:
builtin = {
delay = 100, -- delay(ms) displaying the preview
-- prevents lag on fast scrolling
title = true, -- preview title?
scrollbar = true, -- scrollbar?
scrollchar = '█', -- scrollbar character
syntax = true, -- preview syntax highlight?
syntax_limit_l = 0, -- syntax limit (lines), 0=nolimit
syntax_limit_b = 1024*1024, -- syntax limit (bytes), 0=nolimit
hl_cursor = 'Cursor', -- cursor highlight
hl_cursorline = 'CursorLine', -- cursor line highlight
},
},
-- provider setup
@ -503,8 +521,8 @@ Consult the list below for available settings:
["ctrl-y"] = function(selected) print(selected[1]) end,
},
winopts = {
win_height = 0.55,
win_width = 0.30,
height = 0.55,
width = 0.30,
},
post_reset_cb = function()
-- reset statusline highlights after

@ -19,18 +19,50 @@ if M._has_devicons and not M._devicons.has_loaded() then
M._devicons.setup()
end
function M._default_previewer_fn()
return M.globals.default_previewer or M.globals.winopts.preview.default
end
M.globals = {
winopts = {
win_height = 0.85,
win_width = 0.80,
win_row = 0.30,
win_col = 0.50,
win_border = true,
borderchars = { '', '', '', '', '', '', '', '' },
hl_normal = 'Normal',
hl_border = 'Normal',
height = 0.85,
width = 0.80,
row = 0.35,
col = 0.55,
border = 'rounded',
fullscreen = false,
window_on_create = function()
hl = {
normal = 'Normal',
border = 'Normal',
-- builtin preview only
cursor = 'Cursor',
cursorline = 'CursorLine',
-- title = 'Normal',
-- scrollbar_f = 'PmenuThumb',
-- scrollbar_e = 'PmenuSbar',
},
preview = {
default = "builtin",
border = 'float',
wrap = 'nowrap',
hidden = 'nohidden',
vertical = 'down:45%',
horizontal = 'right:60%',
layout = 'flex',
flip_columns = 120,
title = true,
scrollbar = 'border',
scrolloff = '-2',
scrollchar = '',
scrollchars = {'', '' },
},
_borderchars = {
["none"] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
["single"] = {'', '', '', '', '', '', '', '' },
["double"] = {'', '', '', '', '', '', '', '' },
["rounded"] = {'', '', '', '', '', '', '', '' },
},
on_create = function()
-- vim.cmd("set winhl=Normal:Normal,FloatBorder:Normal")
end,
},
@ -69,14 +101,6 @@ M.globals = {
['--height'] = '100%',
['--layout'] = 'reverse',
},
preview_border = 'border',
preview_wrap = 'nowrap',
preview_opts = 'nohidden',
preview_vertical = 'down:45%',
preview_horizontal = 'right:60%',
preview_layout = 'flex',
flip_columns = 120,
default_previewer = "builtin",
previewers = {
cat = {
cmd = "cat",
@ -110,20 +134,16 @@ M.globals = {
-- https://github.com/junegunn/fzf/issues/2417#issuecomment-809886535
delay = 100,
title = true,
scrollbar = true,
scrollchar = '',
syntax = true,
syntax_delay = 0,
syntax_limit_l = 0,
syntax_limit_b = 1024*1024,
hl_cursor = 'Cursor',
hl_cursorline = 'CursorLine',
_ctor = previewers.builtin.buffer_or_file,
},
},
}
M.globals.files = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = '> ',
cmd = nil, -- default: auto detect find|fd
file_icons = true and M._has_devicons,
@ -149,7 +169,7 @@ M.globals.files = {
-- so we can reference 'M.globals.files'
M.globals.git = {
files = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'GitFiles> ',
cmd = "git ls-files --exclude-standard",
file_icons = true and M._has_devicons,
@ -203,7 +223,7 @@ M.globals.git = {
},
}
M.globals.grep = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'Rg> ',
input_prompt = 'Grep For> ',
cmd = nil, -- default: auto detect rg|grep
@ -218,7 +238,7 @@ M.globals.grep = {
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
}
M.globals.args = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'Args> ',
files_only = true,
file_icons = true and M._has_devicons,
@ -228,7 +248,7 @@ M.globals.args = {
}
M.globals.args.actions["ctrl-x"] = actions.arg_del
M.globals.oldfiles = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'History> ',
file_icons = true and M._has_devicons,
color_icons = true,
@ -236,7 +256,7 @@ M.globals.oldfiles = {
actions = M.globals.files.actions,
}
M.globals.quickfix = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'Quickfix> ',
separator = '',
file_icons = true and M._has_devicons,
@ -245,7 +265,7 @@ M.globals.quickfix = {
actions = M.globals.files.actions,
}
M.globals.loclist = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'Locations> ',
separator = '',
file_icons = true and M._has_devicons,
@ -310,7 +330,7 @@ M.globals.blines = {
},
}
M.globals.tags = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'Tags> ',
ctags_file = "tags",
file_icons = true and M._has_devicons,
@ -319,7 +339,7 @@ M.globals.tags = {
actions = M.globals.files.actions,
}
M.globals.btags = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = 'BTags> ',
ctags_file = "tags",
file_icons = true and M._has_devicons,
@ -334,8 +354,8 @@ M.globals.colorschemes = {
["default"] = actions.colorscheme,
},
winopts = {
win_height = 0.55,
win_width = 0.50,
height = 0.55,
width = 0.50,
},
}
M.globals.helptags = {
@ -364,7 +384,7 @@ M.globals.manpages = {
},
}
M.globals.lsp = {
previewer = function() return M.globals.default_previewer end,
previewer = M._default_previewer_fn,
prompt = '> ',
file_icons = true and M._has_devicons,
color_icons = true,
@ -384,8 +404,8 @@ M.globals.lsp = {
M.globals.builtin = {
prompt = 'Builtin> ',
winopts = {
win_height = 0.65,
win_width = 0.50,
height = 0.65,
width = 0.50,
},
actions = {
["default"] = actions.run_builtin,
@ -516,18 +536,67 @@ else
}
end
function M.normalize_opts(opts, defaults)
if not opts then opts = {} end
if not opts.fzf_opts then opts.fzf_opts = {} end
-- First, merge with provider defaults
opts = vim.tbl_deep_extend("keep", opts, defaults)
opts.keymap = vim.tbl_deep_extend("keep", opts.keymap or {}, M.globals.keymap)
if defaults.winopts then
if not opts.winopts then opts.winopts = {} end
opts.winopts = vim.tbl_deep_extend("keep", opts.winopts, defaults.winopts)
-- Merge required tables from globals
for _, k in ipairs({ 'winopts', 'keymap', 'fzf_opts', 'previewers' }) do
opts[k] = vim.tbl_deep_extend("keep", opts[k] or {}, M.globals[k] or {})
end
-- backward compatibility, rhs overrides lhs
-- (rhs being the "old" option)
local backward_compat = {
['winopts.row'] = 'winopts.win_row',
['winopts.col'] = 'winopts.win_col',
['winopts.width'] = 'winopts.win_width',
['winopts.height'] = 'winopts.win_height',
['winopts.border'] = 'winopts.win_border',
['winopts.on_create'] = 'winopts.window_on_create',
['winopts.preview.wrap'] = 'preview_wrap',
['winopts.preview.border'] = 'preview_border',
['winopts.preview.hidden'] = 'preview_opts',
['winopts.preview.vertical'] = 'preview_vertical',
['winopts.preview.horizontal'] = 'preview_horizontal',
['winopts.preview.layout'] = 'preview_layout',
['winopts.preview.flip_columns'] = 'flip_columns',
['winopts.preview.default'] = 'default_previewer',
['winopts.hl.normal'] = 'winopts.hl_normal',
['winopts.hl.border'] = 'winopts.hl_border',
['winopts.hl.cursor'] = 'previewers.builtin.hl_cursor',
['winopts.hl.cursorline'] = 'previewers.builtin.hl_cursorline',
['winopts.preview.title'] = 'previewers.builtin.title',
['winopts.preview.scrollbar'] = 'previewers.builtin.scrollbar',
['winopts.preview.scrollchar'] = 'previewers.builtin.scrollchar',
}
-- recursive key loopkup, can also set new value
local map_recurse = function(m, s, v, w)
local keys = utils.strsplit(s, '.')
local val, map = m, nil
for i=1,#keys do
map = val
val = val[keys[i]]
if not val then break end
if v~=nil and i==#keys then map[keys[i]] = v end
end
if v and w then utils.warn(w) end
return val
end
-- interate backward compat map, retrieve values from opts or globals
for k, v in pairs(backward_compat) do
map_recurse(opts, k, map_recurse(opts, v) or map_recurse(M.globals, v))
-- ,("'%s' is now defined under '%s'"):format(v, k))
end
if type(opts.previewer) == 'function' then
-- we use a function so the user can override
-- globals.default_previewer
-- globals.winopts.preview.default
opts.previewer = opts.previewer()
end
if type(opts.previewer) == 'table' then
@ -566,6 +635,9 @@ function M.normalize_opts(opts, defaults)
-- are we using skim?
opts._is_skim = opts.fzf_bin:find('sk') ~= nil
-- mark as normalized
opts._normalized = true
return opts
end

@ -8,6 +8,10 @@ local win = require "fzf-lua.win"
local M = {}
M.fzf = function(opts, contents)
-- normalize with globals if not already normalized
if not opts._normalized then
opts = config.normalize_opts(opts, {})
end
-- setup the fzf window and preview layout
local fzf_win = win(opts)
if not fzf_win then return end
@ -66,19 +70,17 @@ M.get_devicon = function(file, ext)
return icon..config.globals.file_icon_padding:gsub(" ", utils.nbsp), hl
end
M.preview_window = function(opts)
local o = vim.tbl_deep_extend("keep", opts, config.globals)
local preview_vertical = string.format('%s:%s:%s:%s',
o.preview_opts, o.preview_border, o.preview_wrap, o.preview_vertical)
local preview_horizontal = string.format('%s:%s:%s:%s',
o.preview_opts, o.preview_border, o.preview_wrap, o.preview_horizontal)
if o.preview_layout == "vertical" then
return preview_vertical
elseif o.preview_layout == "flex" then
return utils._if(vim.o.columns>o.flip_columns, preview_horizontal, preview_vertical)
M.preview_window = function(o)
local preview_args = ("%s:%s:%s:"):format(
o.winopts.preview.hidden, o.winopts.preview.border, o.winopts.preview.wrap)
if o.winopts.preview.layout == "horizontal" or
o.winopts.preview.layout == "flex" and
vim.o.columns>o.winopts.preview.flip_columns then
preview_args = preview_args .. o.winopts.preview.horizontal
else
return preview_horizontal
preview_args = preview_args .. o.winopts.preview.vertical
end
return preview_args
end
M.get_color = function(hl_group, what)

@ -31,10 +31,10 @@ function M.setup(opts)
utils.warn("'previewers.builtin.keymap' moved under 'keymap.builtin', see ':help fzf-lua-customization'")
end
if globals.previewers.builtin.wrap ~= nil then
utils.warn("'previewers.builtin.wrap' is not longer in use, set 'preview_wrap' to 'wrap' or 'nowrap' instead")
utils.warn("'previewers.builtin.wrap' is not longer in use, set 'winopts.preview.wrap' to 'wrap' or 'nowrap' instead")
end
if globals.previewers.builtin.hidden ~= nil then
utils.warn("'previewers.builtin.hidden' is not longer in use, set 'preview_opts' to 'hidden' or 'nohidden' instead")
utils.warn("'previewers.builtin.hidden' is not longer in use, set 'winopts.preview.hidden' to 'hidden' or 'nohidden' instead")
end
-- override BAT_CONFIG_PATH to prevent a
-- conflct with '$XDG_DATA_HOME/bat/config'

@ -31,18 +31,11 @@ function Previewer.base:new(o, opts, fzf_win)
self.type = "builtin"
self.win = fzf_win
self.delay = o.delay
self.title = o.title
self.scrollbar = o.scrollbar
if o.scrollchar and type(o.scrollchar) == 'string' then
self.win.winopts.scrollchar = o.scrollchar
end
self.title = self.win.winopts.preview.title
self.syntax = o.syntax
self.syntax_delay = o.syntax_delay
self.syntax_limit_b = o.syntax_limit_b
self.syntax_limit_l = o.syntax_limit_l
self.hl_cursor = o.hl_cursor
self.hl_cursorline = o.hl_cursorline
self.hl_range = o.hl_range
self.backups = {}
return self
end
@ -234,9 +227,7 @@ function Previewer.base:scroll(direction)
end)
end
end
if self.scrollbar then
self.win:update_scrollbar()
end
self.win:update_scrollbar()
end
@ -373,8 +364,8 @@ local function set_cursor_hl(self, entry)
fn.clearmatches()
if lnum and lnum > 0 and col and col > 1 then
fn.matchaddpos(self.hl_cursor, {{lnum, math.max(1, col)}}, 11)
if self.win.winopts.hl.cursor and lnum and lnum > 0 and col and col > 1 then
fn.matchaddpos(self.win.winopts.hl.cursor, {{lnum, math.max(1, col)}}, 11)
end
end
@ -391,9 +382,7 @@ function Previewer.buffer_or_file:update_border(entry)
end
self.win:update_title(title)
end
if self.scrollbar then
self.win:update_scrollbar()
end
self.win:update_scrollbar()
end
function Previewer.buffer_or_file:preview_buf_post(entry)
@ -477,9 +466,7 @@ function Previewer.help_tags:populate_preview_buf(entry_str)
end)
api.nvim_win_set_buf(self.win.preview_winid, self.preview_bufnr)
api.nvim_win_set_cursor(self.win.preview_winid, self.orig_pos)
if self.scrollbar then
self.win:update_scrollbar()
end
self.win:update_scrollbar()
if self.prev_help_bufnr ~= self.preview_bufnr and
-- only delete the help buffer when the help
-- tag triggers opening a different help file

@ -395,7 +395,7 @@ M.diagnostics = function(opts)
if not opts then return end
local lsp_clients = vim.lsp.buf_get_clients(0)
if utils.tbl_length(lsp_clients) == 0 then
if utils.tbl_isempty(lsp_clients) then
utils.info("LSP: no client attached")
return
end
@ -568,7 +568,7 @@ local function check_capabilities(feature)
if supported_client then
return true
else
if utils.tbl_length(clients) == 0 then
if utils.tbl_isempty(clients) then
utils.info("LSP: no client attached")
else
utils.info("LSP: server does not support " .. feature)

@ -164,13 +164,9 @@ function M.tbl_length(T)
return count
end
function M.tbl_has(table, key)
return table[key] ~= nil
end
function M.tbl_or(key, tbl1, tbl2)
if tbl1[key] ~= nil then return tbl1[key]
else return tbl2[key] end
function M.tbl_isempty(T)
if not T or not next(T) then return true end
return false
end
function M.tbl_concat(...)
@ -414,4 +410,17 @@ function M.io_system(cmd, use_lua_io)
end
end
function M.fzf_bind_to_neovim(key)
local conv_map = {
['alt'] = 'A',
['ctrl'] = 'C',
['shift'] = 'S',
}
key = key:lower()
for k, v in pairs(conv_map) do
key = key:gsub(k, v)
end
return ("<%s>"):format(key)
end
return M

@ -18,6 +18,14 @@ setmetatable(FzfWin, {
function FzfWin:setup_keybinds()
if not self:validate() then return end
if not self.keymap or not self.keymap.builtin then return end
-- find the toggle_preview
if self.keymap.fzf then
for k, v in pairs(self.keymap.fzf) do
if v == 'toggle-preview' then
self._fzf_toggle_prev_bind = utils.fzf_bind_to_neovim(k)
end
end
end
local keymap_tbl = {
['toggle-fullscreen'] = { module = 'win', fnc = 'toggle_fullscreen()' },
}
@ -65,9 +73,12 @@ local generate_layout = function(winopts)
if winopts.split then
anchor = 'NW'
prev_row = 1
prev_width = prev_width - 2
prev_height = prev_height - 1
if vert_split then
prev_col = 1
else
prev_col = prev_col - 1
end
else
anchor = 'SW'
@ -79,6 +90,7 @@ local generate_layout = function(winopts)
prev_col = 1
prev_row = height + padding
prev_height = prev_height - 1
prev_width = prev_width - 2
else
prev_row = row + height + 3
end
@ -86,34 +98,29 @@ local generate_layout = function(winopts)
elseif preview_pos == 'left' or preview_pos == 'right' then
prev_height = height
prev_width = utils.round(width * preview_size/100)
width = width - prev_width
width = width - prev_width - 2
if preview_pos == 'left' then
anchor = 'NE'
col = col + prev_width
prev_col = col - padding
col = col + prev_width + 2
prev_col = col - 1
if winopts.split then
prev_row = 1
prev_width = prev_width - 1
prev_height = prev_height - padding
if vert_split then
anchor = 'NW'
prev_col = 1
prev_width = prev_width + 1
else
prev_width = prev_width - 1
prev_col = col - 3
end
end
else
anchor = 'NW'
if winopts.split then
prev_row = 1
prev_col = width + 4
prev_width = prev_width - 3
prev_height = prev_height - padding
if vert_split then
prev_col = width + 2
prev_width = prev_width - 1
else
prev_col = width + 3
prev_width = prev_width - padding
end
else
prev_col = col + width + 3
end
@ -132,72 +139,113 @@ local generate_layout = function(winopts)
}
end
local normalize_winopts = function(opts)
if not opts then opts = {} end
if not opts.winopts then opts.winopts = {} end
opts = vim.tbl_deep_extend("keep", opts, config.globals)
opts.winopts = vim.tbl_deep_extend("keep", opts.winopts, config.globals.winopts)
local strip_borderchars_hl = function(border)
local default = nil
if type(border) == 'string' then
default = config.globals.winopts._borderchars[border]
end
if not default then
default = config.globals.winopts._borderchars['rounded']
end
if not border or type(border) ~= 'table' or #border<8 then
return default
end
local borderchars = {}
for i=1, 8 do
if type(border[i]) == 'string' then
table.insert(borderchars, border[i])
elseif type(border[i]) == 'table' and type(border[i][1]) == 'string' then
-- can happen when border chars contains a highlight, i.e:
-- border = { {'╭', 'NormalFloat'}, {'─', 'NormalFloat'}, ... }
table.insert(borderchars, border[i][1])
else
table.insert(borderchars, default[i])
end
end
-- assert(#borderchars == 8)
return borderchars
end
local normalize_winopts = function(o)
-- make a local copy of opts so we
-- don't pollute the user's options
local opts = o or {}
opts.winopts = vim.tbl_deep_extend("keep", opts.winopts or {}, config.globals.winopts)
opts.winopts_fn = opts.winopts_fn or config.globals.winopts_fn
opts.winopts_raw = opts.winopts_raw or config.globals.winopts_raw
local raw = {}
if opts.winopts_raw and type(opts.winopts_raw) == "function" then
raw = opts.winopts_raw()
local winopts = utils.tbl_deep_clone(opts.winopts)
if type(opts.winopts_fn) == "function" then
winopts = vim.tbl_deep_extend("force", winopts, opts.winopts_fn())
end
if type(opts.winopts_raw) == "function" then
winopts = vim.tbl_deep_extend("force", winopts, opts.winopts_raw())
end
local winopts = opts.winopts
local height = raw.height or math.floor(vim.o.lines * winopts.win_height)
local width = raw.width or math.floor(vim.o.columns * winopts.win_width)
local row = raw.row or math.floor((vim.o.lines - height) * winopts.win_row)
local col = raw.col or math.floor((vim.o.columns - width) * winopts.win_col)
local border = raw.border or winopts.win_border
local scrollchar = raw.scrollchar or winopts.scrollchar
local hl_normal = raw.hl_normal or winopts.hl_normal
local hl_border = raw.hl_border or winopts.hl_border
local fullscreen = raw.fullscreen or winopts.fullscreen
local max_width = vim.o.columns-2
local max_height = vim.o.lines-4
winopts.width = math.min(max_width, winopts.width)
winopts.height = math.min(max_height, winopts.height)
if not winopts.height or winopts.height <= 1 then
winopts.height = math.floor(max_height * winopts.height)
end
if not winopts.width or winopts.width <= 1 then
winopts.width = math.floor(max_width * winopts.width)
end
if not winopts.row or winopts.row < 1 then
winopts.row = math.floor((vim.o.lines - winopts.height) * winopts.row)
end
if not winopts.col or winopts.col < 1 then
winopts.col = math.floor((vim.o.columns - winopts.width) * winopts.col)
end
winopts.col = math.min(winopts.col, max_width-winopts.width)
winopts.row = math.min(winopts.row, max_height-winopts.height)
-- normalize border option for nvim_open_win()
if border == false then
border = {}
for i=1, 8 do border[i] = ' ' end
elseif border == true or border == nil then
border = config.globals.winopts.borderchars
if not winopts.border or winopts.border == true then
winopts.border = 'rounded'
elseif winopts.border == false then
winopts.border = 'none'
end
-- We only allow 'none|single|double|rounded'
if type(winopts.border) == 'string' then
winopts.border = config.globals.winopts._borderchars[winopts.border] or
config.globals.winopts._borderchars['rounded']
end
-- Store a version of borderchars with no highlights
-- to be used in the border drawing functions
winopts.nohl_borderchars = strip_borderchars_hl(winopts.border)
-- parse preview options
local preview = opts.preview_horizontal
if opts.preview_layout == "vertical" then
preview = opts.preview_vertical
elseif opts.preview_layout == "flex" then
preview = utils._if(vim.o.columns>opts.flip_columns, opts.preview_horizontal, opts.preview_vertical)
local preview
if winopts.preview.layout == "horizontal" or
winopts.preview.layout == "flex" and
vim.o.columns > winopts.preview.flip_columns then
preview = winopts.preview.horizontal
else
preview = winopts.preview.vertical
end
-- builtin previewer params
local prev_pos = preview:match("[^:]+") or 'right'
local prev_size = tonumber(preview:match(":(%d+)%%")) or 50
winopts.preview_pos = preview:match("[^:]+") or 'right'
winopts.preview_size = tonumber(preview:match(":(%d+)%%")) or 50
return {
height = height, width = width, row = row, col = col, border = border,
window_on_create = raw.window_on_create or winopts.window_on_create,
split = raw.split or winopts.split,
hl_normal = hl_normal, hl_border = hl_border,
-- builtin previewer params
scrollchar = scrollchar,
preview_pos = prev_pos, preview_size = prev_size,
fullscreen = fullscreen
}
return winopts
end
function FzfWin:reset_win_highlights(win, is_border)
local hl = ("Normal:%s,FloatBorder:%s"):format(
self.winopts.hl_normal, self.winopts.hl_border)
if self._previewer and self._previewer.hl_cursorline then
hl = hl .. (",CursorLine:%s"):format(self._previewer.hl_cursorline)
self.winopts.hl.normal, self.winopts.hl.border)
if self._previewer and self.winopts.hl.cursorline then
hl = hl .. (",CursorLine:%s"):format(self.winopts.hl.cursorline)
end
if is_border then
-- our border is manuually drawn so we need
-- to replace Normal with the border color
hl = ("Normal:%s"):format(self.winopts.hl_border)
hl = ("Normal:%s"):format(self.winopts.hl.border)
end
vim.api.nvim_win_set_option(win, 'winhighlight', hl)
end
@ -234,7 +282,7 @@ function FzfWin.autoclose()
end
local function opt_matches(opts, key, str)
local opt = opts[key] or config.globals[key]
local opt = opts.winopts.preview[key] or config.globals.winopts.preview[key]
return opt and opt:match(str)
end
@ -248,8 +296,9 @@ function FzfWin:new(o)
self = setmetatable({}, { __index = self })
self.winopts = normalize_winopts(o)
self.fullscreen = self.winopts.fullscreen
self.preview_wrap = not opt_matches(o, 'preview_wrap', 'nowrap')
self.preview_hidden = not opt_matches(o, 'preview_opts', 'nohidden')
self.preview_wrap = not opt_matches(o, 'wrap', 'nowrap')
self.preview_hidden = not opt_matches(o, 'hidden', 'nohidden')
self.preview_border = not opt_matches(o, 'border', 'noborder')
self.keymap = o.keymap
self.previewer = o.previewer
self.previewer_type = o.previewer_type
@ -285,23 +334,25 @@ function FzfWin:fs_preview_layout(fs)
local height_diff = 0
local width_diff = 0
if preview_pos == 'down' or preview_pos == 'up' then
width_diff = vim.o.columns - border_winopts.width - 1
width_diff = vim.o.columns - border_winopts.width
if preview_pos == 'down' then
height_diff = vim.o.lines - border_winopts.row - border_winopts.height - 2
elseif preview_pos == 'up' then
height_diff = border_winopts.row - border_winopts.height
end
prev_winopts.col = prev_winopts.col - width_diff/2
border_winopts.col = border_winopts.col - width_diff/2
border_winopts.col = 0
prev_winopts.col = border_winopts.col + 1
elseif preview_pos == 'left' or preview_pos == 'right' then
height_diff = vim.o.lines - border_winopts.height - 2
if preview_pos == 'left' then
border_winopts.col = border_winopts.col - 1
prev_winopts.col = prev_winopts.col - 1
width_diff = border_winopts.col - border_winopts.width
elseif preview_pos == 'right' then
width_diff = vim.o.columns - border_winopts.col - border_winopts.width - 1
width_diff = vim.o.columns - border_winopts.col - border_winopts.width
end
prev_winopts.row = prev_winopts.row - height_diff/2
border_winopts.row = border_winopts.row - height_diff/2
border_winopts.row = 0
prev_winopts.row = border_winopts.row + 1
end
prev_winopts.height = prev_winopts.height + height_diff
@ -340,7 +391,6 @@ function FzfWin:preview_layout()
local winopts = {relative = 'editor', focusable = false, style = 'minimal'}
if self.winopts.split then
winopts = {relative = 'win', win = self.fzf_winid, focusable = false, style = 'minimal'}
width = width - 2
end
local preview_opts = vim.tbl_extend('force', winopts, {
anchor = anchor,
@ -371,35 +421,14 @@ function FzfWin:preview_winids()
return self.preview_winid, self.border_winid
end
local strip_border_highlights = function(border)
local default = config.globals.winopts.borderchars
if not border or type(border) ~= 'table' or #border<8 then
return default
end
local borderchars = {}
for i=1, 8 do
if type(border[i]) == 'string' then
table.insert(borderchars, border[i])
elseif type(border[i]) == 'table' and type(border[i][1]) == 'string' then
-- can happen when border chars contains a highlight, i.e:
-- border = { {'╭', 'NormalFloat'}, {'─', 'NormalFloat'}, ... }
table.insert(borderchars, border[i][1])
else
table.insert(borderchars, default[i])
end
end
-- assert(#borderchars == 8)
return borderchars
end
function FzfWin:update_border_buf()
local border_buf = self.border_buf
local border_winopts = self.border_winopts
local border_chars = strip_border_highlights(self.winopts.border)
local borderchars = self.winopts.nohl_borderchars
local width, height = border_winopts.width, border_winopts.height
local top = border_chars[1] .. border_chars[2]:rep(width - 2) .. border_chars[3]
local mid = border_chars[8] .. (' '):rep(width - 2) .. border_chars[4]
local bot = border_chars[7] .. border_chars[6]:rep(width - 2) .. border_chars[5]
local top = borderchars[1] .. borderchars[2]:rep(width - 2) .. borderchars[3]
local mid = borderchars[8] .. (' '):rep(width - 2) .. borderchars[4]
local bot = borderchars[7] .. borderchars[6]:rep(width - 2) .. borderchars[5]
local lines = {top}
for _ = 1, height - 2 do
table.insert(lines, mid)
@ -581,9 +610,9 @@ function FzfWin:create()
self:reset_win_highlights(self.fzf_winid)
if self.winopts.window_on_create and
type(self.winopts.window_on_create) == 'function' then
self.winopts.window_on_create()
if self.winopts.on_create and
type(self.winopts.on_create) == 'function' then
self.winopts.on_create()
end
-- create or redraw the preview win
@ -614,6 +643,19 @@ function FzfWin:close_preview()
if self.preview_winid and vim.api.nvim_win_is_valid(self.preview_winid) then
api.nvim_win_close(self.preview_winid, true)
end
if self._sbuf1 and vim.api.nvim_buf_is_valid(self._sbuf1) then
vim.api.nvim_buf_delete(self._sbuf1, {force=true})
end
if self._swin1 and vim.api.nvim_win_is_valid(self._swin1) then
api.nvim_win_close(self._swin1, true)
end
if self._sbuf2 and vim.api.nvim_buf_is_valid(self._sbuf2) then
vim.api.nvim_buf_delete(self._sbuf2, {force=true})
end
if self._swin2 and vim.api.nvim_win_is_valid(self._swin2) then
api.nvim_win_close(self._swin2, true)
end
self._sbuf1, self._sbuf2, self._swin1, self._swin2 = nil, nil, nil, nil
self.border_buf = nil
self.border_winid = nil
self.preview_winid = nil
@ -652,43 +694,174 @@ function FzfWin.win_leave()
_self:close()
end
function FzfWin:update_scrollbar()
if not self:validate_preview() then return end
local border_winid = self.border_winid
local preview_winid = self.preview_winid
local border_chars = strip_border_highlights(self.winopts.border)
local scrollchar = self.winopts.scrollchar
local buf = api.nvim_win_get_buf(preview_winid)
local border_buf = api.nvim_win_get_buf(border_winid)
local line_count = api.nvim_buf_line_count(buf)
function FzfWin:clear_border_highlights()
if self.border_winid and vim.api.nvim_win_is_valid(self.border_winid) then
vim.fn.clearmatches(self.border_winid)
end
end
local win_info = fn.getwininfo(preview_winid)[1]
local topline, height = win_info.topline, win_info.height
function FzfWin:set_title_hl()
if self.winopts.hl.title and self._title_len and self._title_len>0 then
vim.api.nvim_win_call(self.border_winid, function()
fn.matchaddpos(self.winopts.hl.title, {{1, 9, self._title_len}}, 11)
end)
end
end
function FzfWin:update_scrollbar_border(o)
-- do not display on files that are fully contained
if o.bar_height >= o.line_count then return end
local borderchars = self.winopts.nohl_borderchars
local scrollchars = self.winopts.preview.scrollchars
local bar_size = math.min(height, math.ceil(height * height / line_count))
-- bar_offset starts at 0, first line is 1
o.bar_offset = o.bar_offset+1
local bar_pos = math.ceil(height * topline / line_count)
if bar_pos + bar_size > height then
bar_pos = height - bar_size + 1
-- backward compatibility before 'scrollchar' was a table
if type(self.winopts.preview.scrollchar) == 'string' and
#self.winopts.preview.scrollchar > 0 then
scrollchars[1] = self.winopts.preview.scrollchar
end
for i=1,2 do
if not scrollchars[i] or #scrollchars[i]==0 then
scrollchars[i] = borderchars[4]
end
end
-- only accept a string
if not scrollchar or type(scrollchar) ~= 'string' then
scrollchar = ''
-- matchaddpos() can't handle more than 8 items at once
local add_to_tbl = function(tbl, item)
local len = utils.tbl_length(tbl)
if len==0 or utils.tbl_length(tbl[len])==8 then
table.insert(tbl, {})
len = len+1
end
table.insert(tbl[len], item)
end
local lines = api.nvim_buf_get_lines(border_buf, 1, -2, true)
local full, empty = {}, {}
local lines = api.nvim_buf_get_lines(self.border_buf, 1, -2, true)
for i = 1, #lines do
local line, linew = lines[i], fn.strwidth(lines[i])
local bar_char
if i >= bar_pos and i < bar_pos + bar_size then
bar_char = scrollchar
if i >= o.bar_offset and i < o.bar_offset + o.bar_height then
bar_char = scrollchars[1]
add_to_tbl(full, {i+1, linew+2, 1})
else
bar_char = scrollchars[2]
add_to_tbl(empty, {i+1, linew+2, 1})
end
lines[i] = fn.strcharpart(line, 0, linew - 1) .. bar_char
end
api.nvim_buf_set_lines(self.border_buf, 1, -2, 0, lines)
-- border highlights
if self.winopts.hl.scrollbar_f or self.winopts.hl.scrollbar_e then
vim.api.nvim_win_call(self.border_winid, function()
if self.winopts.hl.scrollbar_f then
for i=1,#full do
fn.matchaddpos(self.winopts.hl.scrollbar_f, full[i], 11)
end
end
if self.winopts.hl.scrollbar_e then
for i=1,#empty do
fn.matchaddpos(self.winopts.hl.scrollbar_e, empty[i], 11)
end
end
end)
end
end
local function ensure_tmp_buf(bufnr)
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
return bufnr
end
bufnr = api.nvim_create_buf(false, true)
-- running nvim with `-M` will reset modifiable's default value to false
vim.bo[bufnr].modifiable = true
vim.bo[bufnr].bufhidden = 'wipe'
return bufnr
end
function FzfWin:hide_scrollbar()
if self._swin1 and vim.api.nvim_win_is_valid(self._swin1) then
vim.api.nvim_win_hide(self._swin1)
self._swin1 = nil
end
if self._swin2 and vim.api.nvim_win_is_valid(self._swin2) then
vim.api.nvim_win_hide(self._swin2)
self._swin2 = nil
end
end
function FzfWin:update_scrollbar_float(o)
-- do not display on files that are fully contained
if o.bar_height >= o.line_count then
self:hide_scrollbar()
else
local info = o.wininfo
local style1 = {}
style1.relative = 'editor'
style1.style = 'minimal'
style1.width = 1
style1.height = info.height
style1.row = info.winrow - 1
style1.col = info.wincol + info.width +
(tonumber(self.winopts.preview.scrolloff) or -2)
style1.zindex = info.zindex or 998
if self._swin1 and vim.api.nvim_win_is_valid(self._swin1) then
vim.api.nvim_win_set_config(self._swin1, style1)
else
bar_char = border_chars[4]
style1.noautocmd = true
self._sbuf1 = ensure_tmp_buf(self._sbuf1)
self._swin1 = vim.api.nvim_open_win(self._sbuf1, false, style1)
local hl = self.winopts.hl.scrollbar_e or 'PmenuSbar'
vim.api.nvim_win_set_option(self._swin1, 'winhighlight',
('Normal:%s,NormalNC:%s,NormalFloat:%s'):format(hl, hl, hl))
end
local style2 = utils.tbl_deep_clone(style1)
style2.height = o.bar_height
style2.row = style1.row + o.bar_offset
style2.zindex = style1.zindex + 1
if self._swin2 and vim.api.nvim_win_is_valid(self._swin2) then
vim.api.nvim_win_set_config(self._swin2, style2)
else
style2.noautocmd = true
self._sbuf2 = ensure_tmp_buf(self._sbuf2)
self._swin2 = vim.api.nvim_open_win(self._sbuf2, false, style2)
local hl = self.winopts.hl.scrollbar_f or 'PmenuThumb'
vim.api.nvim_win_set_option(self._swin2, 'winhighlight',
('Normal:%s,NormalNC:%s,NormalFloat:%s'):format(hl, hl, hl))
end
local line = lines[i]
lines[i] = fn.strcharpart(line, 0, fn.strwidth(line) - 1) .. bar_char
end
api.nvim_buf_set_lines(border_buf, 1, -2, 0, lines)
end
function FzfWin:update_scrollbar()
if not self.winopts.preview.scrollbar
or self.winopts.preview.scrollbar == 'none'
or not self:validate_preview() then
return
end
local buf = api.nvim_win_get_buf(self.preview_winid)
local o = {}
o.wininfo = fn.getwininfo(self.preview_winid)[1]
o.line_count = api.nvim_buf_line_count(buf)
local topline, height = o.wininfo.topline, o.wininfo.height
o.bar_height = math.min(height, math.ceil(height * height / o.line_count))
o.bar_offset = math.min(height - o.bar_height, math.floor(height * topline / o.line_count))
-- reset highlights before we move the scrollbar
self:clear_border_highlights()
self:set_title_hl()
if self.winopts.preview.scrollbar == 'float' then
self:update_scrollbar_float(o)
else
self:update_scrollbar_border(o)
end
end
function FzfWin:update_title(title)
@ -700,17 +873,21 @@ function FzfWin:update_title(title)
if #title > width-right_pad then
title = title:sub(1, width-right_pad) .. " "
end
-- save for set_title_hl
self._title_len = #title
local prefix = fn.strcharpart(top, 0, 3)
local suffix = fn.strcharpart(top, fn.strwidth(title) + 3, fn.strwidth(top))
title = ('%s%s%s'):format(prefix, title, suffix)
api.nvim_buf_set_lines(border_buf, 0, 1, 1, {title})
self:set_title_hl()
end
-- keybind methods below
function FzfWin.toggle_fullscreen()
if not _self then return end
if not _self or _self.winopts.split then return end
local self = _self
self.fullscreen = not self.fullscreen
self:hide_scrollbar()
if self and self:validate() then
self:redraw()
end
@ -723,6 +900,9 @@ function FzfWin.toggle_preview()
if not _self then return end
local self = _self
self.preview_hidden = not self.preview_hidden
if self.winopts.split and self._fzf_toggle_prev_bind then
utils.feed_keys_termcodes(self._fzf_toggle_prev_bind)
end
if self.preview_hidden and self:validate_preview() then
self:close_preview()
self:redraw()

Loading…
Cancel
Save