pickers.nvim · Files & navigation · vimdoc

:help pickers

Unified fuzzy-picker plugin for Neovim

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 *pickers-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-intro*

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 *pickers-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 *pickers-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
  resolves dependencies before config() 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-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 via lib.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 *pickers-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 inside repos_dir, lets you pick one, then
  opens the engine picker inside that repo.  Requires repos_dir to be set.

system

  Opens vim.ui.input for a search specification:
    name .ext /path
  Runs fd with 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 *pickers-collections*

Collections are user-defined named scopes configured in setup().  Each
collection automatically becomes:
  • A :Pickers <name> scope (tab-completed alongside built-ins)
  • Compat commands  :{PascalName}Files / :{PascalName}Grep /
    :{PascalName}Smart
  • Optional keymaps (if keys.files / keys.grep / keys.smart are 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 when prefix is set (non-nil).

find (Pickers.FindOpts|nil)

    Per-collection override for the files action, merged over the global
    |pickers-config| find defaults (only the given fields change; grep is
    unaffected — it doesn't use find flags).

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 *pickers-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, via
  Snacks.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_toggle is 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/tab open 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_confirm double-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 or false (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.

  cheatsheet opens 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 and
  open_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 *pickers-compat*

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 *pickers-config*

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 *pickers-history*

File-based picker history, disabled by default. Files live under
stdpath("data")/pickers.nvim/history (override with history.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 (one History object,
  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 of fzf_scope — a
  Telescope architecture limitation, not a choice made here. If you already
  use telescope-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 --history file, 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 own fzf-lua.setup().
    "global" — pickers.nvim doesn't set its own fzf_opts; use
              require("pickers.history").fzf_opts() /
              .telescope_opts() yourself in your own fzf-lua.setup() /
              telescope.setup({defaults={history=...}}) calls. One shared
              history file, same as "patch".
    "patch"  — pickers.nvim calls fzf-lua's setup() itself (deferred via
              vim.schedule, merged non-destructively) so your own direct
              :FzfLua usage also gets the shared history file — no config
              change needed on your end.

11. RESULT COUNT *pickers-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 *pickers-smart*

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
plus find.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 *pickers-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 *pickers-images*

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's display.assume_supported = true
overrides that detection.  :checkhealth pickers names which of the three
states applies, and reports separately whether pages can be rasterized.

The dependency runs one way only: images.nvim exposes
images.integrations.picker (available/is_previewable/preview), pickers.nvim
calls it.  An older images.nvim without is_previewable falls back to
is_image, so the two plugins update in either order.
See docs/FEATURES/IMAGES.md.

15. HEALTH CHECK *pickers-health*

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.