diff.nvim · Debug & inspect · vimdoc

:help diff

Flexible diffing for Neovim

doc/diff.txt — rendered from the plugin's own vimdoc

*diff.txt*  Flexible diffing for Neovim                              *diff.nvim*

Author:   Stefan Bartl
Version:  0.1.0

CONTENTS *diff-contents*

  1. Introduction .............. |diff-intro|
  2. Requirements .............. |diff-requirements|
  3. Installation .............. |diff-installation|
  4. Configuration ............. |diff-config|
     4.1 Picker resolution ..... |diff-picker-resolution|
     4.2 Exit scope ............ |diff-exit-scope|
  5. Commands .................. |diff-commands|
     5.1 :Diff ................. |:Diff|
         URL sources ........... |diff-url-sources|
         Three-way diff ........ |diff-three-way|
     5.2 :DiffClear ............ |:DiffClear|
     5.3 :DiffBuffers .......... |:DiffBuffers|
     5.4 :DiffOrig ............. |:DiffOrig|
     5.5 :DiffExit ............. |:DiffExit|
     5.6 :DiffHistory .......... |:DiffHistory|
  6. Tab completion ............ |diff-completion|
  7. Lua API ................... |diff-api|
  8. Health check .............. |diff-health|
  9. Architecture .............. |diff-architecture|

1. INTRODUCTION *diff-intro*

diff.nvim provides a single flexible :Diff command that compares arbitrary
sources — the current buffer, a file, a buffer number, or the system
clipboard — and delivers the result in several ways: a side-by-side split, an
inline unified buffer, the message prompt, a temp file, or the clipboard.

Cross-platform (Windows + Unix). All diffing goes through vim.diff (libvim)
— no shell commands. git:<rev> sources use vim.system to call git directly
(still no shell); http(s):// sources fetch asynchronously via curl, same
way — see |diff-url-sources|. Notifications go through lib.nvim, the only
dependency.

