doc/pickers.txt — rendered from the plugin's own vimdoc
*pickers.txt* Unified fuzzy-picker plugin for Neovim *pickers* *pickers.nvim* Author: Stefan Bartl <stefan.bartl.work@gmail.com> Homepage: https://github.com/StefanBartl/pickers.nvim
CONTENTS
1. Introduction ..................... |pickers-intro| 2. Requirements ..................... |pickers-requirements| 3. Setup ............................ |pickers-setup| 4. Command .......................... |pickers-command| 5. Scopes ........................... |pickers-scopes| 6. Collections ...................... |pickers-collections| 7. Keymaps .......................... |pickers-keymaps| 8. Compat commands .................. |pickers-compat| 9. Configuration reference .......... |pickers-config| 10. History .......................... |pickers-history| 11. Result count ..................... |pickers-result-count| 12. Smart action ..................... |pickers-smart| 13. Display .......................... |pickers-display| 14. Image previews ................... |pickers-images| 15. Health check ..................... |pickers-health|
1. INTRODUCTION
pickers.nvim consolidates seven previously separate Neovim picker modules
into one unified plugin:
• find_config — search in the Neovim config directory
• find_in_folder — interactively pick a folder, then search it
• dir_picker — depth-based / alias directory navigation
• repo_pickers — pick a repo, then search it
• grep — live grep in CWD
• search_all_drives — search across all mount points / drive letters
• system_find — systemwide fd-based file search
All scopes share one unified command :Pickers and are backed by a single
engine (telescope.nvim, fzf-lua, or snacks.nvim — auto-detected).
2. REQUIREMENTS
Required: • lib.nvim https://github.com/StefanBartl/lib.nvim One of (auto-detected, telescope preferred, then fzf-lua, then snacks.nvim): • telescope.nvim https://github.com/nvim-telescope/telescope.nvim • fzf-lua https://github.com/ibhagwan/fzf-lua • snacks.nvim https://github.com/folke/snacks.nvim (picker module) Recommended CLI tools: • ripgrep (rg) — live_grep and the smart action (content half) • fd / fdfind — system source, dir-picker, and the smart action (files half) The smart action (see |pickers-smart|) needs BOTH rg and fd. On the fzf-lua engine it additionally needs fzf >= 0.45 (Lua-function live mode); use telescope or snacks on older fzf.
3. SETUP
Minimal (lazy.nvim):
{
"StefanBartl/pickers.nvim",
dependencies = { "StefanBartl/lib.nvim" },
config = function()
require("pickers").setup()
end,
}
A fuller spec -- the complete key list is |pickers-config|:
require("pickers").setup({
engine = "auto", -- "auto" | "telescope" | "fzf" | "snacks"
-- repos_dir already defaults to $REPOS_DIR (via lib.nvim) when set, used
-- by the "repos" scope -- omitted here, set it only to override
collections = {
{ name = "notes", dir = vim.env.REPOS_DIR .. "/Notes",
keys = { files = "<leader>mnf", grep = "<leader>mng" } },
{ name = "journals", dir = vim.env.REPOS_DIR .. "/Journals",
prefix = "journal-",
keys = { files = "<leader>jnf", grep = "<leader>jng" } },
},
depth_aliases = {
work = function() return "/home/user/work" end,
},
keymaps = {
enable = true,
dir_pick = "<leader>dp",
explorer = "<leader>.",
folder_files = "<leader>fb",
config_files = "<leader>fc",
config_grep = "<leader>gc",
cwd_grep = "<leader>li",
cwd_files = nil,
cwd_smart = nil, -- smart (grep + find) in CWD
config_smart = nil, -- smart (grep + find) in nvim config
folder_smart = nil, -- smart (grep + find) in picked folder
},
mappings = {}, -- declarative mappings, empty by default (see |pickers-keymaps|)
usercmds = { enable = true },
smart = { -- combined grep + find (see |pickers-smart|)
weights = { filename = 1.0, content = 1.0, both = 25 },
limit = 2000,
timeout = 3000,
frecency = { enabled = false, weight = 1.0, dir = nil },
dedup_grep_rows = false,
},
history = {
enabled = false, -- off by default
fzf_scope = "plugin", -- "plugin"|"global"|"patch" (fzf-lua only)
dir = nil, -- default: stdpath("data")/pickers.nvim/history
limit = 200,
},
display = { path_shorten = false }, -- cosmetic, off by default (see |pickers-display|)
})
Optional engine ownership + auto-install
By default pickers.nvim only detects whichever engine you already declared/configured yourself. It never calls Snacks.setup() at all (it patches Snacks.config.picker instead). For telescope/fzf-lua it stops short of full ownership too, but not all the way to "never calls setup()": what it patches onto the engines (keys, entry actions, history, find.exclude, display.*, PDF text preview) is applied with ONE telescope.setup()/fzf-lua's setup() call per engine, once it has loaded, deep-merging in rather than replacing your config wholesale (see |pickers-keymaps| and the History section above). To have it install AND configure the engine too, use require("pickers").plugin_spec() from your OWN plugin list, at spec-build time (NOT from setup() -- lazy.nvim resolvesdependenciesbeforeconfig()runs, so the engine choice must be known earlier than setup() fires):
require("lazy").setup({
require("pickers").plugin_spec({
engine = "snacks", -- "telescope"|"fzf"|"snacks" ("auto" unsupported here)
own_engine = true, -- opt-in; default false (unchanged behaviour)
engine_opts = {}, -- passed to the engine's own setup()
picker_opts = {}, -- passed to pickers.setup() (engine= filled in for you)
}),
-- ...your other plugins
})
Returns a list of ready lazy spec entries -- splat it into your own list.
own_engine=true requires an explicit engine ("auto" has no single engine
to install) and errors immediately if omitted.
Note: setup() is optional. The :Pickers command is always registered
automatically by plugin/pickers.lua, built-in scopes included. Collection
scopes (see |pickers-collections|) only appear in :Pickers <Tab> and as
literal subcommands once setup() runs, or — if you never call it — at the
VimEnter fallback. Call setup() to change defaults, add collections, or
toggle keymaps/usercmds.
4. COMMAND
*:Pickers*
:Pickers [{scope} [{action}]]
:Pickers [{scope} files all]
:Pickers dir [{nav} [{action}]]
scope One of: cwd config folder repos system drives dir
or any user-defined collection name (see |pickers-collections|)
action One of: files grep smart (smart: see |pickers-smart|)
nav (dir only) alias name, integer depth, or path=<dir>
A trailing "all" after "files" is the "find all" escape hatch: forces
hidden+no_ignore+follow for this one search only, regardless of configured
find.* defaults. Works for every built-in scope and collection, not dir.
No-op on grep/smart (silently ignored, live grep already searches
--hidden --no-ignore-vcs unconditionally).
When an argument is omitted an interactive picker (hover_select or
vim.ui.select) prompts the user.
Examples:
:Pickers " scope picker → action picker
:Pickers cwd " action picker for CWD
:Pickers cwd files " find files in CWD
:Pickers cwd smart " grep + find in CWD, merged and ranked
:Pickers cwd files all " find files in CWD, forcing hidden+no_ignore+follow
:Pickers config grep " live grep in nvim config
:Pickers dir " dir-nav picker → action picker
:Pickers dir 2 " go 2 dirs up → action picker
:Pickers dir git files " git repo root → find files
:Pickers dir path=/tmp grep " explicit path → live grep
:Pickers repos files " pick repo → find files
:Pickers drives grep " live grep across all drives
:Pickers system files " systemwide fd search (prompts for query)
:Pickers notes files " find files in 'notes' collection
:Pickers journals grep " pick journal subdir → live grep (prefix-filtered collection)
Tab-completion is supported for all arguments. Built vialib.nvim.bindings.usercmd.composer— a route tree drives dispatch,<Tab>completion, and this doc's own command list from one source. An unknown {scope} now reports composer's own "unknown subcommand" usage block (every registered scope, one per line) instead of a plain error string.
5. SCOPES
cwd
Search root: vim.uv.cwd().
config
Search root: vim.fn.stdpath("config").
folder
Opens an engine directory picker so you can interactively choose a folder, then searches inside it.
repos
Lists all git repositories insiderepos_dir, lets you pick one, then opens the engine picker inside that repo. Requiresrepos_dirto be set.
system
Opensvim.ui.inputfor a search specification: name .ext /path Runsfdwith the given arguments. Requires fd or fdfind in PATH.
drives
Discovers all mount points / drive letters:
• Windows: PowerShell Get-PSDrive (A-Z fallback)
• WSL: /mnt/* scan
• POSIX: df -P --output=target
Roots are session-cached after first discovery.
dir
Navigation argument forms:
<number> go N directories above cwd (1 = parent, 2 = grandparent…)
git git repository root of cwd
home OS home directory
cwd current working directory
root filesystem root above cwd
<alias> any name registered in depth_aliases
path=<dir> explicit path; ~ / %VAR% / $VAR expanded
6. COLLECTIONS
Collections are user-defined named scopes configured insetup(). Each collection automatically becomes: • A:Pickers <name>scope (tab-completed alongside built-ins) • Compat commands:{PascalName}Files/:{PascalName}Grep/:{PascalName}Smart• Optional keymaps (ifkeys.files/keys.grep/keys.smartare set) Configuration:
collections = {
-- Direct root (nil prefix): dir is used as-is
{ name = "notes",
dir = vim.env.REPOS_DIR .. "/Notes",
keys = { files = "<leader>mnf", grep = "<leader>mng" } },
-- Prefix-filtered subdirs: show only dirs starting with "journal-"
{ name = "journals",
dir = vim.env.REPOS_DIR .. "/Journals",
prefix = "journal-",
keys = { files = "<leader>jnf", grep = "<leader>jng" } },
-- All subdirs (empty string): list every immediate subdir
{ name = "projects", dir = "/home/user/projects", prefix = "" },
-- Git repos only (only_git = true)
{ name = "myrepos", dir = "/home/user/src", prefix = "", only_git = true },
}
Collection fields:
name (string, required)
Unique scope identifier. Used verbatim in :Pickers <name>.
dir (string, required)
Absolute path to the collection root.
prefix (string|nil)
nil — use dir directly as the search root
"" — list all immediate subdirs; user picks one interactively
"xyz-" — list only subdirs whose name starts with "xyz-"
keys ({ files?: string, grep?: string, smart?: string }|nil)
Optional normal-mode keymaps registered at startup.
only_git (boolean|nil)
When true, only subdirs that contain a.git/directory are shown. Effective only whenprefixis set (non-nil).
find (Pickers.FindOpts|nil)
Per-collection override for thefilesaction, merged over the global |pickers-config|finddefaults (only the given fields change; grep is unaffected — it doesn't usefindflags).
Auto-generated compat commands
For a collection name = "notes_lua" the following commands are created
automatically:
:NotesLuaFiles → :Pickers notes_lua files
:NotesLuaGrep → :Pickers notes_lua grep
:NotesLuaSmart → :Pickers notes_lua smart
The name is converted to PascalCase: underscores are removed and the
following letter is uppercased.
7. KEYMAPS
All keymaps are registered in lua/pickers/bindings/. They mirror the
keymaps from the original individual modules exactly:
<leader>dp :Pickers dir (dir navigation picker; a count is the
depth, so 2<leader>dp is two levels up)
<leader>. :Pickers builtin explorer
(file explorer / browser, active engine)
<leader>fb :Pickers folder files (find in interactively picked folder)
<leader>fc :Pickers config files (find files in nvim config)
<leader>gc :Pickers config grep (grep in nvim config)
<leader>li :Pickers cwd grep (live grep in CWD)
The following are opt-in (nil, disabled by default):
cwd_files :Pickers cwd files (find files in CWD)
repos_files :Pickers repos files (pick a repo, then find files)
repos_grep :Pickers repos grep (pick a repo, then live grep)
system_files :Pickers system files (systemwide fd search, prompts)
cwd_smart :Pickers cwd smart (grep + find in CWD; see |pickers-smart|)
config_smart :Pickers config smart (grep + find in nvim config)
folder_smart :Pickers folder smart (pick folder, then grep + find)
cwd_find_all :Pickers cwd files all ("find all" escape hatch: forces
hidden+no_ignore+follow for one search)
Disable all keymaps:
require("pickers").setup({ keymaps = { enable = false } })
Change a keymap:
require("pickers").setup({ keymaps = { cwd_grep = "<leader>sg" } })
Declarative mappings (per-entry engine override)
mappings is a second, more flexible keymap surface alongside the fixed
keymaps.* fields -- any scope×action combo or any |pickers-command|
builtin name, each with an lhs and an OPTIONAL per-entry engine override.
Does not replace keymaps.*.
require("pickers").setup({
mappings = {
cwd_files = { "<leader>ff", "telescope" }, -- always telescope
cwd_grep = { "<leader>gr" }, -- active/default engine
explorer = { "<leader>.", "snacks" }, -- always snacks
},
})
Name resolution:
<builtin name> -> pickers.builtins.run(name)
<scope>_files | <scope>_grep | <scope>_smart -> :Pickers <scope> <action>
<scope>_find_all -> :Pickers <scope> files all
<scope> is any built-in scope or a collection name (the LAST
_files/_grep/_smart/_find_all suffix is stripped, so scope names may
contain underscores, e.g. notes_lua_grep -> collection "notes_lua",
action "grep"). dir is NOT supported (same limitation as the "find
all" escape hatch).
The optional 2nd element pins that entry to a specific engine
("telescope"|"fzf"|"snacks") regardless of the configured default. An
engine named but not installed falls back to the default engine, never a
dead keymap. An unresolvable name or malformed entry is skipped with a
warning, never a throw.
In-picker keys (preview scroll + history + entry actions)
Separate from the keymaps above, keys controls bindings that act inside
an open picker — one config surface for everything in this category,
defined once and translated per engine. See |pickers-config| and
lua/pickers/keys/.
require("pickers").setup({
keys = {
preview_scroll_down = { "<PageDown>", "<C-d>" }, -- two bindings
history_back = false, -- unbind
},
})
telescope and fzf-lua are patched globally (defaults.mappings/keymap.builtin) — every picker they open, pickers.nvim's own and native builtins alike, inherits the keys. snacks is patched too, viaSnacks.config.picker(read live on every picker open).create_file/open_background/cheatsheet/the path-copy and system actions (the in-picker entry actions) are installed the same way by pickers.entry_actions.patch; a key or action you already bound always wins. keys.snacks_win() and the adapters' get_*() stay exported for merging by hand.preview_toggleis opt-in (false/unbound by default) and telescope-only: fzf-lua already binds toggle-preview on <F4>, snacks on <A-p>, both natively — neither needs pickers.nvim to provide one. Telescope ships the underlying action (actions.layout.toggle_preview) but binds no key to it by default. Unlike create_file/open_background, it IS patched globally (a plain built-in telescope action):
require("pickers").setup({ keys = { preview_toggle = "<M-p>" } })
split/vsplit/tabopen the selected entry in a horizontal/vertical split or a new tab, default <C-s>/<C-v>/<C-t> across all three engines. All three engines already ship the primitive natively (telescope actions.select_horizontal/select_vertical/select_tab, snacks actions.split/vsplit/tab, fzf-lua's fixed ctrl-s/ctrl-v/ctrl-t) — this is pure translation-table wiring like preview_toggle, no pickers.nvim-side logic. fzf-lua's keys are fixed/unremappable and left unpatched (not a capability gap, fzf already ships them):
require("pickers").setup({
keys = { split = "<C-x>", vsplit = false }, -- rebind / unbind
})
mouse_confirmdouble-clicks a result open, same as <CR>, default <2-LeftMouse>. Telescope has no default mouse mapping at all — this is the actual gap it closes there (actions.select_default, patched into mappings.n, results-window/normal-mode only). Snacks already ships <2-LeftMouse> = "confirm" as its own default; it is translated here too so a custom lhs orfalse(unbind) is still honored by|pickers.keys|.snacks_win(). fzf-lua's own fzf binary handles mouse clicks itself, outside keymap.builtin — same capability-gap class as its history keys.cheatsheetopens a read-only panel (pickers.cheatsheet) listing every currently-bound key in this section, grouped, built from|pickers.keys|.resolve() so a remapped or unbound key shows up as what it actually is. The two keys worth knowing first — the cheatsheet itself andopen_background(<S-CR>, "add to the buffer list, no focus switch") — lead the panel under "Essentials". Default <C-/> and <M-?> — NOT <C-?>: every picker prompt starts in insert mode, where a raw "?" just searches for a literal question mark, and Neovim resolves <C-?> to the same byte (0x7F/DEL) that Backspace sends in many terminals, which would open the cheatsheet on every backspace instead. Like create_file/open_background it runs pickers.nvim logic and is patched in by pickers.entry_actions.patch. fzf-lua's binding is fixed to f1, same class as its ctrl-a/ctrl-o/ shift-enter:
require("pickers").setup({ keys = { cheatsheet = { "<C-/>", "<M-?>" } } })
Those two keys are also the legend, visible without pressing anything: the
telescope results_title, the fzf-lua --header and the snacks picker title
show "<C-/> cheatsheet, <S-CR> add to buffers" ("f1 cheatsheet,
shift-enter add to buffers" on fzf-lua) the moment the picker opens; each
half drops out when its action is unbound. On snacks the legend is
appended to the title, which otherwise only composes from a template plus
the live {flags} toggle badges (follow/hidden/ignored/modified booleans,
e.g. the "f"/"h" badges visible by default since pickers.nvim's own
find.hidden/find.follow default to true — nothing to do with a typed
query). Snacks additionally has "?" (input or list window, normal mode)
for its own native keymap help (Snacks.win:toggle_help(), bound by
default) — it reads real buffer keymaps, pickers.nvim's own included, and
pickers.entry_actions.adapters.snacks feeds it a matching desc for every
entry action.
copy_absolute/copy_dirname/copy_env_rooted/copy_project_root/
copy_project_relative/copy_buffer_relative/markdown_link copy the
selected entries' paths in various formats to the "+"/unnamed registers,
and open_system/reveal_in_manager hand the current entry to the OS
(default application / file manager) — the curated subset of
filetree.nvim's path-copy, markdown-link, copy-file-list and system
features that still makes sense on a picker RESULT ROW (a plain path
string, not a FiletreeNode). filetree.nvim's "marks if any, else the node
under the cursor" maps onto the picker's multi-selection: with <Tab>-
selected entries every copy takes all of them, one line each (that is [f
and MM); otherwise just the current entry. Deliberately not ported:
trash, and the recursive Markdown-link variant (MR — a result row is one
file, not a directory subtree). filetree.nvim's gb ("add to buffer
list") is not duplicated either — open_background above already is that
action here. Unlike create_file/open_background/cheatsheet, the copies do
NOT close the picker on telescope/snacks (filetree.nvim's own path-copy is
non-disruptive); fzf-lua closes+resumes regardless (its action table
always closes the running process first), approximating the same effect.
Every prompt is in insert mode, where "[", "a", "M", "L" are just
characters of the query — filetree.nvim's chords can never fire there. So
each action carries a DIRECT key (Ctrl/Alt: <C-y> <M-y> <M-v> <M-t> <M-e>
<M-j> <M-l> <M-o> <M-x>), bound in insert AND normal mode, plus the
filetree chords ([a [f ]a [e [R ]R ]b ML MM <leader>sm <leader>fm), bound
in NORMAL MODE ONLY so they never swallow typed characters
(|pickers.keys|.modes_for decides per lhs: one non-printing key press is
direct, anything else is a chord). fzf's own --bind syntax has no
concept of a multi-keystroke chord (a single logical key, not a
pending-key state machine), so its bindings are fixed to the same single
physical keys the direct lhs resolve to: ctrl-y/alt-y/alt-v/alt-t/alt-e/
alt-j/alt-l/alt-o/alt-x, same class as its ctrl-a/ctrl-o/shift-enter/f1.
copy_env_rooted folds $REPOS_DIR back into the path (reading
pickers.config's already-resolved repos_dir) and falls back to the plain
absolute path when unset or the entry is outside it:
require("pickers").setup({
keys = { copy_absolute = { "<C-y>", "[a" }, open_system = false },
})
8. COMPAT COMMANDS
These commands are registered alongside :Pickers for backwards compatibility
with configs that used the original individual modules:
:DirPicker [nav] → :Pickers dir [nav]
:FindConfig → :Pickers config files
:GrepConfig → :Pickers config grep
:FindInFolder → :Pickers folder files
:LiveGrep → :Pickers cwd grep
:AllDrives → :Pickers drives files
:AllDrivesGrep → :Pickers drives grep
:FindOnSystem → :Pickers system files
:RepoFiles [repo] → :Pickers repos files (or files in [repo] directly)
:RepoGrep [repo] → :Pickers repos grep (or grep in [repo] directly)
[repo] tab-completes from REPOS_DIR and, when given, skips the repo picker and
jumps straight into files/grep for that repo.
Every collection additionally gets :{PascalName}Files / Grep / Smart — see
|pickers-collections|.
*:PickersRepeat*
:PickersRepeat
Reopens the most recently dispatched :Pickers action — same resolved
scope/root, same action (files/grep/smart) — without re-resolving through
any interactive sub-picker (folder/repo/collection subdir) in between.
Covers every scope, including dir. In-memory only, current session;
warns if nothing has been dispatched yet. See lua/pickers/last.lua.
*:PickersScopes*
:PickersScopes
Lists every scope :Pickers can resolve — built-in scopes (with a one-line
description) plus every user-defined collection (with its root directory)
— via notify.info, without opening the interactive scope picker.
*:PickersResume*
:PickersResume
Reopens the last picker with its last query — the engine's own native
resume/history-of-open-pickers feature, via :Pickers builtin resume.
Not the same as :PickersRepeat: this resumes the *engine's* last picker
session (including the prompt text); :PickersRepeat replays pickers.nvim's
own last resolved scope/action from scratch, with an empty prompt.
fzf-lua has no resume concept — documented no-op notify.warn there.
9. CONFIGURATION REFERENCE
All keys are optional. Unset keys retain their default values.
engine (string, default "auto")
"auto" detect: telescope → fzf → snacks
"telescope" always use telescope.nvim
"fzf" always use fzf-lua
"snacks" always use snacks.nvim (picker module)
deps_popup (bool, default true)
Show the one-time "which CLI tools does this plugin want, and why" popup
on the first setup() after install, built from docs/install.json via
lib.nvim's deps module. Set false here to silence it for pickers.nvim
only, without touching any vim.g. :Lib deps show pickers.nvim repeats
the same report on demand.
repos_dir (string|nil, default $REPOS_DIR)
Root directory that contains git repositories. Used by the repos scope.
collections (Pickers.Collection[], default {})
User-defined named scopes. See |pickers-collections| for the full field reference and auto-generated compat commands.
depth_aliases (table<string, fun():string>)
Map alias name → function that returns an absolute path. Merged with the
built-in aliases (cwd, home, root, git).
find (table)
File-listing flags for the built-in file pickers (config/cwd/folder/repos/
collections). The system scope is unaffected (it builds its own fd
command). Honoured by telescope, fzf-lua, and snacks.nvim.
hidden (bool, default true) — show dotfiles / hidden entries
no_ignore (bool, default false) — ignore .gitignore/.ignore rules
follow (bool, default true) — follow symlinks
exclude (string[]|nil) — extra globs to skip (e.g. node_modules);
applied to BOTH the file listing and
live grep (as rg -g '!<glob>'), and to
the smart action's fd/rg calls
keymaps (table)
enable (bool, default true) — set false to disable all keymaps
dir_pick (string|nil) — default "<leader>dp"
explorer (string|nil) — default "<leader>.", the file
explorer on the active engine
folder_files (string|nil) — default "<leader>fb"
config_files (string|nil) — default "<leader>fc"
config_grep (string|nil) — default "<leader>gc"
cwd_grep (string|nil) — default "<leader>li"
cwd_files (string|nil) — default nil (disabled)
repos_files (string|nil) — default nil (disabled)
repos_grep (string|nil) — default nil (disabled)
system_files (string|nil) — default nil (disabled)
cwd_smart (string|nil) — default nil (disabled), see |pickers-smart|
config_smart (string|nil) — default nil (disabled)
folder_smart (string|nil) — default nil (disabled)
cwd_find_all (string|nil) — default nil (disabled) — "find all"
escape hatch, forces
hidden+no_ignore+follow for one search
mappings (table, default {})
Declarative mappings: table<name, {lhs, engine?}>, empty by default.
A second, more flexible keymap surface alongside keymaps.* above. See
"Declarative mappings" under |pickers-keymaps|.
usercmds (table)
enable (bool, default true)
history (table)
enabled (bool, default false) — master toggle, see |pickers-history|
fzf_scope (string, default "plugin") — "plugin"|"global"|"patch", fzf-lua only
dir (string|nil, default nil) — override history dir
limit (integer, default 200) — max entries kept per history file
smart (table)
Weights, limit, timeout, frecency and dedup_grep_rows for the combined
grep + find action. Listed in full under |pickers-smart|.
result_count (table)
enabled (bool, default false) — master toggle, telescope-only, see
|pickers-result-count|
display (table)
path_shorten (bool, default false) — cosmetic long-path shortening, see
|pickers-display|
images (table)
enabled (bool, default true) — draw image entries as pictures in the
preview window, see |pickers-images|
keys (table)
Unified in-picker keys: preview scroll + history navigation (patched
globally into telescope/fzf-lua/snacks) plus the create_file/
open_background/cheatsheet entry actions (merged manually into your own
engine setup() — see lua/pickers/entry_actions/README.md). See |pickers-keymaps|.
enable (bool, default true) — master switch
preview_scroll_down (string|string[]|false, default "<PageDown>")
preview_scroll_up (string|string[]|false, default "<PageUp>")
preview_scroll_left (string|string[]|false, default "<C-Left>")
preview_scroll_right (string|string[]|false, default "<C-Right>")
history_back (string|string[]|false, default "<C-p>")
history_forward (string|string[]|false, default "<C-n>")
create_file (string|string[]|false, default "<C-a>")
open_background (string|string[]|false, default { "<S-CR>", "<C-o>" })
preview_toggle (string|string[]|false, default false) — opt-in,
telescope-only (fzf-lua/snacks ship this natively)
split (string|string[]|false, default "<C-s>") — open in
horizontal split
vsplit (string|string[]|false, default "<C-v>") — open in
vertical split
tab (string|string[]|false, default "<C-t>") — open in
new tab
mouse_confirm (string|string[]|false, default "<2-LeftMouse>")
— double-click a result to open it
cheatsheet (string|string[]|false, default { "<C-/>",
"<M-?>" }) — show the in-picker keymap
cheatsheet; fixed to f1 on fzf-lua
copy_absolute (string|string[]|false, default { "<C-y>", "[a",
"[f" }) — copy the selected entries' absolute
paths; fixed to ctrl-y on fzf-lua
copy_dirname (string|string[]|false, default { "<M-y>", "]a" })
— copy the parent directory (absolute); fixed
to alt-y on fzf-lua
copy_env_rooted (string|string[]|false, default { "<M-v>", "[e" })
— copy the path with $REPOS_DIR folded in;
fixed to alt-v on fzf-lua
copy_project_root (string|string[]|false, default { "<M-t>", "[R" })
— copy the absolute project root (.git); fixed
to alt-t on fzf-lua
copy_project_relative (string|string[]|false, default { "<M-e>",
"]R" }) — copy the path relative to the
project root; fixed to alt-e on fzf-lua
copy_buffer_relative (string|string[]|false, default { "<M-j>", "]b" })
— copy the path relative to the open buffer;
fixed to alt-j on fzf-lua
markdown_link (string|string[]|false, default { "<M-l>", "ML",
"MM" }) — copy as Markdown link(s); fixed to
alt-l on fzf-lua
markdown_link_insert (string|string[]|false, default { "<M-n>", "MI" })
— INSERT the entries as Markdown links into the
window behind the picker (closes it, cursor into
the first link, insert mode); fixed to alt-n on
fzf-lua. Tuned by link_insert (path =
"buffer"|"cwd"|"absolute"|"env", cursor = {...})
open_system (string|string[]|false, default { "<M-o>",
"<leader>sm" }) — open the current entry with
the system default application; fixed to alt-o
on fzf-lua
reveal_in_manager (string|string[]|false, default { "<M-x>",
"<leader>fm" }) — reveal it in the system file
manager; fixed to alt-x on fzf-lua
fzf-lua only binds the vertical preview scroll and the fixed ctrl-a/
ctrl-o/shift-enter/f1 and ctrl-y/alt-y/alt-v/alt-t/alt-e/alt-j/alt-l/
alt-o/alt-x entry actions —
horizontal scroll, history, and remapping the entry-action keys are all
fzf-native/fixed there.
10. HISTORY
File-based picker history, disabled by default. Files live understdpath("data")/pickers.nvim/history(override withhistory.dir). Enable:
require("pickers").setup({
history = {
enabled = true,
fzf_scope = "plugin", -- "plugin"|"global"|"patch"
limit = 200,
},
})
Telescope has no scope knob
Telescope's history is a process-wide singleton (oneHistoryobject, created on first use and reused for the rest of the session by every telescope picker, not just pickers.nvim's — see|telescope.defaults.history|upstream). There is no per-call override, so enabling history for telescope always behaves like a global default regardless offzf_scope— a Telescope architecture limitation, not a choice made here. If you already usetelescope-smart-history(sqlite-backed, scoped by picker+cwd), keep managing that yourself instead of enabling history here for telescope — this feature is plain file-based, with no sqlite involvement.
fzf_scope (fzf-lua only)
Each fzf-lua provider call can carry its own--historyfile, so this knob is meaningful there: "plugin" (default) — separate history files per provider (files/grep/ item), set only on pickers.nvim's own fzf-lua calls. Doesn't touch your ownfzf-lua.setup(). "global" — pickers.nvim doesn't set its ownfzf_opts; userequire("pickers.history").fzf_opts()/.telescope_opts()yourself in your ownfzf-lua.setup()/telescope.setup({defaults={history=...}})calls. One shared history file, same as "patch". "patch" — pickers.nvim calls fzf-lua'ssetup()itself (deferred viavim.schedule, merged non-destructively) so your own direct:FzfLuausage also gets the shared history file — no config change needed on your end.
11. RESULT COUNT
Shows the live result count in the prompt window's title, e.g. "Find Files (128)". Telescope-only — fzf-lua and snacks.nvim both already show a position/total counter natively, so this has no effect there and is skipped. Disabled by default.
require("pickers").setup({
result_count = {
enabled = true,
},
})
Updates by polling the entry manager every 150ms while the results buffer is open (not event-driven) — result counts can change asynchronously as a live finder (e.g. live_grep) streams in matches, with no CursorMoved or TextChanged event to hang the update off of.
12. SMART ACTION
The smart action runs rg (content) AND fd (filenames) for the same live query and merges both result sets into ONE list ranked by relevance — a filename hit and a content hit interleave by score instead of appearing as two separate blocks. A file matched by name that also contains matches floats to the top. Open it like any other action, for every scope and collection:
:Pickers cwd smart
:Pickers config smart
:Pickers dir git smart
:Pickers notes smart " collection
:NotesSmart " collection compat command
An empty prompt behaves like a file picker (files only, no grep); results fill in as you type. Selecting a grep row opens the file at the matched line; selecting a file row opens it at the top. All three engines drive the same core (lua/pickers/smart/), so the ranking is identical regardless of engine: snacks via a synchronous live finder (order preserved with sort_empty=false), telescope via new_dynamic + sorters.empty(), fzf-lua via Lua-function live mode (needs fzf >= 0.45). The files half honours |pickers-config|find(hidden/no_ignore/follow/ exclude); the grep half always searches --hidden --no-ignore-vcs --smart-case plusfind.exclude, exactly like live_grep. Configuration (all optional):
require("pickers").setup({
smart = {
weights = {
filename = 1.0, -- multiplier for filename-match component (fd hits)
content = 1.0, -- multiplier for content-match component (rg hits)
both = 25, -- flat bonus for a file that ALSO has grep hits
},
limit = 2000, -- max merged results kept after ranking
timeout = 3000, -- per-command (rg/fd) wait timeout in ms
frecency = { -- opt-in recency/frequency ranking boost, off by default
enabled = false,
weight = 1.0, -- multiplier applied to the raw frecency score
dir = nil, -- default: stdpath("data") .. "/pickers.nvim"
},
dedup_grep_rows = false, -- collapse multiple grep hits per file to the best line
},
})
Tuning:
• Favour filenames — raise weights.filename or lower weights.content.
• Favour content — raise weights.content.
• weights.both floats a name+content match above lone hits of either kind;
set 0 to disable that boost.
• limit caps the merged list after ranking (lower it on huge trees).
• timeout bounds each per-keystroke rg/fd call.
• frecency.enabled boosts files you've opened before (BufReadPost-tracked,
persisted as JSON under stdpath("data")/pickers.nvim/frecency.json by
default) so a frequently/recently edited file outranks an equal-scoring
stranger. Tune overall strength with frecency.weight.
• dedup_grep_rows collapses multiple grep hits for the same file to its
single best-scoring line (denser list, one row per file); off by
default. Other matches for that file are dropped, not merged.
Opt-in keymaps: cwd_smart, config_smart, folder_smart (nil by default; see
|pickers-keymaps|). Per-collection: keys.smart, plus a :{PascalName}Smart
compat command (see |pickers-collections|).
13. DISPLAY
Cosmetic only, disabled by default. display.path_shorten = true visually
shortens long paths in the results list, via each engine's own native
mechanism -- no pickers.nvim-side logic:
require("pickers").setup({
display = { path_shorten = true },
})
telescope path_display = { "shorten" } passed to find_files/live_grep
fzf-lua path_shorten = true passed to files/live_grep
snacks no-op -- already truncates to fit the available column width
by default, nothing to opt into
14. IMAGE PREVIEWS
An entry whose file is an image (.png/.jpg/... -- whatever images.nvim's own
extensions lists) is drawn as a picture in the preview window instead of
being previewed as bytes. Needs images.nvim
(https://github.com/StefanBartl/images.nvim); on by default, and inert
without it:
require("pickers").setup({
images = { enabled = true }, -- false keeps the engine's text preview
})
snacks pick_files, smart and pick_item draw image and PDF entries;
everything else falls through to snacks' own preview.file
telescope pick_files and pick_item draw image and PDF entries; everything
else goes to telescope's own buffer previewer. smart keeps
grep_previewer -- a grep row has to jump to its matched line.
fzf-lua no-op -- its builtin previewer has no per-call Lua hook and
ships image support of its own (previewers.builtin.extensions
= chafa/viu/ueberzug)
PDF entries *pickers-images-pdf*
A .pdf entry previews as its FIRST PAGE, on the same switch, when
pdfport.nvim (https://github.com/StefanBartl/pdfport.nvim) and poppler's
pdftoppm are installed alongside images.nvim. Nothing in pickers.nvim reads
a PDF: images.nvim rasterizes the page and draws it like any other picture,
and without a rasterizer the entry simply stays the engine's to preview.
Which page and at what resolution is images.nvim's own setting:
require("images").setup({
pdf = { enabled = true, page = 1, dpi = 120 },
})
The first sight of a page costs a pdftoppm run (~250 ms for A4), during which the preview window says "rendering the page..." -- a line taken back out the tick before the page is drawn, since the picture covers only its own box and anything left beside it would stay on screen. Afterwards the page is cached on disk by images.nvim and the draw is immediate. A page that will not rasterize falls back to the engine's own text preview. Enabled is not the same as active: images.nvim must be installed AND report that this terminal can draw. On a terminal it does not recognise the answer is no and the text preview stays -- an empty preview window would be worse than the one it replaced. images.nvim'sdisplay.assume_supported = trueoverrides that detection.:checkhealth pickersnames which of the three states applies, and reports separately whether pages can be rasterized. The dependency runs one way only: images.nvim exposesimages.integrations.picker(available/is_previewable/preview), pickers.nvim calls it. An older images.nvim withoutis_previewablefalls back tois_image, so the two plugins update in either order. See docs/FEATURES/IMAGES.md.
15. HEALTH CHECK
Run:
:checkhealth pickers
Checks: lib.nvim availability, telescope / fzf-lua / snacks.nvim presence, rg / fd in PATH, repos_dir existence, registered aliases count, image previews (|pickers-images|), and each collection directory.