doc/lib.nvim.txt — rendered from the plugin's own vimdoc
*lib.nvim.txt* Reusable Lua/Neovim helper library *lib.nvim*
CONTENTS
1. Introduction ............................. |lib.nvim-introduction| 2. Installation ............................. |lib.nvim-installation| 3. Usage .................................... |lib.nvim-usage| 4. Namespaces ............................... |lib.nvim-namespaces| lib.lua ................................ |lib.nvim-lib.lua| lib.nvim ............................... |lib.nvim-lib.nvim| 5. Configuration ............................ |lib.nvim-config| 6. Health ................................... |lib.nvim-health| 7. Conventions .............................. |lib.nvim-conventions| 8. Module reference ......................... |lib.nvim-modules|
1. INTRODUCTION
lib.nvim is a reusable Lua/Neovim helper library, extracted from a private Neovim configuration so that personal plugins can share one tested base via a|lazy.nvim|dependency. The library is split by responsibility into two namespaces: lib.lua.* general, editor-independent Lua helpers (novimAPI) lib.nvim.* Neovim-specific helpers (adapters onto thevimAPI) Guiding rule: anything that does not need thevimAPI belongs in lib.lua.*. lib.nvim.* is only an adapter onto Neovim.
2. INSTALLATION
How you install lib.nvim depends on WHEN it is needed. As a dependency of other plugins (loaded on demand):
{
"you/my-plugin.nvim",
dependencies = { "StefanBartl/lib.nvim" },
}
With packer.nvim:
use {
"you/my-plugin.nvim",
requires = { "StefanBartl/lib.nvim" },
}
The package.path bootstrap below (|lib.nvim-bootstrap|) is specific to lazy.nvim's module loader. Under packer or another manager, adapt the same idea: prepend lib.nvim's lua/ dir to package.path before your first require("lib.*"). Standalone, lazily on first require:
{ "StefanBartl/lib.nvim", lazy = true }
*lib.nvim-bootstrap*
Config-wide use
If you use lib.* directly in your own config (lua/config/*, lua/plugins/*,
autocmds, mappings, ...), it is needed BEFORE lazy.nvim finishes reading your
plugin specs. A normal spec is too late, and rtp:prepend alone does not help:
lazy.nvim installs its own module loader that ignores runtimepath entries added
afterwards. You must also register lib.nvim on package.path (the C require
searcher, which lazy does not replace).
Bootstrap it in init.lua, BEFORE require("lazy").setup():
local libpath = vim.fn.stdpath("data") .. "/lazy/lib.nvim"
if not (vim.uv or vim.loop).fs_stat(libpath) then
vim.fn.system({
"git", "clone", "--filter=blob:none",
"https://github.com/StefanBartl/lib.nvim.git", libpath,
})
end
vim.opt.rtp:prepend(libpath)
package.path = table.concat({
libpath .. "/lua/?.lua",
libpath .. "/lua/?/init.lua",
package.path,
}, ";")
Then add a managed spec so :Lazy update keeps it current:
{ "StefanBartl/lib.nvim", lazy = false, priority = 1000 }
lib.nvim has no third-party dependencies; it uses only vim and itself.
3. USAGE
Direct module paths are recommended in plugin code (tree-shake friendly):
local notify = require("lib.nvim.notify")
local tables = require("lib.lua.tables")
local map = require("lib.nvim.bindings.keymap")
Or via the aggregator (require("lib")), which resolves keys on first access:
local lib = require("lib")
lib.notify -- -> lib.nvim.notify
lib.map -- -> lib.nvim.bindings.keymap
lib.is_windows() -- -> lib.nvim.cross.platform.is_windows
4. NAMESPACES
lib.lua
Editor-independent Lua helpers. No vim API; usable/testable outside Neovim.
lib.lua.tables array / dict / set / functional / safe / unique / with;
deep_merge (mutating recursive merge)
lib.lua.strings trim, split/join, case conversion, padding, slugify, …;
utf8 (codepoint encode/decode/iter), encoding
(percent-encode, base64, html_escape), distance (levenshtein,
similarity), format (format_bytes, format_number),
location (parse "path:line:col" and friends), case
(case_shape/apply_shape, change_case), wrap
(center_text, center_text_lines)
lib.lua.functions meta helpers: noop, identity, const, raise, …
lib.lua.time time / diff calculation; presets (today/yesterday/
last_week/this_{month,quarter,year}); format
(iso/human/short/log/filename/unix timestamps)
lib.lua.json encode (pure-Lua JSON encoder) + decode helpers
lib.lua.yaml minimal, deliberately non-spec-complete YAML decoder
lib.lua.uuid UUIDv4 generate/format
lib.lua.numeral roman <-> int, bijective base-26 alpha <-> int
lib.lua.diff lines (common-prefix/suffix splice region), myers
(LCS edit script)
lib.lua.error structured errors ({kind, message, data}) + safe_call
with traceback
lib.lua.dump recursive Lua value dumper (vim.inspect alternative)
lib.lua.memo memoization
lib.lua.lazy lazy-require proxy
lib.lua.config the "defaults + user overrides" store pattern:
deep_merge (non-mutating, arrays replaced wholesale)
+ get(tbl, path) dot-path accessor
lib.lua.class prototype OOP: new/extend/include, pure Lua
lib.lua.context_manager try/finally as one composable primitive (with);
built on lib.lua.error.safe_call
lib.lua.range parse "1-3,5,7"-style range specs into a sorted,
deduplicated integer[] (page/line/commit ranges)
lib.nvim
Neovim adapters.
lib.nvim.notify notify wrapper + log-level resolution
lib.nvim.logger structured logging / diagnostics / crash dumps
lib.nvim.bindings.keymap keymap helpers
lib.nvim.bindings.usercmd user-command helpers
lib.nvim.bindings.autocmd autocmd / augroup helpers; autocmd.dispatcher: one
autocmd, many handlers (lazy-loaded, prioritized,
per-buffer once), plus a FileType convenience
wrapper
lib.nvim.buffer buffer helpers (insert_lines, is_markdown_buf,
open_background, …); buffer.context: changedtick-
cached buffer metadata accessor; buffer.apply_edits:
bottom-up positional text edits with an optional
stale-match check per edit
lib.nvim.buf_win_tab buffer / window / tab utilities
lib.nvim.window overlay/float helpers (make_scratch, nice_quit, …);
window.context: same-event-cached window metadata
accessor
lib.nvim.ui kit (themed UI toolkit), highlight helpers
lib.nvim.fs path / filesystem helpers (vim.fs / uv); cached
marker-based root finding (find_root: glob markers
like *.rockspec, opt-in chain caching), relpath,
create_entry, mkdirp (mkdir -p on libuv only, safe
in a fast event context), normkey, project_key,
path_shorten,
collect_recursive / scan_cached / scan_roots
(recursive scanning, in-memory or on-disk TTL
cache; each has a coroutine-driven *_async
counterpart that doesn't block the main loop on
large trees), trash (cross-platform send-to-trash)
|lib.nvim-fs|
lib.nvim.cross cross-platform: OS detection, run/argv, clipboard,
run.env (spawn environment: guaranteed-complete
PATH + session/keyring vars) |lib.nvim-spawn-env|,
uv: spawn_capture (buffered, one callback at exit)
and spawn_stream (line-by-line streaming, optional
timeout, returns a kill handle),
path separators: unify_slashes (\ -> /), normalize
(-> OS-native), collapse_dots (lexical ./.. collapse),
has_win_sep (C:\ drive predicate), drive_upper
(uppercase C: prefix),
fs.wslpath (to_win / to_unix via the wslpath
binary; nil off WSL and for Linux-only paths)
lib.nvim.normalize path / value normalization
lib.nvim.git git helpers
lib.nvim.terminal terminal-buffer helpers
lib.nvim.require safe / dir / lazy require
lib.nvim.lua_ls LuaLS: module path, @module annotation
lib.nvim.core misc Neovim helpers (has_exec, simple_echo)
lib.nvim.treesitter filetype allowlist gate + prompt/auto-install
policy for missing treesitter parsers
|lib.nvim-treesitter|
lib.nvim.system host env snapshot + Windows rpc pipe + cross-platform
system info (float + clipboard, :SystemInfo usercmd)
+ proc_trace (blocking-call instrumentation for
freeze diagnosis, see lua/lib/nvim/system/README.md)
lib.nvim.selection reselect a Visual line/char range after a mapping
mutates it (keep_lines, keep_chars) |lib.nvim-selection|
lib.nvim.cache disk: persistent JSON cache with TTL; memory:
namespace cache with TTL/changedtick validation
and opt-in, toggleable autocmd auto-invalidation
lib.nvim.store project: persistent state keyed by project root
(git root, else cwd), built on cache.disk +
fs.project_key |lib.nvim-store|
lib.nvim.markdown.table GFM pipe-table parse/render engine (parse, at_cursor,
render, format_lines/_buffer/_at_cursor/_file);
extracted from markdown.nvim and buffer-ctx.nvim,
see lua/lib/nvim/markdown/table/README.md
lib.nvim.markdown.frontmatter flat-YAML frontmatter: parse, get/set/patch,
update_text/update change only the touched keys
(byte-exact roundtrip, CRLF/BOM aware, REMOVE
deletes a key); see
lua/lib/nvim/markdown/frontmatter/README.md
lib.nvim.health :checkhealth authoring helpers: version_ok(min),
check_require(mod, label, level); see
lua/lib/nvim/health/README.md
(telemetry) opt-in call counting / usage statistics moved to
runtime-analysis.telemetry (runtime-analysis.nvim,
a sibling plugin; see that plugin's README.md and
documentation.nvim's docs/ECOSYSTEM.md step 7).
This repo keeps a thin caller,
lib.strategies.telemetry_wrap, for instrumenting
require("lib")'s own metatable-hidden aggregate
specifically (see lua/lib/strategies/telemetry_wrap.lua).
lib.nvim.neotree neo-tree.nvim-specific helpers: node (path/node
extraction from a neo-tree state, marks/pickers
share it), watch (file-watcher handle registry +
proactive release — fixes a Windows directory-
lock neo-tree's own fs_watch leaks on rename/
delete; drive release() from cross.fs.mutate's
on_retry hook, see lua/lib/nvim/neotree/watch/README.md)
lib.nvim.contextmenu nvzone/menu context-menu item builder (entry/
group/submenu, self-gating) + mouse-trigger binder
(bind_buffer, soft dependency) |lib.nvim-contextmenu|
lib.nvim.async coroutine async/await over libuv (await/run/wrap),
Semaphore + Condvar + LatestWins (a "newest
request wins" token gate for overlapping async
work) |lib.nvim-async|
lib.nvim.count count-prefix helpers for keymaps (count1 vs count
vs "no count" disambiguation)
lib.nvim.debounce generic debounce primitive: new(fn, ms) returns a
{ call, cancel } handle
lib.nvim.dotrepeat wire native .-repeat through operatorfunc,
no vim-repeat dependency
lib.nvim.frecency frequency x recency ranking for anything a user
picks repeatedly
lib.nvim.image_preview in-Neovim image preview via images.nvim/
snacks.nvim/image.nvim (soft deps, auto-detected)
lib.nvim.json decode/encode arbitrary JSON strings (not just
files -- see lib.nvim.fs.json for that)
lib.nvim.lastcmd repeat the last real command (mapping or native
change), skipping pure motions
lib.nvim.net.curl async/blocking HTTP via curl (JSON / raw /
download-to-file tiers)
lib.nvim.safe_api validated, pcall-wrapped vim.api accessors for
buffers/windows (never raises on a deleted
buffer / closed window)
lib.nvim.token ephemeral session-nonce / token generator (NOT
cryptographically secure)
lib.nvim.vregex build \V-literal Vim-regex patterns from
arbitrary text (literal/escape); prevents
regex-injection when user/arbitrary text is
dropped into a search or :s pattern
lib.nvim.checkpoint snapshot a set of files before a destructive
multi-file operation (create/restore/discard),
byte-exact restore via fs_copyfile, built on
cross.fs.mutate
5. CONFIGURATION
The only runtime choice is which aggregator strategyrequire("lib")uses. All strategies expose the same surface; they differ only in WHEN submodules load. Configure BEFORE the firstrequire("lib"):
require("lib.config").setup({ strategy = "lazy" })
local lib = require("lib")
*lib.nvim-config-strategy*
strategy
"metatable" (default) per-key proxy; a submodule loads on first access.
"lazy" eager key registry; submodules load on first access.
"eager" every submodule is required up-front.
Direct module paths (e.g. require("lib.nvim.notify")) ignore this setting and
are always the most efficient way to consume the library.
6. HEALTH
Run a diagnostic with:
:checkhealth lib
It reports the Neovim version, the configured aggregator strategy, and whether a representative set of modules resolves.
7. CONVENTIONS
- One module per directory withinit.lua; module path == directory path. ----@module 'lib.<namespace>.<path>'as the first line of every file. - LuaLS type definitions (@class,@alias, standalone@type) live in@types/files, never inline in the source module. - Internal (non-public) modules are prefixed with_or live underinternal/; everything else is part of the public API. - Documentation is two-tier: a per-moduleREADME.mdnext to the source (the detailed function reference) and, for modules with:helpdocs, a filedoc/lib.nvim-<module>.txtin THIS directory tagged*lib.nvim-<module>*. Help only works from the runtimepath-rootdoc/, so all.txthelp lives here, one file per module — never in a nesteddoc/.
8. MODULE REFERENCE
Modules with dedicated:helpdocumentation. Jump to one via its tag, or:helpthe file directly. lib.nvim.window overlay / floating window helpers |lib.nvim-window| lib.nvim.ui.kit themed, composable UI toolkit |lib.nvim-kit| lib.lua.time.diff high-precision time / interval diff |lib.nvim-time_diff| lib.nvim.progress style-agnostic progress indicator |lib.nvim-progress| lib.nvim.treesitter filetype allowlist gate + parser install policy |lib.nvim-treesitter| lib.nvim.selection reselect a Visual range after mutation |lib.nvim-selection| lib.nvim.bindings.usercmd.composer subcommand user commands + docgen |lib.nvim-composer| lib.nvim.fs filesystem helpers: paths, scanning, IO, trash |lib.nvim-fs| lib.nvim.store project-scoped persistent state |lib.nvim-store| lib.nvim.logger structured logging / diagnostics / crash dumps |lib.nvim-logger| lib.nvim.harvest collect from a scope, then show/export it |lib.nvim-harvest| lib.nvim.deps declare/detect/install external tools |lib.nvim-deps| lib.nvim.cross.run.env spawn environment: complete PATH + session vars |lib.nvim-spawn-env| lib.nvim.async coroutine async/await over libuv |lib.nvim-async| lib.lua.strings.width display-width (column) arithmetic |lib.nvim-strings_width| lib.nvim.contextmenu nvzone/menu item builder + mouse-trigger binder |lib.nvim-contextmenu| Every other module documents itself in aREADME.mdnext to its source; see the namespace tables under |lib.nvim-namespaces|.