2. REQUIREMENTS *diff-requirements*

  - Neovim 0.9 or later (0.10+ for git:<rev> and http(s):// sources/targets)
  - lib.nvim (used for notifications)
  - Optional: a git executable on PATH for git:<rev> sources/targets
  - Optional: a curl executable on PATH for http(s):// sources/targets

3. INSTALLATION *diff-installation*

lazy.nvim:
  {
    "StefanBartl/diff.nvim",
    cmd  = { "Diff", "DiffClear", "DiffBuffers", "DiffOrig", "DiffHistory", "DiffExit" },
    opts = {},
  }
packer.nvim:
  use {
    "StefanBartl/diff.nvim",
    cmd = { "Diff", "DiffClear", "DiffBuffers", "DiffOrig", "DiffHistory", "DiffExit" },
    config = function()
      require("diff").setup({})
    end,
  }
vim-plug:
  Plug 'StefanBartl/diff.nvim'
  Then, in an init.lua sourced later:
  require("diff").setup({})

4. CONFIGURATION *diff-config*

  require("diff").setup({
    features = {
      diff         = true,   -- register :Diff / :DiffClear / :DiffBuffers
      diff_origin  = true,   -- register :DiffOrig
      diff_history = true,   -- register :DiffHistory
      diff_exit    = true,   -- register :DiffExit + exit keymap
    },
    diff = {
      default_view      = "vsplit",    -- "vsplit"|"split"|"tab"|"inline"|"float"
      default_output    = "buffer",    -- "buffer"|"prompt"|"file"|"clipboard"|"stat"
      default_source    = "current",   -- "current"|"clipboard"|"ask"|"git:<rev>"|"http(s)://…"|path|bufnr
      default_orig_view = "vsplit",    -- "vsplit"|"split" — :DiffOrig split direction
      algorithm         = "histogram", -- vim.diff algorithm
      ctxlen            = 3,           -- context lines per hunk
      word_diff         = true,        -- word/char DiffText highlighting in view=inline/float
      url_timeout_ms    = 10000,       -- fetch timeout for http(s):// sources/targets
      image_compare     = true,        -- show two raster-image paths via images.nvim
    },
    exit = {
      key             = "<Esc><Esc>", -- exit mapping
      scope           = "buffer",     -- "buffer"|"global"|false
      native_diffthis = false,        -- also mirror the key onto native :diffthis buffers
    },
    commands = {
      diff         = "Diff",
      diff_clear   = "DiffClear",
      diff_buffers = "DiffBuffers",
      diff_orig    = "DiffOrig",
      diff_history = "DiffHistory",
      diff_exit    = "DiffExit",
    },
    select_fn        = nil,         -- optional vim.ui.select replacement
    use_pickers_nvim = true,        -- auto-detect pickers.nvim as the picker engine
  })

features.diff

  Register the |:Diff|, |:DiffClear|, and |:DiffBuffers| commands.
  Default: true.

features.diff_origin

  Register the |:DiffOrig| command. Default: true.

features.diff_history

  Register the |:DiffHistory| command. Default: true.

features.diff_exit

  Register |:DiffExit| and the exit keymap. Default: true.

diff.default_view

  Layout used when view= is omitted. Default: "vsplit".

diff.default_output

  Delivery used when output= is omitted. Default: "buffer".

diff.default_source

  Left-hand source used when source= is omitted. Default: "current".

diff.default_orig_view

  Split direction used by |:DiffOrig| ("vsplit" for side-by-side, "split"
  for stacked). Kept separate from default_view because |:DiffOrig| always
  opens a native diffmode split — it never supports "inline".
  Default: "vsplit".

diff.algorithm

  Algorithm passed to vim.diff: "myers", "minimal", "patience", or
  "histogram". Default: "histogram".

diff.ctxlen

  Number of context lines around each hunk in unified output. Default: 3.

diff.word_diff

  Highlights the exact changed byte span within each paired removed/added
  line in view=inline/view=float, using the same DiffText group
  Neovim's native diffmode uses for intra-line changes. Only applies to runs
  where the removed and added line counts match (an unambiguous 1:1
  pairing). Default: true.

diff.url_timeout_ms

  Timeout in milliseconds for http(s):// sources/targets before the fetch
  is cancelled and reported as an error. See |diff-url-sources|.
  Default: 10000.

diff.image_compare

  When both source= and target= are readable raster-image file paths
  (png/jpg/jpeg/gif/webp/bmp — not svg, which is text and diffs fine as
  text), show them side by side via images.nvim
  (https://github.com/StefanBartl/images.nvim, images.gallery) instead of
  text-diffing raw bytes, which produces meaningless output. Every
  view=/output= value is ignored in this case. Without images.nvim
  installed, a clear warning is shown instead of silently falling through
  to the meaningless text diff. Set to false to restore the old
  behavior unconditionally. No relative scaling between the two images,
  unlike images.nvim's own :Image compare — see
  lua/diff/features/image_compare.lua's moduledoc for why.
  Default: true.

select_fn

  Optional replacement for vim.ui.select, injected for the target/source
  picker. Useful to wire a custom UI. Default: nil. When unset, see
  |diff-picker-resolution| for what actually gets used.

use_pickers_nvim

  Auto-detect pickers.nvim (https://github.com/StefanBartl/pickers.nvim) as
  the picker engine when select_fn is unset. Set false to always use the
  ui.kit chooser instead (which still honors a real vim.ui.select
  override), even if pickers.nvim is installed. Default: true.

setup() validates opts before merging them over the defaults above: an
unknown key or a value that doesn't fit its option is dropped instead of
silently reaching the merge (the default applies to that field, the rest of
opts still merges normally), and every dropped entry is listed under
|:checkhealth| diff. setup() itself never aborts or errors over a
validation issue.

4.1 Picker resolution *diff-picker-resolution*

The target/source picker (shown when target=/source= is omitted or set to
ask) resolves in this order:

  1. select_fn, if set — an explicit override always wins.
  2. pickers.nvim, if installed and use_pickers_nvim isn't false — its
     fuzzy engine (telescope.nvim, fzf-lua, or snacks.nvim, whichever
     pickers.nvim already resolved) is used automatically. No configuration
     needed on diff.nvim's side.
  3. ui.kit's own chooser — the always-available fallback. It
     still defers to a real vim.ui.select override (telescope-ui-select,
     dressing.nvim, …) when one is installed.

Detection is soft: if pickers.nvim isn't installed, or has no picker engine
available, diff.nvim silently falls back to the kit chooser — nothing errors.
Note that pickers.nvim's engines have no reliable cross-engine cancel signal,
so cancelling that picker (<Esc>) does not show the usual "Diff cancelled"
message the way cancelling the kit / vim.ui.select picker does.

4.2 Exit scope *diff-exit-scope*

The original global <Esc><Esc> mapping noticeably delayed a plain <Esc>
because Neovim had to wait for a possible second key everywhere. diff.nvim
fixes this:

exit.scope = "buffer" (default)

  The exit key is bound buffer-locally, only on buffers diff.nvim itself puts
  into diffmode. No global delay. Press it inside the diff (scratch) window to
  leave; |:DiffExit| works from anywhere.

exit.scope = "global"

  Legacy behaviour: a global normal-mode mapping.

exit.scope = false

  No mapping at all; rely on |:DiffExit|.

Native :diffthis

  By default the buffer-local exit key is only attached to buffers diff.nvim
  itself puts into diffmode — a plain :diffthis on some other buffer (outside
  diff.nvim's workflow) won't have it. Set exit.native_diffthis = true
  (requires scope = "buffer") to mirror the key onto any buffer that
  enters or leaves diffmode, native :diffthis/:diffoff! included, via an
  OptionSet watcher on the window-local 'diff' option.

  Off by default: it changes buffer-local keymaps outside diff.nvim's own
  workflow, which could surprise a config that already binds its own key on
  native :diffthis buffers, or uses :diffthis for something unrelated to
  diff.nvim entirely.

5. COMMANDS *diff-commands*


5.1 :Diff *:Diff*

  :[range]Diff [target=…] [source=…] [base=…] [view=…] [output=…]
Compare a source (left) with a target (right). Arguments use a key=value
grammar in any order. A key outside target=/source=/base=/view=/output= has
no effect and is warned about (typically a typo, e.g. veiw=inline), rather
than being silently indistinguishable from not typing it at all. Adding
base= turns this into a three-way diff — see |diff-three-way|.

When invoked with a range (e.g. a visual selection, :'<,'>Diff) and
source=current (the default), only the selected lines are used as the
source instead of the whole buffer. The range applies to the source side
only; the target is always taken in full.

target=

  The "other" material to compare against.
    clipboard    Content from the system clipboard register (+)
    ask          Force the interactive picker (same as omitting target=)
    git:{rev}    The current file at a git revision (see below)
    http(s)://…  Content fetched from a URL, async (see |diff-url-sources|)
    {path}       A file (tab-completed)
    {number}     An already-open buffer number
  When omitted, an interactive picker is shown (see |diff-picker-resolution|).

source=

  The left-hand side. Default: current.
    current      The buffer active when :Diff was invoked
    clipboard    System clipboard
    ask          Force the interactive picker (offers "current buffer" too)
    git:{rev}    The current file at a git revision (see below)
    http(s)://…  Content fetched from a URL, async (see |diff-url-sources|)
    {path}       A file
    {number}     A buffer number

git:{rev}

    Resolves the *current file* at a git revision — e.g. git:HEAD,
    git:HEAD~1, git:<sha>, or git:<branch>. Requires Neovim 0.10+
    (vim.system), a git executable on PATH, and a file-backed buffer
    inside a git repository. Runs git show <rev>:<relpath> off the main
    loop (async); no shell is spawned.

http(s)://{url}

    Fetches the URL's content asynchronously via curl and diffs against
    it. Requires Neovim 0.10+ (vim.system) and a curl executable on
    PATH. See |diff-url-sources| for the timeout setting and examples.

Image files

  When both source= and target= are readable raster-image file paths
  (png/jpg/jpeg/gif/webp/bmp; svg is excluded — it's text and diffs fine as
  text), :Diff shows them side by side via images.nvim instead of
  text-diffing raw bytes — every view=/output= value is ignored in this
  case. Without images.nvim installed, a clear warning is shown instead of
  silently falling through to a meaningless text diff. See |diff-config|
  (diff.image_compare) to disable this.

base=

  Optional — turns :Diff into a three-way diff. Accepts the same grammar as
  target= (clipboard, ask, git:{rev}, http(s)://{url}, a file path, or a
  buffer number). Requires output=buffer and view=vsplit/split/tab — see
  |diff-three-way|.

view=

  Layout for output=buffer. Default: vsplit.
    vsplit      Vertical split + native diffmode (side-by-side)
    split       Horizontal split + native diffmode
    tab         Side-by-side native diffmode in a new tab
    inline      Single scratch buffer holding the unified diff (ft=diff),
                word-level DiffText highlighting on changed spans
    float       Same as inline, in a floating window (press q or <Esc> to close)

  For vsplit/split/tab the left-hand pane is the origin window's own live
  buffer — and stays editable, so :diffget/:diffput write into the file you
  will save — but only when the source is that buffer in full, i.e.
  source=current (the default) without a range. Any other source= and any
  range are materialized into their own read-only scratch buffer and get a
  window of their own; the origin window keeps its buffer and stays out of
  the diff.

  Side labels: the unified-diff header (--- <source> / +++ <target>) and
  the scratch-buffer names ([Diff:source] …, [Diff:target] …,
  [Diff:base] …) use the specifier as written — which reads well
  for a file path, clipboard, git:{rev} or a URL. A buffer number is the
  exception (--- 7 says nothing), so it is labelled by that buffer's own
  name, shortened relative to the cwd/$HOME; an unnamed buffer falls back to
  buf:{N}. source=current is labelled buf:{N}, plus @{line1}-{line2}
  when a range narrowed it. A label is always folded to a single line: a
  buffer name may legally contain a newline, and a diff header is two lines.

  Line endings: a side that arrives as raw text (clipboard, http(s)://,
  git:{rev}) is normalized to the shape a buffer or a file already has — a
  trailing CR is dropped from every line, and a trailing newline terminates
  the last line rather than starting an empty one. Without that, a clipboard
  filled by a Windows application, a URL serving a CRLF document, or a
  repository with core.autocrlf=true would make two identical sides differ in
  every line. Line-ending differences are therefore not reported by :Diff;
  Neovim keeps that in 'fileformat', and a buffer side could never have shown
  it either.

output=

  Where the result goes. Default: buffer.
    buffer      Interactive diff in a split (see view=)
    prompt      Unified diff echoed to the message area
    file        Unified diff written to a temp file
    clipboard   Unified diff copied to the clipboard register (+)
    stat        Report +N -M, K hunks as a notification only (no window)

URL sources ~                                             *diff-url-sources*
  target=http(s)://… / source=http(s)://… fetch content asynchronously
  via curl (a direct argv exec through vim.system, never a shell string)
  — the editor stays responsive while the fetch is in flight, bounded by
  diff.url_timeout_ms (default 10000ms). Non-2xx HTTP responses are
  reported as errors, not diffed as content. Requires Neovim 0.10+ and a
  curl executable on PATH (both checked by |diff-health|). See
  docs/url-sources.md in the repository for requirements, configuration, and
  a set of real-world usage examples (dotfiles drift, vendored-code drift,
  gists, API schema checks, verifying a script before running it, …).

Three-way diff ~                                              *diff-three-way*
  base= opens a native *three-window* diffmode instead of two — the layout
  merge-conflict tools use. The current buffer stays the local side, live
  and editable, in the origin window (:diffget/:diffput write straight into
  the file you'll save). base= (the common ancestor) and target= (the
  remote/incoming version) each get a read-only scratch buffer. Neovim's
  diffmode natively diffs 3+ windows against each other — nothing custom is
  computed.

  Requires output=buffer (the default), view=vsplit/split/tab and
  source=current (the default) — prompt/file/clipboard/stat and
  inline/float are all two-input concepts with no three-way equivalent,
  and local is always the origin window's live buffer, so there is no window
  to put an explicit source= in. All three are rejected with an error if
  combined with base=, rather than accepted and ignored. A configured
  default_source is not affected; only a source= you typed is checked.

  See docs/three-way-diff.md in the repository for the full picture,
  layout diagrams, and merge-conflict-resolution examples.

Examples:
  :Diff
  :Diff target=clipboard
  :Diff target=42
  :Diff target=src/old.lua
  :Diff target=clipboard output=prompt
  :Diff target=clipboard view=inline
  :Diff target=a.lua source=b.lua
  :Diff target=clipboard output=clipboard
  :Diff target=src/old.lua output=stat
  :'<,'>Diff target=clipboard
  :Diff target=clipboard view=float
  :Diff target=git:HEAD
  :Diff target=git:HEAD~1 output=stat
  :Diff target=https://raw.githubusercontent.com/user/repo/main/f.lua
  :Diff target=git:MERGE_HEAD base=git:HEAD
  :Diff target=new.png source=old.png

5.2 :DiffClear *:DiffClear*

Close every scratch buffer diff.nvim created and disable diffmode in all
windows.

5.3 :DiffBuffers *:DiffBuffers*

  :DiffBuffers [view=…] [output=…]
Diff the current buffer against another open buffer, chosen from a picker of
all other listed, loaded buffers (uses the same picker as |:Diff|, see
|diff-picker-resolution|). A convenience wrapper over :Diff target={number};
the source is always the current buffer, so only view= and output= apply.

5.4 :DiffOrig *:DiffOrig*

Diff the current buffer against its last-saved version on disk — "what changed
since the last save". The snapshot buffer is tracked and cleaned up by
|:DiffClear|.

5.5 :DiffExit *:DiffExit*

Leave diff mode from anywhere (diffoff!). Works regardless of the configured
|diff-exit-scope|.

5.6 :DiffHistory *:DiffHistory*

  :DiffHistory [path] [view=…] [output=…]
List the commits that touched a file (git log --follow, so a rename is
tracked back through it), newest first, in a picker (uses the same picker as
|:Diff|, see |diff-picker-resolution|). Picking one diffs that commit against
its parent; view=/output= apply exactly as they do for |:Diff|. path
defaults to the current buffer's file.

Across a rename, the diff still compares the right two paths — the file's
name at the picked commit against its name at the parent — via the
git:{rev}:{path} form of a git source/target (an explicit path, relative
to the repo root, instead of the current buffer's own path). Capped at
opts.diff.history_max_entries commits (default 200). The very first commit
of a file's history has no parent to diff against and reports an error
rather than diffing against an empty tree.

Requires the same git/vim.system availability as git:{rev} sources.

6. TAB COMPLETION *diff-completion*

:Diff completes the key=value grammar context-sensitively, built via
lib.nvim.bindings.usercmd.composer (Route.kv). The value lists below are completion
hints, not a closed set — any string is still accepted as target=/source=/
base= (e.g. a literal file path); only view=/output= are drawn from a
fixed set of modes:

  :Diff <Tab>            target=  source=  base=  view=  output=
  :Diff view=<Tab>       view=vsplit  view=split  view=tab  view=inline  view=float
  :Diff output=<Tab>     output=buffer  output=prompt  output=file  output=clipboard  output=stat
  :Diff source=<Tab>     source=current  source=clipboard  source=ask  source=git:HEAD
  :Diff target=<Tab>     target=clipboard  target=ask  target=git:HEAD
  :Diff base=<Tab>       base=clipboard  base=ask  base=git:HEAD
  :DiffHistory <Tab>     a file path, then view=  output=

7. LUA API *diff-api*

  local diff = require("diff")
setup({opts})                                                *diff.setup()*
  Configure and activate. Idempotent.

enable({opts})                                              *diff.enable()*
  Alias for setup() — matches the legacy custom.diff signature.

run({raw_args}, {opts})                                        *diff.run()*
  Run a diff. {raw_args} uses the same grammar as |:Diff|. {opts} is an
  optional table of caller-side options — see |diff-on-done|.

clear()                                                      *diff.clear()*
  Close all diff windows and disable diffmode.

diff_buffers({raw_args}, {opts})                    *diff.diff_buffers()*
  Diff the current buffer against another open buffer chosen from a picker.
  {raw_args} accepts the same view=/output= grammar as |:Diff|; {opts}
  works as it does for |diff.run()|.

diff_origin()                                          *diff.diff_origin()*
  Diff the current buffer against its on-disk saved version.

exit()                                                        *diff.exit()*
  Leave diff mode from anywhere.

status({opts})                                              *diff.status()*
  Statusline component. Returns a short string (default diff:N, where N is
  the number of active diff.nvim scratch buffers) while a diff is active, or
  an empty string when none is. {opts.prefix} overrides the diff: prefix.
  Example:
    vim.o.statusline = "%f %{v:lua.require'diff'.status()}"
KNOWING WHEN A DIFF HAS FINISHED                                *diff-on-done*

opts.on_done is called exactly once when the diff has finished:
  require("diff").run("source=7 target=8 view=vsplit", {
    on_done = function(result, err)
      if not result then return end
      -- Closing the windows is enough; the scratch buffers are
      -- bufhidden=wipe and go with them.
      for _, win in ipairs(result.windows) do
        pcall(vim.api.nvim_win_close, win, true)
      end
    end,
  })
It fires on the asynchronous paths too (http(s):// fetches, git:<rev>,
the interactive picker), which is why this is a callback and not a return
value from run(): a return value could only be filled in for the synchronous
specifiers and would be silently empty for the rest. Errors are notified as
before; on_done is in addition to that, not instead of it. An on_done that
raises is caught, so a caller's error cannot surface inside a URL fetch.

{result} is nil (and {err} says why) when nothing was produced: an
unresolvable side, a rejected option combination, or a cancelled picker.
Otherwise it carries:

  output      The output= that produced this run
  view        The layout that was applied; nil when none was (the text
              outputs, and image/directory comparisons, which ignore view=)
  buffers     Scratch buffers diff.nvim created
  windows     Windows diff.nvim opened
  path        The file written, for output=file (nil otherwise)

windows lists only windows diff.nvim opened. The window |:Diff| was invoked
from is never included, even when it is part of the diff — with
source=current and a side-by-side view that window keeps the user's live,
editable buffer as the left-hand side. It belongs to the caller, and closing
everything in windows must not close the window they were working in.

Both lists can be empty on success: output=prompt/clipboard/stat create
nothing, and two identical sides produce a result with nothing in it rather
than an error. Check #result.windows, not result, to decide whether
anything is on screen. See docs/api.md for the per-mode table.

"Nothing to show" and "it could not be produced" stay apart, because they need
opposite handling: a diff that could not be computed, a file that could not be
written or a layout that could not be opened is nil plus a reason, exactly
like an unresolvable side, never an empty result.

A window in windows may be showing a buffer that is not yours to touch.
view=tab opens its own tab and puts the left-hand side in it, so with
source=current one of the two windows shows the user's own live buffer.
Closing that window is correct and is what windows is for; deleting the
buffer behind it is not, and with unsaved changes it destroys the user's
work. Close windows; never nvim_buf_delete() whatever nvim_win_get_buf()
hands back.

buffers is safe by construction — it only ever lists scratch buffers
diff.nvim created, never the user's. It is there to be read, not deleted:
those buffers are bufhidden=wipe and go away with their windows, and
|:DiffClear| takes down everything diff.nvim tracks.

8. HEALTH CHECK *diff-health*

  :checkhealth diff
Checks:
  - Neovim >= 0.9
  - vim.diff available
  - vim.ui.select available
  - clipboard provider present
  - git + vim.system available (for git:<rev> sources/targets)
  - curl + vim.system available (for http(s):// sources/targets)
  - pickers.nvim detected (informational — falls back to ui.kit)
  - plugin loaded (guard flag set)

9. ARCHITECTURE *diff-architecture*

  plugin/diff.lua        Load guard
  lua/diff/
    init.lua                  Public API, setup()/enable()
    @types.lua                LuaLS type definitions
    config/
      DEFAULTS.lua            Immutable default configuration
      init.lua                Merge + access to active config
    util/
      notify.lua              "[diff] " prefixed vim.notify wrapper
      validate.lua            Pure validation helpers
    core/
      init.lua                Orchestration: run(), run_buffers(), execute(), picker
      resolve.lua             Specifier → lines, argument parsing
      git.lua                 git:<rev> resolution via vim.system git-show
      url.lua                 http(s):// async fetch via curl + timeout timer
      pickers_bridge.lua      Optional select_fn adapter for pickers.nvim
      scratch.lua             Scratch-buffer lifecycle + cleanup_all() + active_count()
      render.lua              Output renderers (buffer/prompt/file/clipboard/stat/three_way)
    features/
      origin.lua              :DiffOrig logic
      exit.lua                :DiffExit logic + exit-behaviour config
      native_diffthis.lua     Opt-in exit-key mirroring onto native :diffthis buffers
    bindings/
      usrcmds.lua             :Diff/:DiffClear/:DiffBuffers/:DiffOrig/:DiffExit (lib.nvim.bindings.usercmd.composer) + completion
      keymaps.lua             Exit-keymap wiring (global + buffer-local)
      autocmds.lua            VimLeavePre cleanup
      init.lua                Orchestrates the three above
    health.lua                checkhealth provider

Load order: util -> config -> core -> features -> bindings -> init

Every keymap, user command, and autocmd is also cataloged in
docs/BINDINGS.md.