Skip to content

Commit fc70792

Browse files
committed
feat(win)!: support additional border styles (#1668)
Refactor win class code: - Support both neovim and fzf border styles for `winopts.border|winopts.preview.border`, styles inlcude: ``` # `nvim_open_win` styles none single double rounded solid shadow # fzf-lua styles (some are aliases) empty bold block solidblock thicc thiccc thicccc # fzf borders border noborder border-none border-rounded border-sharp border-bold border-double border-block border-thinblock border-horizontal border-top border-bottom ``` - Improve `treesitter-context` attach - Refactor (normalize) layout generation code - Refactor scrollbar code - Refactor preview border: remove dedicated border window and use `nvim_open_win` title options instead (neovim >= 0.9) - Fix scroll position caching with entries that have no line|col - Fix fullscreen with `winopts.relative=cursor` - Added an option for custom `winopts.split` function, this enables the use to create their own window for fzf-lua Other changes: - Require Neovim 0.7, remove backward compat code for 0.5|0.6 - Default profile set to `default-title` on neovim >= 0.9 - Renamed profile `borderless_full` -> `borderless-full` - Modify profiles `borderless|borderless-full` to use the new border options - New profile: `border-fused`, fuse fzf and preview window together with minimum spacing, will look similar to fzf's native layout where the preivewer is "contained" inside fzf - Modified defaults (backward compat): + winopts.preview.wrap: `nowrap` -> `false` + winopts.preview.hidden: `nohidden` -> `false` + winopts.preview.border: `border` -> `rounded` - Defaulted `grep.rg_glob` to true
1 parent 60428a8 commit fc70792

23 files changed

+600
-663
lines changed

OPTIONS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,13 +312,13 @@ Debounce time (milliseconds) for displaying the preview buffer in the builtin pr
312312

313313
#### globals.winopts.preview.wrap
314314

315-
Type: `string`, Default: `nowrap`
315+
Type: `boolean`, Default: `false`
316316

317317
Line wrap in both native fzf and the builtin previewer, mapped to fzf's `--preview-window:[no]wrap` flag.
318318

319319
#### globals.winopts.preview.hidden
320320

321-
Type: `string`, Default: `nohidden`
321+
Type: `boolean`, Default: `false`
322322

323323
Preview startup visibility in both native fzf and the builtin previewer, mapped to fzf's `--preview-window:[no]hidden` flag.
324324

@@ -382,7 +382,7 @@ Scrollbar style in the builtin previewer, set to `false` to disable, possible va
382382

383383
#### globals.winopts.preview.scrolloff
384384

385-
Type: `number`, Default: `-2`
385+
Type: `number`, Default: `-1`
386386

387387
Float style scrollbar offset from the right edge of the preview window.
388388

README.md

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# fzf :heart: lua
44

5-
![Neovim version](https://img.shields.io/badge/Neovim-0.5-57A143?style=flat-square&logo=neovim)
5+
![Neovim version](https://img.shields.io/badge/Neovim-0.7-57A143?style=flat-square&logo=neovim)
66

77
[Quickstart](#quickstart)[Installation](#installation)[Usage](#usage)[Commands](#commands)[Customization](#customization)[Wiki](https://github.com/ibhagwan/fzf-lua/wiki)
88

@@ -55,7 +55,7 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim)
5555

5656
### Dependencies
5757

58-
- [`neovim`](https://github.com/neovim/neovim/releases) version > `0.5.0`
58+
- [`neovim`](https://github.com/neovim/neovim/releases) version > `0.7.0`
5959
- [`fzf`](https://github.com/junegunn/fzf) version > `0.25`
6060
or [`skim`](https://github.com/skim-rs/skim) binary installed
6161
- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons)
@@ -406,9 +406,8 @@ winopts = {
406406
width = 0.80, -- window width
407407
row = 0.35, -- window row position (0=top, 1=bottom)
408408
col = 0.50, -- window col position (0=left, 1=right)
409-
-- border argument passthrough to nvim_open_win(), also used
410-
-- 'none', 'single', 'double', 'thicc' (+cc) or 'rounded' (default)
411-
border = { '', '', '', '', '', '', '', '' },
409+
-- border argument passthrough to nvim_open_win()
410+
border = "rounded",
412411
-- Backdrop opacity, 0 is fully opaque, 100 is fully transparent (i.e. disabled)
413412
backdrop = 60,
414413
-- title = "Title",
@@ -425,34 +424,36 @@ winopts = {
425424
preview = {
426425
-- default = 'bat', -- override the default previewer?
427426
-- default uses the 'builtin' previewer
428-
border = 'border', -- border|noborder, applies only to
427+
border = "rounded", -- preview border: accepts both `nvim_open_win`
428+
-- and fzf values (e.g. "border-top", "none")
429429
-- native fzf previewers (bat/cat/git/etc)
430-
wrap = 'nowrap', -- wrap|nowrap
431-
hidden = 'nohidden', -- hidden|nohidden
432-
vertical = 'down:45%', -- up|down:size
433-
horizontal = 'right:60%', -- right|left:size
434-
layout = 'flex', -- horizontal|vertical|flex
430+
-- can also be set to `fun(winopts, metadata)`
431+
wrap = false, -- preview line wrap (fzf's 'wrap|nowrap')
432+
hidden = false, -- start preview hidden
433+
vertical = "down:45%", -- up|down:size
434+
horizontal = "right:60%", -- right|left:size
435+
layout = "flex", -- horizontal|vertical|flex
435436
flip_columns = 100, -- #cols to switch to horizontal on flex
436437
-- Only used with the builtin previewer:
437438
title = true, -- preview border title (file/buf)?
438439
title_pos = "center", -- left|center|right, title alignment
439-
scrollbar = 'float', -- `false` or string:'float|border'
440+
scrollbar = "float", -- `false` or string:'float|border'
440441
-- float: in-window floating border
441442
-- border: in-border "block" marker
442-
scrolloff = '-2', -- float scrollbar offset from right
443+
scrolloff = -1, -- float scrollbar offset from right
443444
-- applies only when scrollbar = 'float'
444445
delay = 20, -- delay(ms) displaying the preview
445446
-- prevents lag on fast scrolling
446447
winopts = { -- builtin previewer window options
447448
number = true,
448449
relativenumber = false,
449450
cursorline = true,
450-
cursorlineopt = 'both',
451+
cursorlineopt = "both",
451452
cursorcolumn = false,
452-
signcolumn = 'no',
453+
signcolumn = "no",
453454
list = false,
454455
foldenable = false,
455-
foldmethod = 'manual',
456+
foldmethod = "manual",
456457
},
457458
},
458459
on_create = function()
@@ -942,7 +943,7 @@ previewers = {
942943
-- search strings will be split using the 'glob_separator' and translated
943944
-- to '--iglob=' arguments, requires 'rg'
944945
-- can still be used when 'false' by calling 'live_grep_glob' directly
945-
rg_glob = false, -- default to glob parsing?
946+
rg_glob = true, -- default to glob parsing with `rg`
946947
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
947948
glob_separator = "%s%-%-", -- query separator pattern (lua): ' --'
948949
-- advanced usage: for custom argument parsing define
@@ -1248,7 +1249,7 @@ previewers = {
12481249
-- actions inherit from 'actions.files' and merge
12491250
actions = { ["enter"] = actions.complete },
12501251
-- previewer hidden by default
1251-
winopts = { preview = { hidden = "hidden" } },
1252+
winopts = { preview = { hidden = true } },
12521253
},
12531254
-- uncomment to use fzf native previewers
12541255
-- (instead of using a neovim floating window)
@@ -1345,16 +1346,19 @@ require('fzf-lua').setup({'fzf-vim'})
13451346

13461347
#### Available Profiles
13471348

1348-
| Profile | Details |
1349-
| --------------- | --------------------------------------------------------------------------------------------------- |
1350-
| `default` | fzf-lua defaults, uses neovim "builtin" previewer and devicons (if available) for git/files/buffers |
1351-
| `default-title` | fzf-lua defaults, using title instead of prompt |
1352-
| `fzf-native` | utilizes fzf's native previewing ability in the terminal where possible using `bat` for previews |
1353-
| `fzf-tmux` | similar to `fzf-native` and opens in a tmux popup (requires tmux > 3.2) |
1354-
| `fzf-vim` | closest to `fzf.vim`'s defaults (+icons), also sets up user commands (`:Files`, `:Rg`, etc) |
1355-
| `max-perf` | similar to `fzf-native` and disables icons globally for max performance |
1356-
| `telescope` | closest match to telescope defaults in look and feel and keybinds |
1357-
| `skim` | uses [`skim`](https://github.com/skim-rs/skim) as an fzf alternative, (requires the `sk` binary) |
1349+
| Profile | Details |
1350+
| ----------------- | --------------------------------------------------------------------------------------------------- |
1351+
| `default` | fzf-lua defaults, uses neovim "builtin" previewer and devicons (if available) for git/files/buffers |
1352+
| `default-title` | fzf-lua defaults, using title instead of prompt (default on neovim > =0.9) |
1353+
| `fzf-native` | utilizes fzf's native previewing ability in the terminal where possible using `bat` for previews |
1354+
| `fzf-tmux` | similar to `fzf-native` and opens in a tmux popup (requires tmux > 3.2) |
1355+
| `fzf-vim` | closest to `fzf.vim`'s defaults (+icons), also sets up user commands (`:Files`, `:Rg`, etc) |
1356+
| `max-perf` | similar to `fzf-native` and disables icons globally for max performance |
1357+
| `telescope` | closest match to telescope defaults in look and feel and keybinds |
1358+
| `skim` | uses [`skim`](https://github.com/skim-rs/skim) as an fzf alternative, (requires the `sk` binary) |
1359+
| `borderless` | borderless and minimalistic seamless look & feel |
1360+
| `borderless-full` | borderless with description in window title (instead of prompt) |
1361+
| `border-fused` | single border around both fzf and the previewer |
13581362

13591363
</details>
13601364

@@ -1386,7 +1390,7 @@ vim.keymap.set({ "i" }, "<C-x><C-f>",
13861390
function()
13871391
require("fzf-lua").complete_file({
13881392
cmd = "rg --files",
1389-
winopts = { preview = { hidden = "nohidden" } }
1393+
winopts = { preview = { hidden = true } }
13901394
})
13911395
end, { silent = true, desc = "Fuzzy complete file" })
13921396
```

doc/fzf-lua-opts.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*fzf-lua-opts.txt* For Neovim >= 0.8.0 Last change: 2024 December 18
1+
*fzf-lua-opts.txt* For Neovim >= 0.8.0 Last change: 2025 January 05
22

33
==============================================================================
44
Table of Contents *fzf-lua-opts-table-of-contents*
@@ -408,7 +408,7 @@ previewer.
408408

409409
globals.winopts.preview.wrap *fzf-lua-opts-globals.winopts.preview.wrap*
410410

411-
Type: `string`, Default: `nowrap`
411+
Type: `boolean`, Default: `false`
412412

413413
Line wrap in both native fzf and the builtin previewer, mapped to fzf's
414414
`--preview-window:[no]wrap` flag.
@@ -417,7 +417,7 @@ Line wrap in both native fzf and the builtin previewer, mapped to fzf's
417417

418418
globals.winopts.preview.hidden *fzf-lua-opts-globals.winopts.preview.hidden*
419419

420-
Type: `string`, Default: `nohidden`
420+
Type: `boolean`, Default: `false`
421421

422422
Preview startup visibility in both native fzf and the builtin previewer,
423423
mapped to fzf's `--preview-window:[no]hidden` flag.
@@ -504,7 +504,7 @@ values are `float|border`.
504504

505505
globals.winopts.preview.scrolloff*fzf-lua-opts-globals.winopts.preview.scrolloff*
506506

507-
Type: `number`, Default: `-2`
507+
Type: `number`, Default: `-1`
508508

509509
Float style scrollbar offset from the right edge of the preview window.
510510

lua/fzf-lua/config.lua

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,37 @@ function M.normalize_opts(opts, globals, __resume_key)
236236
-- merge with provider defaults from globals (defaults + setup options)
237237
opts = vim.tbl_deep_extend("keep", opts, utils.tbl_deep_clone(globals))
238238

239+
-- Backward compat: merge `winopts` with outputs from `winopts_fn`
240+
local winopts_fn = opts.winopts_fn or M.globals.winopts_fn
241+
if type(winopts_fn) == "function" then
242+
if not opts.silent then
243+
utils.warn(
244+
"Deprecated option: 'winopts_fn' -> 'winopts'. Add 'silent=true' to hide this message.")
245+
end
246+
local ret = winopts_fn(opts) or {}
247+
if not utils.tbl_isempty(ret) and (not opts.winopts or type(opts.winopts) == "table") then
248+
opts.winopts = vim.tbl_deep_extend("force", opts.winopts or {}, ret)
249+
end
250+
end
251+
239252
-- Merge values from globals
240253
for _, k in ipairs({
241254
"winopts", "keymap", "fzf_opts", "fzf_colors", "fzf_tmux_opts", "hls"
242255
}) do
243256
local setup_val = M.globals[k]
257+
if type(setup_val) == "function" then
258+
setup_val = setup_val(opts)
259+
if type(setup_val) == "table" then
260+
local default_val = utils.map_get(M.defaults, k)
261+
if type(default_val) == "table" then
262+
setup_val = vim.tbl_deep_extend("force", {}, default_val, setup_val)
263+
end
264+
end
265+
end
244266
if type(setup_val) == "table" then
245267
-- must clone or map will be saved as reference
246268
-- and then overwritten if found in 'backward_compat'
247269
setup_val = utils.tbl_deep_clone(setup_val)
248-
elseif type(setup_val) == "function" then
249-
setup_val = setup_val(opts)
250270
end
251271
if opts[k] == nil then
252272
opts[k] = setup_val
@@ -268,6 +288,14 @@ function M.normalize_opts(opts, globals, __resume_key)
268288
if v == "" then opts.fzf_opts[k] = true end
269289
end
270290

291+
-- backward compat for `winopts.preview.{wrap|hidden}`
292+
for k, v in pairs({ wrap = "nowrap", hidden = "nohidden" }) do
293+
local val = utils.map_get(opts, "winopts.preview." .. k)
294+
if type(val) == "string" then
295+
utils.map_set(opts, "winopts.preview." .. k, not val:match(v))
296+
end
297+
end
298+
271299
-- fzf.vim's `g:fzf_history_dir` (#1127)
272300
if vim.g.fzf_history_dir and opts.fzf_opts["--history"] == nil then
273301
local histdir = libuv.expand(vim.g.fzf_history_dir)
@@ -279,12 +307,6 @@ function M.normalize_opts(opts, globals, __resume_key)
279307
end
280308
end
281309

282-
-- Merge `winopts` with outputs from `winopts_fn`
283-
local winopts_fn = opts.winopts_fn or M.globals.winopts_fn
284-
if type(winopts_fn) == "function" then
285-
opts.winopts = vim.tbl_deep_extend("force", opts.winopts, winopts_fn(opts) or {})
286-
end
287-
288310
-- Merge arrays from globals|defaults, can't use 'vim.tbl_xxx'
289311
-- for these as they only work for maps, ie. '{ key = value }'
290312
for _, k in ipairs({ "file_ignore_patterns" }) do
@@ -449,10 +471,21 @@ function M.normalize_opts(opts, globals, __resume_key)
449471
-- globals.winopts.preview.default
450472
opts.previewer = opts.previewer()
451473
end
452-
if type(opts.previewer) == "table" or opts.previewer == true then
453-
-- merge with the default builtin previewer
474+
-- "Shortcut" values to the builtin previewer
475+
-- merge with builtin previewer defaults
476+
if type(opts.previewer) == "table"
477+
or opts.previewer == true
478+
or opts.previewer == "hidden"
479+
or opts.previewer == "nohidden"
480+
then
481+
-- of type string, can only be "hidden|nohidden"
482+
if type(opts.previewer) == "string" then
483+
assert(opts.previewer == "hidden" or opts.previewer == "nohidden")
484+
utils.map_set(opts, "winopts.preview.hidden", opts.previewer ~= "nohidden")
485+
end
454486
opts.previewer = vim.tbl_deep_extend("keep",
455-
type(opts.previewer) == "table" and opts.previewer or {}, M.globals.previewers.builtin)
487+
type(opts.previewer) == "table" and opts.previewer or {},
488+
M.globals.previewers.builtin)
456489
end
457490

458491
-- Convert again in case the bool option came from global opts

lua/fzf-lua/core.lua

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,6 @@ M.fzf = function(contents, opts)
407407

408408
fzf_win:attach_previewer(previewer)
409409
local fzf_bufnr = fzf_win:create()
410-
-- save the normalized winopts, otherwise we
411-
-- lose overrides by 'winopts_fn|winopts_raw'
412-
opts.winopts.preview = fzf_win.winopts.preview
413410
-- convert "reload" actions to fzf's `reload` binds
414411
-- convert "exec_silent" actions to fzf's `execute-silent` binds
415412
opts = M.convert_reload_actions(opts.__reload_cmd or contents, opts)
@@ -464,12 +461,50 @@ M.fzf = function(contents, opts)
464461
return selected
465462
end
466463

464+
-- Best approximation of neovim border types to fzf border types
465+
local function translate_border(winopts, metadata)
466+
local neovim2fzf = {
467+
none = "noborder",
468+
single = "border-sharp",
469+
double = "border-double",
470+
rounded = "border-rounded",
471+
solid = "noborder",
472+
empty = "border-block",
473+
shadow = "border-thinblock",
474+
bold = "border-bold",
475+
block = "border-block",
476+
solidblock = "border-block",
477+
thicc = "border-bold",
478+
thiccc = "border-block",
479+
thicccc = "border-block",
480+
}
481+
local border = winopts.border
482+
if not border then border = "none" end
483+
if border == true then border = "border" end
484+
if type(border) == "function" then
485+
border = border(winopts, metadata)
486+
end
487+
border = type(border) == "string" and (neovim2fzf[border] or border) or nil
488+
return border
489+
end
490+
467491
---@param o table
468492
---@return string
469493
M.preview_window = function(o, fzf_win)
470494
local layout
471-
local prefix = string.format("%s:%s:%s",
472-
o.winopts.preview.hidden, o.winopts.preview.border, o.winopts.preview.wrap)
495+
local prefix = string.format("%s:%s%s",
496+
o.winopts.preview.hidden and "hidden" or "nohidden",
497+
o.winopts.preview.wrap and "wrap" or "nowrap",
498+
(function()
499+
local border = (function()
500+
local preview_str = fzf_win:fzf_preview_layout_str()
501+
local preview_pos = preview_str:match("[^:]+") or "right"
502+
return translate_border(o.winopts.preview,
503+
{ type = "fzf", name = "prev", layout = preview_pos })
504+
end)()
505+
return border and string.format(":%s", border) or ""
506+
end)()
507+
)
473508
if utils.has(o, "fzf", { 0, 31 })
474509
and o.winopts.preview.layout == "flex"
475510
and tonumber(o.winopts.preview.flip_columns) > 0

0 commit comments

Comments
 (0)