-
Notifications
You must be signed in to change notification settings - Fork 161
Home
Click here for ADVANCED CUSTOMIZATION / API DOCS
- General
- Customization
- Provider Options
-
Appearance
- How do I change the window size and position?
- Can I set the window size dynamically?
- How do I change the layout of fzf and the preview window?
- How do I set the preview to start hidden?
- How do I set the preview syntax highlight theme?
- How do I change the colors of the fzf popup window?
- Can I use fzf-lua in a split instead of a window?
- Icon padding for terminals with double width icon support
- Auto redraw when neovim window is resized
- How do I disable current working dir in
files
prompt? - Automatic sizing of height/width of
vim.ui.select
- Keybinds
-
Misc
- Set current working directory for providers
- How do I exclude paths (e.g. 'node_modules')?
- How do I ignore specific file patterns?
- What does
multiprocess
do? - What's the difference between
grep
andlive_grep
? - How can I simulate fzf.vim's
Rg
command? - Can I continue from the last search?
live_grep
shows no results before I start typing- Can I use ripgrep's
--iglob
option withlive_grep
? - How can I send custom flags to ripgrep with
live_grep
? - How can I restrict grep search to certain files?
- Search results do not appear in the same order
- LSP: prevent window flashing when no results
- LSP: jump to location for single result
- LSP references: ignore current line
- LSP references: ignore declaration
- Disable or hide filename fuzzy search
- How do I get maximum performance out of fzf-lua?
- Can fzf-lua preview media files?
- A recommended workflow for big projects
Fzf-lua is a neovim (>0.5) plugin written in lua integrating
fzf
into the neovim ecosystem.
fzf
is described as a general-purpose command line fuzzy finder meaning
you can search any output using the fuzzy search algorithm. This opaque
description hides an endless wealth of functionality from searching files,
processes, command-line history and much more. fzf-lua aims to bring all this
goodness to neovim.
Wait, but isn't there already a vim/neovim plugin for that,
fzf.vim
?
fzf.vim
is a great plugin, it worked
flawlessly for me for a long time and until I recently I wouldn't have
considered replacing it.
So what changed? Neovim 0.5 was released with lua programming language as a first class citizen and with it the opportunity to extend neovim for anyone who has been avoiding it due to the vimL scripting language. Don't get me wrong, there's nothing wrong with vimL but I personally found it not very intuitive and although I've been using vim for years now I avoided writing anything but the absolute basics in vimL.
Pros over fzf.vim
:
- Written in pure lua especially for neovim 0.5
- Customizable but has sane defaults
- Easily extendible in lua
- Builtin providers for LSP and quickfix lists (references, definitions,
diagnostics, symbols, etc) without requiring additional plugins like
nvim-lspfuzzy
orcoc-fzf
- Builtin support for file icons and git indicators
Cons vs fzf.vim
:
- Does not work with vim or any neovim version lower than 0.5
Fzf-lua only requirements are the fzf
binary, it does not
require nor conflict with fzf.vim
.
Fzf-lua utilizes a separate neovim --headless --clean
instance to do it's
processing which is then piped into fzf in a neovim terminal windows, it does
not require any of the vim functions exposed by the fzf
or fzf.vim
plugins.
telescope.nvim
is a
great fuzzy finding ecosystem written by the core maintainer of neovim,
@tjdevries, it has a thriving ecosystem of
extensions and a big community, some of its advantages over fzf-lua
are:
- Tightly integrated within neovim, does not require an external program or process
- Vast amount of extensions and vast support for pretty much anything
- Big community
That said, after using telescope for a while it had a few shortcomings which pushed me towards developing fzf-lua:
-
Inability to cycle through the results, you are only able to see one page of the results and narrow them down using fuzzy search, you can't page up/down (ctrl-b|f) the results paneNo longer relevant since Telescope#1232 - Performance issue when operating on large files and code bases, that is partly due to telescope being mostly synchronous. Recently telescope merged plenary PR #83: async await using libuv and telescope PR #709: feat: asyncify pickers in an effort to solve these issues.
- A rather large code base and community naturally introduces more complexity which has greater potential for bugs and issues down the road.
In addition, I really liked fzf
, it had worked flawlessly for me for a long
time and I enjoy having uniformity and similar experience/interface throughout
my shell, tmux and neovim.
To get the best out of fzf-lua I highly recommend installing the below:
-
fd: a better version of the
find
utility -
ripgrep(rg): a better version of
the
grep
utility
In addition, in neovim, use your favorite plugin manager to install
kyazdani42/nvim-web-devicons
in order to be able to display file icons in
the interface.
If you're using packer.nvim
add
the below to packer.startup({})
:
use { 'ibhagwan/fzf-lua',
requires = { 'kyazdani42/nvim-web-devicons' },
}
If you're using vim-plug
:
Plug 'ibhagwan/fzf-lua'
Plug 'kyazdani42/nvim-web-devicons'
There are two ways of testing fzf-lua with minimal init:
❯ sh -c "$(curl -s https://raw.githubusercontent.com/ibhagwan/fzf-lua/main/scripts/mini.sh)"
NOTES:
- Creates a sandbox environment for neovim under the OS temp dir (i.e.
/tmp/fzf-lua.tmp/
)- Also downloads
nvim-web-devicons
so devicons integration can be tested- Default binds:
<C-k>
::FzfLua builtin
<C-p>
::FzfLua files
<F1>
::FzfLua help_tags
If you wish to modify the default init.lua
and test your own version,
download fzf-lua and run the script locally:
❯ git clone https://github.com/ibhagwan/fzf-lua.git
❯ cd fzf-lua
# optional: edit 'scripts/init.lua'
# make sure to delete the previous sandbox (OSX will
# be a different folder, use `mktemp -d` to discover)
❯ rm -rf /tmp/fzf-lua.tmp
❯ ./scripts/mini.sh
Download (+modify setup) and run:
❯ curl -LO https://raw.githubusercontent.com/ibhagwan/fzf-lua/main/scripts/minimal_init.lua
❯ nvim -u minimal_init.lua
Or run directly from github:
❯ nvim -u <((echo "lua << EOF") && (curl -s https://raw.githubusercontent.com/ibhagwan/fzf-lua/main/scripts/minimal_init.lua) && (echo "EOF"))
NOTES:
nvim-web-devicons
is not used here, to test fzf-lua with devicons usescripts/mini.sh
- The script uses
packer.nvim
but does not remove your currently downloaded plugins, it does not load optional plugins or runsetup
for auto-loaded plugins, therefore plugin conflicts (although rare) can still happen- Default binds:
<C-p>
::FzfLua files
require('fzf-lua').setup {
fzf_opts = { ['--cycle'] = true }
}
It's important to understand how customization works in order to understand the following sections better as all options are specified in the same manner and adhere to the same order:
- Global options
- Provider options
- Function call options
For example, if we wanted to change the height of the files
window, we could
do so in 3 different ways:
- Apply a global
height
setting:
require("fzf-lua").setup({
winopts = { height = 0.4 }
})
- Apply
height
under thefiles
provider:
require("fzf-lua").setup({
files = {
prompt = 'Files❯ ', -- example, not required
winopts = { height = 0.4 },
}
})
- Apply
height
to a single call:
:lua require("fzf-lua").files({ winopts = { height = 0.4 } })
- For dynamically generated arguments, options can also be defined as a function:
:lua require("fzf-lua").files(function() return {cwd_header=true, cwd=vim.loop.cwd()} end)
Most options defaults can be found on the project's main page, unfortunately, some options are not yet properly documented, searching the project's issues is your best bet of finding an esoteric option (e.g.
live_grep({exec_empty_query=true})
)
The window look and feel is controlled by 4 parameters, winopts.height
,
winopts.width
, winopts.row
and winopts.col
, their values can either contain a range from
0.0 - 1.0
and correspond as a percentage of available screen space based on nvim width and height
(vim.o.columns
, vim.o.lines
) or they can also accept a normal integer value indicating exact size.
If you wish to set some (or all) winopts
parameters dynamically, you can
define winopts_fn
as a function that returns the parameters you wish to
override:
require("fzf-lua").setup({
winopts_fn = function()
-- smaller width if neovim win has over 80 columns
return { width = vim.o.columns>80 and 0.65 or 0.85 }
end,
})
The appearance of fzf (input box location, colors, etc) is controlled by the fzf binary.
Fzf-lua exposes all fzf options through the config, fzf_opts
can accept any
fzf flag, fzf_colors
controls the colors and keymap.fzf
controls all binds
that are native to fzf and the preview when using the native fzf previewer.
To change the location of the fuzzy search input field set 'fzf_opts[--layout']
option which is equal to the fzf --layout=
command line flag, valid values are
default
, reverse
and reverse-list
.
Fundamentally fzf-lua has two different preview window options, the first preview option is controlled by the fzf binary and the second is a neovim floating window, some providers support both options (mostly files-based providers) and some support only the native fzf preview window.
The default previewer for files uses neovim's floating win, if you wish to use
bat
as a previewer set winopts.preview.default = 'bat'
.
To change the location of the preview window, set the winopts.preview.layout
option, valid values are vertical
, horizontal
and flex
, when set to flex
the layout will dynamically switch between vertical and horizontal based on
the number of available columns in conjunction with the flip_columns
option.
E.g. when flip_columns = 120
the layout will be set to vertical
(i.e.
preview on the bottom) when screen size is less than 120 columns or horizontal
otherwise.
To control location of the preview of the preview window
(up|down|left|right
) look into winopts.preview.horizontal
and winopts.preview.vertical
respectively.
Set winopts.preview.hidden
option to control the preview visibility (works
for both neovim floating preview and native fzf's --preview-window=
), can be
set to hidden
or nohidden
.
Note: The above also applies to the preview
wrap|nowrap
option which controls word wrapping in the preview window.
When using the default neovim float win previewer the syntax highlight will match the current neovim colorscheme.
To enable preview syntax highlighting when using fzf native previewer, you need
to install bat
which is the fancy version of
cat
(used as the fzf previewer).
Fzf-lua will auto-detect you have bat installed and use it automatically, run
bat --list-themes
in your shell to preview all available theme and set
previewers.bat.theme
to your preferred theme.
Fzf colors can be set under fzf_colors
, fzf-lua will then translate the
neovim highlight to a RGB color and pass it to fzf using the --color=
flag.
The popup colors of fzf (bg/background) as well as the neovim floating window
preview colors are defined under winopts.hl
.
Yes, this requires setting the winopts.split
command, the command will then
run right before fzf-lua starts:
Once
split
option is set all other layout options for the window (col
,row
,border
, etc) are ignored.
require("fzf-lua").setup({
winopts = {
-- Use **only one** of the below options
split = "aboveleft new" -- open in split above current window
split = "belowright new" -- open in split below current window
split = "aboveleft vnew" -- open in split left of current window
split = "belowright vnew" -- open in split right of current window
split = "topleft new" -- open in a full-width split on top
split = "botright new" -- open in a full-width split on the bottom
split = "topleft vnew" -- open in a full-height split on the far left
split = "botright vnew" -- open in a full-height split on the far right
}
})
Some terminals (such as kitty
) support rendering double-width icons, this
can cause the edge of wide icons to be cut off, this can be solved with the
file_icon_padding
option:
require("fzf-lua").setup({
file_icon_padding = ' ',
})
FzfLua will automatically redraw when the neovim window size changes and the
VimResized
event is triggered.
If for some reason you still need manually redraw the fzf-lua window you can
call :FzfLua redraw
or :lua require'fzf-lua'.redraw()
to manually trigger
the redraw.
NOTE: fzf-lua is a terminal window, you need to first press
<C-\>C-n>
to get toNormal
mode before you can issue the:FzfLua ...
command.
You can also use an autocmd to trigger an automatic redraw once certain events happen, below is an equivalent of the buffer local autocmd setup by fzf-lua:
vim.api.nvim_create_autocmd("VimResized", {
pattern = '*',
command = 'lua require("fzf-lua").redraw()'
})
-- using the default prompt
:lua require("fzf-lua").files({ cwd_prompt = false })
-- using a custom prompt
:lua require("fzf-lua").files({ cwd_prompt = false, prompt = 'Files❯ ' })
See #793 for more info
require("fzf-lua").register_ui_select(function(_, items)
local min_h, max_h = 0.15, 0.70
local h = (#items + 4) / vim.o.lines
if h < min_h then
h = min_h
elseif h > max_h then
h = max_h
end
return { winopts = { height = h, width = 0.60, row = 0.40 } }
end)
All keybinds for both the neovim floating win previewer and fzf are defined
under the keymap.builtin
and keymap.fzf
tables respectively.
When defining keymap.builtin
binds you must use neovim-style keymaps
(i.e. <C-c>
for Control-C).
When defining keymap.fzf
binds you must use fzf-style keymas (i.e. ctrl-c
for Control-c), man fzf
for all available fzf mappings.
If you wish to define custom logic or define fzf binds that are otherwise not
available you can do so inside the winopts.on_create
callback event.
For example, the Super/Meta key is not available to use with fzf but you can map
<Super-Backspace>
to <ctrl-u>
with:
fzf_lua.setup({
winopts = {
on_create = function()
-- creates a local buffer mapping translating <M-BS> to <C-u>
vim.keymap.set("t", "<M-BS>",
"<cmd>lua vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<C-u>', true, false, true), 'n', true)<CR>",
{nowait = true, buffer = true})
end
}
})
Actions are essentially fzf keybinds which control what to do with selected entries.
All file actions inherit from actions.files
and all buffer actions inherit
from actions.buffers
which then gets merged with the provider actions table
(if such table exists), for example the actions for files
are defined as
actions.files
table merged with files.actions
table.
It's important to note not all providers inherit from the global actions
table:
-
actions.files
is inherited by:files
,git_files
,git_status
,grep
,lsp
,oldfiles
,quickfix
,loclist
,tags
,btags
args
,tagstack
-
actions.buffers
is inherited by:buffers
,tabs
,lines
,blines
For example, the default action for file edit is to open a single file or send multiple selection to the quickfix list, if you wish to change the default behavior to open multiple selection you need to define:
local actions = require'fzf-lua.actions'
require 'fzf-lua'.setup({
actions = {
files = {
-- instead of the default action 'actions.file_edit_or_qf'
-- it's important to define all other actions here as this
-- table does not get merged with the global defaults
["default"] = actions.file_edit,
["ctrl-s"] = actions.file_split,
["ctrl-v"] = actions.file_vsplit,
["ctrl-t"] = actions.file_tabedit,
["alt-q"] = actions.file_sel_to_qf,
},
},
})
Fzf supports saving input history into a history file using --history
flag.
You can configure a history file globally or per provider. Once the --history
flag is supplied fzf will automatically map ctrl-{n|p}
to
{next|previous}-history
, you can change the default binds under keymap.fzf
.
Example #1: saving global input history under
~/.local/share/nvim/fzf-lua-history
:
require('fzf-lua').setup{
fzf_opts = {
['--history'] = vim.fn.stdpath("data") .. '/fzf-lua-history',
},
}
NOTE: the directory must exist, so if you're using a custom folder make sure the directory exists using
mkdir -p
.
Example #2: saving a separate history file for files|grep
under
~/.local/share/nvim/
:
require('fzf-lua').setup{
files = {
fzf_opts = {
['--history'] = vim.fn.stdpath("data") .. '/fzf-lua-files-history',
},
},
grep = {
fzf_opts = {
['--history'] = vim.fn.stdpath("data") .. '/fzf-lua-grep-history',
},
}
}
To replicate Telescope's ctrl-q
behavior:
require('fzf-lua').setup({
grep = {
actions = {
['ctrl-q'] = {
fn = actions.file_edit_or_qf, prefix = 'select-all+'
},
},
},
})
Note: Prefix requires at least fzf 0.53. If you're running Ubuntu 22.04 or similar, you need to install a newer release from github.
Most providers support setting the working directory using the cwd
option:
:lua require("fzf-lua").files({ cwd = "~/dots" })
Some providers (buffers
, oldfiles
) support showing files from the current
directory only using the cwd_only
option.
An example by @gegoune, show all file history when in ~
, otherwise show
current directory only:
:lua require('fzf-lua').oldfiles({
cwd_only = function()
return vim.api.nvim_command('pwd') ~= vim.env.HOME
end
})
Excluding paths can be done by adding flags to the underlying executed command for the provider.
Setting the cmd
option will override the underlying command:
files
: excluding a folder using fd
:
:lua require'fzf-lua'.files({ cmd = 'fd --type f --exclude node_modules' })
files
: excluding a folder using find
:
:lua require'fzf-lua'.files({ cmd = "find -type f -not -path '*/node_modules/*' -printf '%P\n'" })
grep
: excluding a folder using rg
:
:lua require'fzf-lua'.grep({ cmd = "rg --color=always --smart-case -g '!{.git,node_modules}/'" })
Alternatively, you can also modify the default auto-detect options:
files
prioritizesfd
overrg --files
overfind
, default options can be modified by settingfiles.{fd|rg|find}_opts
respectively.grep
prioritizesrg
overgrep
, default options can be modified by settinggrep.{rg|grep|_opts
respectively.-- Note the omission of `fd`: :lua require'fzf-lua'.files({ fd_opts = '--type f --exclude node_modules' })
We can also exclude some more complex regex patterns by either piping a filter
command to files.cmd
or using grep.filter
. The below example excludes .lua
files:
-- use `grep -P` for perl regex syntax
:lua require'fzf-lua'.files({ cmd = "fd --type f | rg -v '\\.lua$'" })
:lua require'fzf-lua'.files({ cmd = "fd --type f | grep -v -P '\\.lua$'" })
However, we cannot do this with grep
because the search string is appended
after the cmd
argument, we use the filter
option to achieve the same
result:
Note the omission of the
$
sign as the regex is performed on the entire line including the matching text so the filename is in not at the end of the line
:lua require'fzf-lua'.live_grep({ filter = "rg -v '\\.lua'", debug=true})
Excluding paths as explained above is the preferred and most performant way as
it reduces the amount of lines fzf-lua needs to process but it won't work on
file based providers where a command is not used (e.g. oldfiles
, lsp_xxx
,
etc).
In addition, sometimes a different approach is needed and you might want to
ignore ceratin file patterns globally, for this we have file_ignore_patterns
,
a simple array of
lua pattern matching
strings which can be set globally, per provider or sent directly in the call
arguments.
Note:
file_ignore_patterns
affects all file based providers:files
,grep|live_grep
,git_files
,git_status
,diagnostics
,lsp_XXX
,args
,oldfiles
,quickfix
,loclist
,dap_breakpoints
As a global setting:
require'fzf-lua'.setup {
-- ignore all '.lua' and '.vim' files
file_ignore_patterns = { "%.lua$", "%.vim$" }
}
As a provider setting:
require'fzf-lua'.setup {
files = {
prompt = "Files> ",
file_ignore_patterns = { "%.lua$", "%.vim$" },
}
}
Directly in a call:
-- ignore all files that start with a '/'
:lua require'fzf-lua'.lsp_live_workspace_symbols({ file_ignore_patterns={ "^/" } })
All of the above can be combined, you can have a global setting, provider setting and call argument all at the same time, all 3 settings will be merged at runtime into a single
file_ignore_patterns
array.
Best explained by reading the commit message and the discussion in #248:
Since LUA is single threaded I reached a limit to performance
optimization, both 'git_icons' and 'file_icons' require string
matching and manipulations which eventually hurt performance
when running on large amount of files.
In order to solve that this commit introduces the option to spawn
commands and process the entries in a separate neovim process which
prints to stdio as if it was a regular shell command. This speeds up
things significantly and also makes the UI super responsive as if fzf
was run in the shell. This required a few lua hacks to be able to load
nvim-web-devicons in a '--headless --clean' instance and sharing the
user configuration through the RPC interface from the running instance.
This is enabled by default for 'files' and 'grep' providers and can also
be enabled for 'git.files' if required, control using the 'multiprocess'
option.
Unless you're having issues I highly recommend keeping the default
multiprocess = true
. Also see the discussion how to debug multiprocess in What's the difference betweengrep
andlive_grep
?
grep
uses rg
(or grep
if rg
is not installed) to search for a pattern,
the results are then fed into fzf
for further refinement using fuzzy search.
live_grep
on the other hand uses fzf
as a UI selector only, the fuzzy
search functionality is disabled and each keystroke generates a new rg
query
with the input prompt text.
Both methods have their pros and cons, in large codebases live_grep
can be
faster at the cost of not having the fuzzy search, it's about using the right
tool for the job.
In order to better understand how this works I recommend experimenting with
both grep
and live_grep
with both multiprocess
and debug
:
:lua require'fzf-lua'.grep({ multiprocess=true, debug=true })
:lua require'fzf-lua'.live_grep({ multiprocess=true, debug=true })
With debug=true
the underlying rg
command will be printed as the first
match so you'll be able to see that with grep
the command never changes, all
results are already indexed inside fzf
and every keystroke shows the fuzzy
mathced results, on the other hand with live_grep
you'll notice that each
keystroke generates a new underlying rg
command. I find this very helpful to
also understand how live_grep_glob
query parsing works, for example you can
see that the typed query now -- *which*
gets translated to searching for the
word now
limited to files which contain the word which
:
Unless a search term is specified, Rg
feeds all lines of the project into fzf,
the equivalent in fzf-lua would be running grep
with an empty search query:
:lua require("fzf-lua").grep({ search = "" })
fzf-lua
comes with a convinience shortcut grep_project
that combines both
the empty string search as well as excluding file names from fuzzy matching:
-- both commands are equal:
:lua require("fzf-lua").grep_project()
:lua require("fzf-lua").grep({ search = "", fzf_opts = { ['--nth'] = '2..' } })
Yes, both grep
and live_grep
can resume the last search:
-- `live_grep` is also supported
:lua require("fzf-lua").grep({ resume = true })
Alternatively you can also use :FzfLua resume
.
By default live_grep
does not run an empty query unless:
:lua require("fzf-lua").live_grep({ exec_empty_query = true })
Both --glob
and --iglob
are supported, the default options are under the grep
provider:
require("fzf-lua").setup({
grep = {
rg_glob = true -- enable glob parsing by default to all
-- grep providers? (default:false)
glob_flag = "--iglob", -- for case sensitive globs use '--glob'
glob_separator = "%s%-%-" -- query separator pattern (lua): ' --'
}
})
Setting rg_glob=true
instructs fzf-lua to parse the search query (regex) and look for the
separator, anything after the separator will be converted to a separate glob flag, few examples:
Search for all lines starting with foo
within *.lua
files:
*Rg> ^foo -- *.lua
Generates the below underlying command:
rg ${rg_opts} --iglob *.lua -e "^foo"
Search for all lines starting with foo
within *.lua
files, exclude *spec*
files:
*Rg> ^foo -- *.lua !*spec*
Generates the below underlying command:
rg ${rg_opts} --iglob *.lua --iglob !*spec* -e "^foo"
Once rg_glob=true
we aren't limited to live_grep
, any search regex is parsed for a separator
and uses the same logic so we can use globs in grep
directly:
:lua require("fzf-lua").grep({ no_esc=true, search="foo -- *.lua" })
Another example, from
issue #167, shows how
the underlying rg
command (below the status line) changes with each keystroke and the generated
glob arguments once the separator is detected:
Using a custom rg_glob_fn
we can build our own live_grep_glob
, one such example would
be sending any argument to rg
similar to
telescope-live-grep-args.nvim:
require("fzf-lua").setup({
grep = {
rg_glob = true,
-- first returned string is the new search query
-- second returned string are (optional) additional rg flags
-- @return string, string?
rg_glob_fn = function(query, opts)
local regex, flags = query:match("^(.-)%s%-%-(.*)$")
-- If no separator is detected will return the original query
return (regex or query), flags
end
}
})
For example, sending the "word boundary" flag:
*Rg> ^foo -- --word-regexp --glob="*.lua"
generates the below underlying command
rg ${rg_opts} --word-regexp --glob="*.lua" -e "^foo"
Similar to the default glob search, once rg_glob
is enabled we can use the new parser directly
from grep
:
:lua require("fzf-lua").grep({ no_esc=true, search="foo -- --word-regexp" })
For other examples of custom rg_glob_fn
see
#373.
Note restricting searches to file globs requires installing
rg
.
Building on the above, either enable rg_glob
to all providers an or use live_grep_glob
and use the default separator --
(space dash dash) to separate the search query from the
file glob specification:
- Open glob enabled live grep by executing
:FzfLua live_grep_glob
- Type your search, separated by the glob_separator, e.g:
Rg> <search_term> -- <glob_spec1> <glob_spec2> ...
More specific examples:
-
Searching for all test files that import the
react-hooks
library assuming test files are named<name>.spec.<ext>
:Rg> @testing-library/react-hooks --*.spec.*
-
Searching for all occurrences of the
Partial
utility in javascript and typescript files:Rg> Partial --*.ts* *.js
For performance reasons the default rg
command does not sort the results, to
sort the results add --sort-files
to the default grep.rg_opts
:
require("fzf-lua").setup({
grep = {
rg_opts = "--sort-files --hidden --column --line-number --no-heading " ..
"--color=always --smart-case -g '!{.git,node_modules}/*'",
}
})
By default all LSP calls are asynchronous (better UI responsiveness), when running async calls fzf-lua can't tell in advance if there will be any results hence when there are no results the window is flashing.
It's possible to prevent the window from flashing by running in sync
mode,
to do so either define lsp.async = false
(globally) or send send async = false
with your LSP call, e.g.:
:lua require("fzf-lua").lsp_code_actions({ async = false })
When running refernces|definitions
the LSP can sometimes return a single
result, if you wish to jump there immediately instead of opening the window
run:
:lua require("fzf-lua").lsp_definitions({ jump_to_single_result = true })
It's also possible to control how the single result action will behave:
:lua require('fzf-lua').lsp_definitions({
sync = true,
jump_to_single_result = true,
jump_to_single_result_action = require('fzf-lua.actions').file_vsplit,
})
When using lsp_references
some users prefer to see "other references" only
and ignore the current line, this is possible via:
:lua require("fzf-lua").lsp_references({ ignore_current_line = true })
lsp_references
can be configured to exclude declaration of current symbol from results:
:lua require("fzf-lua").lsp_references({ includeDeclaration = false })
Combined with ignore_current_line = true
, it achieves "show other usages" behavior.
By default, fuzzy searching will match the entire line which includes
filenames, it is possible to hide certain fields or leave them visible but
exclude them from the fuzzy search using fzf's --nth
and --with-nth
options:
For example when using lsp_document_symbols
matching the filename doesn't
add much value, to disable filename matching:
:lua require'fzf-lua'.lsp_document_symbols({ fzf_cli_args = '--nth 2..' })
To hide the filename altogether use --with-nth
:
:lua require'fzf-lua'.lsp_document_symbols({ fzf_cli_args = '--with-nth 2..' })
man fzf
and search forFIELD INDEX EXPRESSION
for more info
Having to extract extensions from filenames, git status
indicators and
drawing icons all have a performance impact as well as having to deal with
ANSI coloring (fzf's --ansi
option).
If you do not care about any of these and just want maximum speed you need to
disable both file_icons
and git_icons
and disable fzf's --ansi
:
The below does this for files
but it can obviously be done for grep
,
lsp
, etc and all file-based providers:
require("fzf-lua").setup({
winopts = {
preview = { default = 'bat_native' }
},
fzf_opts = { ['--ansi'] = false },
files = {
git_icons = false,
file_icons = false,
}
})
-- Alternatively, can be called be called directly or mapped to a bind
:lua require("fzf-lua").files({fzf_opts = {['--ansi']=false}, file_icons=false, git_icons=false})
In addition it is worth noting that the default previewer (neovim's floating
win aka "builtin") is slower than bat
(or cat
) previewers, to switch to
bat
previewer set winopts.default.preview = 'bat'
or
winopts.default.preview = 'bat_native'
, the difference between the two is
that the former will use neovim as a proxy to the bat
command and
bat_native
will defer the execution of bat
to fzf
(hence "native").
Due to the same reasoning as above live_grep_native
may be a bit faster than
live_grep
as the latter needs to call neovim for each keystroke in order to
save the search term for the continue_last_search
option. live_grep_native
is the closest as possible to a pure shell/terminal version.
Yes! it's possible to preview media files with the builtin
previewer. To do
so you will need to configure previewer.builtin.extensions
with a shell
command per extension, the shell command will then run in a neovim terminal
buffer inside the preview window.
For example, the below configures png
files to open with
viu
and jpg
files to open with
ueberzug
:
require("fzf-lua").setup({
previewers = {
builtin = {
extensions = {
-- neovim terminal only supports `viu` block output
["png"] = { "viu", "-b" },
["jpg"] = { "ueberzug" },
}
-- When using 'ueberzug' we can also control the way images
-- fill the preview area with ueberzug's image scaler, set to:
-- false (no scaling), "crop", "distort", "fit_contain",
-- "contain", "forced_cover", "cover"
-- For more details see:
-- https://github.com/seebye/ueberzug
ueberzug_scaler = "cover",
}
},
})
Below is an example of using ueberzug
with cover
scaling:
Type | Name | Linux | Mac | Comments |
---|---|---|---|---|
Images | viu |
✔️ | ✔️ | |
Images | ueberzugpp |
✔️ | ❌ | Requires X11 |
Images | termpix |
✔️ | ✔️ | |
Images | kitty imgcat |
❌ | ❌ | Does not work inside neovim terminal, see https://github.com/kovidgoyal/kitty/issues/413 |
Images | iTerm2 imgcat |
❌ | ❌ | Does not work inside neovim terminal, see https://github.com/neovim/neovim/issues/4349 |
For more information see
vifmimg
,ranger
Wiki
Type | Name | Linux | Mac |
---|---|---|---|
Videos | ffmpegthumbnailer |
✔️ | ✔️ |
ePub | epub-thumbnailer |
✔️ | ✔️ |
pdftoppm |
✔️ | ✔️ | |
Font | fontpreview |
✔️ | ✔️ |
djvu | ddjvu |
✔️ | ✔️ |