btv
Top-level btv.* functions (those without a sub-namespace), plus the
methods of the handles they hand back (promise:, iter:, stream:,
timer:).
btv.mode()
nvim_get_mode(): the editor’s current mode, read from the btv._cur_mode
snapshot the server refreshes before each Lua entry. blocking is always
false — the in-VM Lua bindings only run when the server is between keys, so it
is never blocked on input here. (The dedicated RPC method serves remote clients.)
Defined in api.lua.
btv.current_line()
nvim_get_current_line(): the text of the line the cursor is on in the current
window/buffer (no trailing newline). Composed from the cursor row and the
buffer’s lines — a completion plugin reads this when it builds a completion
context, which runs as soon as its core spins up, so a missing builtin would
break completion (and every completion source) at load.
Defined in api.lua.
btv.regex(pattern, opts)
btv.regex(pattern, opts) -> regex: compile pattern into a reusable regex object
for matching Lua strings — a more capable string.find/match/gmatch/gsub
with a real regex dialect (named groups, alternation, lazy quantifiers, …). The
match runs in Rust, so a string you already hold in Lua is matched in place (no
copy). For searching buffer text instead, use btv.buf.search. Raises on an
invalid pattern.
opts (all optional):
engine = "pcre" | "vim" -- regex dialect (default "pcre", the `regex` crate)
plain = false -- match the pattern literally (ignores `engine`)
ignorecase = false -- case-insensitive match
Offsets follow the string library: 1-based and byte-based, with :find’s end
inclusive, so s:sub(re:find(s)) is the matched text. The returned object has:
re:find(s, init?) -> start, end, cap1, … | nil (like string.find)
re:match(s, init?) -> the capture(s), or the whole match if the pattern
has none, or nil (like string.match)
re:gmatch(s) -> iterator over each match's captures or whole match
(like string.gmatch)
re:find_all(s) -> { s1, e1, s2, e2, … }: every match's start/end, in
one pass (a `:find` loop is quadratic on a long string)
re:gsub(s, repl, n?) -> newstring, count (like string.gsub)
repl is a string (`%0` whole match, `%1`-`%9` captures, `%%` literal `%`),
a function called with the captures (return nil/false to keep the match),
or a table keyed by the first capture.
re:test(s) -> boolean: does the pattern match anywhere
init is 1-based and may be negative to count from the end, as in string.find.
local re = btv.regex([[(\w+)@(\w+)]])
local _, _, user, host = re:find("to jo@acme now") -- "jo", "acme"
for word in btv.regex([[\w+]]):gmatch("one two") do ... end
local masked = btv.regex([[\d]]):gsub("id 42", "*") -- "id **"
Defined in api.lua.
btv.replace_termcodes(str, _from_part, _do_lt, _special)
nvim_replace_termcodes(str, from_part, do_lt, special): translate key notation
(<CR>, <C-w>, <lt>, …) into neovim’s terminal-byte encoding — <C-o> →
\15, <CR> → \r, <Esc> → \27, <lt> → < — via the native
btv._replace_termcodes. Keys with no single ASCII byte (arrows, function keys)
have no such encoding bemtvi can emit (neovim uses K_SPECIAL sequences bemtvi
doesn’t model), so they stay as <...> notation, which parse_keys still
consumes — so the result round-trips exactly through nvim_feedkeys (build a
“feed string”, later feed it). The flags (from_part / do_lt / special) only
shape neovim’s byte output and are accepted for call-compatibility; bemtvi always
translates the special names and <lt>.
Defined in api.lua.
btv.mode_str(_expanded)
btv.mode_str([expanded]) [alias vim.fn.mode]: the single-letter mode code
("n"/"i"/"v"/"V"/"R"/"c"). (Distinct from btv.mode(), which returns the
nvim_get_mode {mode, blocking} table.) INCOMPLETE: expanded is ignored — the
core has a flat Mode (no operator-pending / sub-state), so mode(1)’s multi-char
forms ("no", "niI", …) don’t exist here; the short code is returned for both.
Defined in api.lua.
btv.err_writeln(msg)
Message writers (aliases nvim_err_writeln / nvim_err_write / nvim_out_write).
Error writers route through btv._echo_err, which lands on the message line and
in :messages painted red (the core echo_err path); out_write funnels
through print like a plain message. nvim_err_writeln/out_write append a
newline; the *_write forms don’t (the message line is line-oriented, so both
just emit the text).
Defined in api.lua.
btv.err_write(msg)
No documentation comment in the prelude.
Defined in api.lua.
btv.out_write(msg)
No documentation comment in the prelude.
Defined in api.lua.
btv.list_uis()
btv.list_uis() [alias nvim_list_uis]: the attached UIs. bemtvi drives one client
at a time, so this reports a single UI sized to the editor screen
(vim.o.columns/lines), with the fields a layout calculation reads. The ext_*
feature flags are all false (bemtvi’s redraw protocol carries no external-UI
widgets).
Defined in api.lua.
btv.exec(src, output)
btv.exec(src, output) [alias nvim_exec]: run the ex-command(s) in src and,
when output is truthy, return the text they produced (see exec_capture).
Defined in autocmd.lua.
btv.on(event, opts, fn)
Events — structured autocmd subscriptions. btv.on(event, opts, fn): the
canonical verb. fn (when given) is the handler; otherwise opts.callback /
opts.command apply, exactly as the underlying registry expects. Returns the
subscription id (droppable with btv.off).
btv.on(event, fn) is the same thing with no options — the spelling to reach for
when there is nothing to configure:
btv.on("FileType", { pattern = "lua" }, function(ev) … end) -- with options
btv.on("BufWritePost", function(ev) … end) -- without
Defined in btv.lua.
btv.off(id)
Drop a subscription created by btv.on.
Defined in btv.lua.
btv.command_disabled(name)
btv.command_disabled(name) -> whether this editor refuses the command name spells
(any spelling: "e" and "edit" alike; a leading : and a trailing ! are
ignored). An app embedding the editor can take commands away from it; running one
then fails with E5800, on every route. Always false elsewhere.
Defined in btv.lua.
btv.command(name, fn, opts)
User commands — btv.command(name, fn, opts) defines :Name; fn is a
function or an ex-command string.
Defined in btv.lua.
btv.uuid()
btv.uuid() -> a fresh random (version-4) UUID as a canonical 8-4-4-4-12 lowercase-hex
string, e.g. "f47ac10b-58cc-4372-a567-0e02b2c3d479". Bytes come from the OS CSPRNG,
so each call is unique; handy for a session id, a temp-file name, or any unique key.
Available on every build (native and browser/wasm).
Defined in btv.lua.
btv.echo(msg)
btv.echo(msg) -> nil. Append msg (a string) to the message line — the programmatic
echo, the canonical form of vim.api.nvim_echo. For a transient, separately-styled
notification prefer btv.notify.
Defined in btv.lua.
btv.argv()
btv.argv() -> the list of positional file arguments this process was launched with
(strings; empty when none). A launcher / wrapper reads them to forward to a
relaunched editor; carried through the BEMTVI_ARGV environment variable, so the
binary stays the single source of truth.
Defined in btv.lua.
btv.now_ms()
btv.now_ms() -> a monotonic timestamp in milliseconds (a number) for timing and
scheduling math. Unlike os.clock (CPU time, ≈0 across an awaited tick) it advances
with real wall-clock time, so it measures durations that span async work.
Defined in btv.lua.
btv.runtime_file(name, all)
btv.runtime_file(name[, all]) -> full paths of runtimepath files matching name (a
runtimepath-relative path whose final component may be globbed with *), as a list.
With all falsey it returns just the first match (a one- or zero-element list).
Reads the LIVE runtimepath, so a plugin installed mid-session contributes its files
immediately. The lsp/<server>.lua config-discovery primitive.
Defined in btv.lua.
btv.open(path, opts)
btv.open(path[, opts]) -> nil. Open a file (or a directory, which opens the file
explorer) in the editing area. opts is an optional table:
-
reuse(boolean, default true) — “open or jump”. How to handle the file when it is already up:- shown in a window → focus that window (across tabs under the default
'switchbuf'=usetab); the file is NOT reloaded and no split is made. - loaded but hidden → show that existing buffer in the current window, preserving its edits and cursor (no re-read, no duplicate buffer).
- not open at all → read it fresh into the current window.
This is what a file explorer, a “go to file”, or a jump-to-source wants: click a file that’s already on screen and you land on it rather than getting a second copy. Set
reuse = falsefor plain:editsemantics — always load into the current window even when another window already shows the file, e.g. to deliberately place a buffer into a split you just created. - shown in a window → focus that window (across tabs under the default
-
where("main"| nil) — withwhere = "main"the open first crosses to the main editor layer, so an open fired from a dock / sidebar keymap lands in the main area instead of inside the dock. Omitted, it opens in the current window.
Note: only reuse (the default) consults 'switchbuf', and 'switchbuf' only
ever redirects to a window already DISPLAYING the buffer — a hidden buffer has no
such window, hence the “loaded but hidden” case above.
Defined in btv.lua.
btv.component(def)
btv.component(def) -> { mount(opts) } — build a reactive, Vue-shaped UI component for
a plugin surface, then :mount() one or more instances of it. The reactive core is
surface-agnostic: the same component can drive a focus-taking buffer or a passive
float, chosen per def / mount.
def is a table:
render(state, inst)— REQUIRED and PURE. Maps the current state to what’s on screen and returns the surface’s output (see Surfaces). The framework re-runs it automatically whenever reactive state changes, coalesced to ONE render per tick. May be async (callbtv.await(...)straight inside it).setup(ctx, props)— OPTIONAL; runs ONCE on mount and owns every side effect — it creates reactive state, subscribes to events, binds keys, fetches data — and RETURNS thestatevalue handed torender. Runs only after the surface is ready, so everything onctxis already valid (no tick-dance). May be async.surface—"view"(default) or"float"; or passbackend(afunction(opts) -> adapter) to render to a custom surface.
The ctx handed to setup carries the reactivity and lifecycle:
ctx.reactive(tbl)— a deep reactive proxy; writing any key (s.x = 1) schedules a re-render. Iterate withipairs/#(NOTpairs— PUC 5.4 has no__pairs).ctx.computed(getter)— a cached derived value, read asc()orc.value; it re-evaluates only when a reactive input it read last time has changed.ctx.refresh()— force a re-render.ctx.props— theopts.propsfrommount.ctx.on_close(fn)/ctx.close()— register a teardown hook / close the instance. On the"view"surfacectxalso gains:ctx.view,ctx.bufnr(),ctx.winid(),ctx.line(),ctx.set_cursor(n),ctx.bo/ctx.wo(the view’s buffer/window-local option tables), andctx.keymap_set(mode, lhs, rhs, opts)(buffer-scoped +nowaitby default). A view is read-only, so in a modeless session ('modeless') its keys reach it only through themodelesslayer: pass{ "n", "modeless" }asmodeto serve both editing models.
Surfaces — what render returns, and how the surface behaves:
"view"— a focus-taking, navigablebtv.viewbuffer (dock / split / grabbing float): the file-tree / list / modal-dialog case.renderreturns{ lines, decor }(or a bare line list).btv.view.component(def)is the sugar."float"— a NON-focusbtv.ui.floatcontent float (the which-key surface): never steals focus, binds no keys.renderreturns{ lines, title?, relative?, border? }; an EMPTY render HIDES the float (a later non-empty one re-opens it), so a component shows/hides purely by what it returns. Only one float component may display at once (a second fails loud rather than clobbering the single content-float slot).
mount(opts) instantiates and returns the instance (with :close()). opts.props is
passed to setup; the rest configures the surface — view: name / filetype / dock
/ split / float (and eob to keep end-of-buffer tildes); float: title /
relative / border. Render errors and setup errors are caught and surfaced via
btv.notify rather than crashing the editor.
Persistence (view surface) — mount{ persist = "<id>", … } opts a view component into
cross-session restore, the high-level form of btv.view.create{ persist=} +
btv.view.on_restore. The framework resolves the owning namespace ONCE (from the
mount call site, or opts.namespace for a no-attribution context — the same escape-hatch
contract as btv.shada.plugin()), records only (namespace, id) + the view’s slot in the
session, and on a restart picks fresh-vs-restore for you: it adopts the reserved slot if
the session reopened the view, else mounts fresh — no on_restore handler, no VimEnter
fallback. The content is the component’s own: setup reads it from ctx.store and a
mutation saves it back. A persistent component’s ctx gains:
ctx.store—btv.shada.plugin(ns)for the resolved owner namespace: an isolated, cross-session key/value slice. Read saved state insetup, write it on every change.ctx.namespace— the resolved owner namespace;ctx.persist_id— the stable id. (examples/view-persist/is a runnable pinned-notes plugin built on exactly this.)
Example — a live-updating counter in a floating view:
local Counter = btv.component({
setup = function(ctx)
local s = ctx.reactive({ n = 0 })
local keys = { "n", "modeless" } -- both editing models
ctx.keymap_set(keys, "+", function() s.n = s.n + 1 end) -- write -> re-render
ctx.keymap_set(keys, "q", ctx.close)
return s
end,
render = function(s)
return { lines = { "count: " .. s.n, "", "+ to increment · q to quit" } }
end,
})
Counter.mount({ float = { width = 30, height = 4, grab = true } })
Defined in component.lua.
btv.on_key_pending(fn)
btv.on_key_pending(fn): subscribe to the engine-computed pending-key signal. The
server fires it whenever the pending key-context changes — a mapped prefix
grows (you type <leader>, then w) or clears (the sequence completed, broke, or
the idle flush resolved it) — NOT per keystroke (ADR 0002 rule 4: no per-key
Lua). The handler receives
ctx = { mode, keys, continuations = { { key, desc, kind = “map”|“group” }, … } }
where keys is the withheld prefix in vim notation (“” when the context cleared,
which a which-key popup treats as “close”) and each continuation is one key that
extends the prefix — kind = "map" completes a mapping (carrying its desc),
kind = "group" only leads on to longer mappings. The finite built-in prefixes
(g -> gg/gt/.., z -> zt/zz/.., <C-w> -> window commands) enumerate
continuations too, merged under one prefix with any maps that share it (g + the
LSP gd/gD/gr).
available (boolean) is true for every reachable continuation. It is false
only for a continuation kept visible but no longer firable: a mapped g-prefix
(gd/gD/gr) surfaced AFTER the leader timeout committed g to the built-in grammar
– the maps need a faster sequence to fire, so which-key shows them dimmed / cued
rather than dropping them mid-popup. (A future per-line float highlight will let a
which-key gray these properly; see docs/plans/2026-06-16-keypending-source-b.md.)
ctx.label (a string, or nil) is the source B channel: the built-in command
grammar’s open pending states — f/t/F/T find-char, r replace, i/a
text-object, marks, registers, <C-w> — have no finite key list to enumerate, so
they arrive with continuations = {} and a human label instead
("Find character"). A which-key renders the label as a hint card when
continuations is empty; sources A/C (mapped prefixes) leave label nil and list keys.
This is the render-time oracle a native which-key plugin debounces
(btv.utils.debounce) and draws as a persistent btv.ui.float. Continuations come from
the mapped-prefix trie (user + native-default maps, sources A/C) AND the built-in
command grammar’s finite prefixes (source B); the open built-in states are
surfaced via label instead.
Defined in keymap.lua.
btv.run_local(spec)
btv.run_local { cmd, args, cwd, env, stdin } -> promise of { code, stdout, stderr }.
The local-always twin of btv.run: runs a one-shot child to completion on THIS machine,
even when the session’s processes route to a daemon. Same spec + resolution as btv.run
(RESOLVES, never rejects; a spawn failure is code = -1). Reach for it for client-side
work — provisioning a remote, an ssh control-master, a docker cp — that must not run on
the daemon. In a bare/local session it is exactly btv.run.
Defined in localseam.lua.
btv.run(spec)
btv.run { cmd, args, cwd, env, stdin } -> promise of { code, stdout, stderr }.
Runs a child to completion off the input tick, buffering all of its stdout and
stderr and resolving once, on exit.
Spec fields:
cmd— the program. A string, or an argv list whose first element is the program. Spawned directly, with NO shell: nothing is word-split, quoted, or glob-expanded, so pass each argument as its own element.args— optional list appended aftercmd, so{ cmd = "git", args = { "log" } }and{ cmd = { "git", "log" } }are equivalent.cwd— optional working directory for the child.env— optional{ NAME = value }map of environment overrides.stdin— optional string piped to the child; its stdin then closes (EOF).
RESOLVES (never rejects) with the exit result: a non-zero code is the caller’s
to act on, and a spawn failure (e.g. binary not found) surfaces as code = -1
with empty output — exactly like vim.system. Await it inside btv.async, or chain
with :next / :catch:
btv.async(function()
local r = btv.await(btv.run({ cmd = "git", args = { "rev-parse", "HEAD" } }))
if r.code == 0 then btv.print(r.stdout) end
end)()
The one-shot promise twin of btv.run_stream (stream stdout as it arrives). For a
duplex child whose stdin stays open for a framed protocol (LSP/DAP) use
btv.process instead.
Defined in process.lua.
stream:next()
No documentation comment in the prelude.
Defined in process.lua.
stream:kill()
No documentation comment in the prelude.
Defined in process.lua.
stream:exit()
stream:exit(): how the child ENDED — a table { code, stderr } once it has
exited, or nil while it is still running (so it is meaningful only after the
btv.await_each loop has finished). code is the child’s real exit status;
-1 means the child never ran — the binary wasn’t found, or :kill() reaped
it. stdout is empty here: it was already delivered through the stream.
The point of the accessor is to tell “the tool ran and its answer was nothing”
apart from “the tool isn’t installed”, which output alone cannot distinguish. A
fallback chain must branch on the status, not on emptiness — rg exits 1 on
zero matches, and re-searching the tree with the next tool for that is pure waste:
local stream = btv.run_stream({ cmd = "rg", args = { "--vimgrep", "--", q } })
for batch in btv.await_each(stream) do ... end
local exit = stream:exit()
if exit and exit.code == -1 then ... end -- no rg here; try the next tool
Defined in process.lua.
stream:pid()
stream:pid(): the running child’s OS pid, or nil. The pid is reported
asynchronously by the event-loop actor (it can’t be known synchronously on the
single-threaded runtime) into the btv._proc_pids registry this reads — so nil
before the spawn lands, on a failed spawn, and again after the child exits (the
registry entry dies with the child). Handy for signalling the child out-of-band
(e.g. kill -USR1) where :kill()’s reap is too blunt.
Defined in process.lua.
btv.run_stream(spec)
btv.run_stream { cmd, args, cwd, env } -> Stream. Spawns a child and streams its
stdout as it arrives, in newline-delimited batches — each batch a list of lines
with the trailing newline stripped. Takes the same spec as btv.run minus stdin
(the child’s stdin is closed at spawn). Only stdout streams; the exit status and
stderr land together at the end, on stream:exit().
The streaming twin of btv.run — reach for it when output is large or long-lived
and you want to act on lines as they come (the picker / completion sources feed
results this way) rather than waiting for the child to exit. Iterate it with
btv.await_each inside an btv.async function; call :kill() to reap the child early
(e.g. a superseded query):
btv.async(function()
local stream = btv.run_stream({ cmd = "rg", args = { "TODO" } })
for batch in btv.await_each(stream) do
for _, line in ipairs(batch) do btv.print(line) end
end
end)()
Defined in process.lua.
btv.await_each(stream)
btv.await_each(stream): a for-loop iterator over a Stream’s batches. Each step
awaits the next batch; the loop ends when the stream is exhausted (:next()
resolves nil). MUST run inside an btv.async function (btv.await suspends the
enclosing coroutine).
for batch in btv.await_each(stream) do
for _, line in ipairs(batch) do ... end
end
Defined in process.lua.
promise:next(on_fulfilled, on_rejected)
:next(on_fulfilled, on_rejected) — the spine. Returns a NEW promise resolved
with the handler’s return (adopting it if it’s itself a promise), or rejected
if the handler throws. A missing handler passes the value/reason straight
through to the returned promise, which is what makes a bare :catch at the end
of a chain catch errors from anywhere earlier in it.
Named :next (not :then) because then is a Lua keyword — p:then(...)
won’t parse. :next is the idiomatic Lua spelling; :catch/:finally keep
their browser names verbatim.
Defined in promise.lua.
promise:catch(on_rejected)
:catch(on_rejected) — sugar for :next(nil, on_rejected), verbatim browser.
Defined in promise.lua.
promise:finally(on_finally)
:finally(on_finally) — run on_finally whichever way the promise settles, then
pass the original value/reason through untouched (so finally can’t swallow a
result or a rejection). Matches the browser’s pass-through semantics: when
on_finally returns a promise (an async cleanup), the chain waits for it, and a
cleanup that rejects (or throws) replaces the outcome with its own reason.
Defined in promise.lua.
btv.async(fn)
btv.async(fn) returns a function that, when called, runs fn as a coroutine
and returns a promise for its result. Inside, btv.await(p) suspends until p
settles and evaluates to its value (or re-raises its rejection as a Lua error),
so a sequence of awaits reads top-to-bottom with no nesting:
local load = btv.async(function(path)
local stat = btv.await(fs.stat(path))
local data = btv.await(fs.read(path))
return parse(data, stat)
end)
load("init.lua"):next(use):catch(report)
A rejected await raises inside the coroutine, so you can handle it either way:
wrap the await in pcall to catch it locally (PUC 5.2+ yields across a pcall
boundary, so this works on bemtvi’s 5.4 backend), or let it propagate to the
coroutine edge (the returned promise rejects, caught by :catch on the result)
or attach :catch to the awaited promise itself.
Defined in promise.lua.
btv.await(awaitable)
btv.await(awaitable): suspend the enclosing btv.async coroutine until awaitable
settles. Returns the fulfilment value, or raises the rejection reason as an
error (which, uncaught, rejects the async function’s promise). Errors loudly if
called outside an btv.async coroutine — there is nothing to suspend.
Defined in promise.lua.
btv.schedule(fn)
btv.schedule(fn): defer fn to the end of the current convergence — it runs
after the work that scheduled it settles, no longer nested in the caller’s
stack frame (the strict improvement over the old inline fn()), but still
within the same input tick (not a later wall-clock turn; that is defer_fn).
This is exactly what the colorscheme’s “defer to avoid reentrancy” wants.
Defined in runtime.lua.
btv.schedule_wrap(fn)
btv.schedule_wrap [alias vim.schedule_wrap] (fn): return a function that, when
called, schedules fn with whatever arguments it was given — a common plugin
idiom for “run this callback safely on the loop”. The captured args ride into
the deferred call via a closure.
Defined in runtime.lua.
timer:stop()
No documentation comment in the prelude.
Defined in runtime.lua.
timer:is_active()
No documentation comment in the prelude.
Defined in runtime.lua.
btv.timer(fn, timeout)
btv.timer(fn, timeout): the canonical timer / defer primitive (aliased by
vim.defer_fn) — run fn once, timeout ms from now, on the loop — the
off-tick deferral configs use for retry patterns. Returns a handle so the
caller can :stop() it before it fires.
Defined in runtime.lua.
btv.on_next_tick(fn)
btv.on_next_tick(fn): run fn on the NEXT event-loop tick — the turn after the
current one finishes. The cross-tick sibling of btv.schedule: where btv.schedule
fires at the end of THIS convergence (a same-tick microtask, so it cannot observe
state that only refreshes between ticks — a freshly-mounted window’s id, a mirror
the server repopulates each turn), btv.on_next_tick yields the tick entirely and runs
on the next one, when those mirrors have been refreshed. A zero-delay one-shot
timer is exactly that. Returns the timer handle, so a caller can :stop() it before
it fires. (Poll across several ticks by calling it again from within fn.)
Defined in runtime.lua.
btv.notify(msg, level, _opts)
No documentation comment in the prelude.
Defined in runtime.lua.
btv.notify_once(msg, level, opts)
btv.notify_once(msg[, level[, opts]]) [alias vim.notify_once]: btv.notify, but a
given message is shown only the FIRST time it is sent this session (as neovim’s), so a
deprecation warning raised from a hot path does not flood the message line. Returns
true when the message was shown, false when it was suppressed as a repeat.
Defined in runtime.lua.
btv.inspect(value)
btv.inspect [alias vim.inspect]: pretty-print a value (tables recursively). A
table reached again on the current descent renders as "<cycle>" — a plugin
inspects arbitrary state (parent-linked trees, self-referencing registries),
and unguarded recursion would blow the C stack instead of printing.
Defined in runtime.lua.
btv.exists(expr)
btv.exists(expr) [alias vim.fn.exists]: does the vim entity named by expr exist? (1 / 0). bemtvi
answers the forms it can verify and reports 0 for the rest (rather than a fake
- so feature-probing stays honest:
'&opt'/'&l:opt'/'&g:opt'/'+opt'-> an option bemtvi models. A completion plugin gates every window-option write onexists('+'..key), so an unknown option is skipped instead of erroring the float setup.'g:'/'b:'/'w:'/'t:'/'v:'prefixed name -> that scoped variable is set.':Cmd'-> a user command bemtvi can confirm (2, neovim’s exact-match value); a buffer-local command for the current buffer counts, like at dispatch.- everything else (
'*func', built-in':write', bare names) -> 0 (can’t confirm).
Defined in state.lua.
btv.log
btv.log [alias vim.log]: the log-level constants plugins compare against.
Defined in state.lua.
btv.set_option(name, value)
btv.set_option / btv.get_option(name[, value]) [aliases nvim_set_option /
nvim_get_option]: the global-scope (deprecated) accessors. Route through btv.o,
the canonical global-option table that canonicalizes the scope the name implies.
Defined in state.lua.
btv.get_option(name)
No documentation comment in the prelude.
Defined in state.lua.
btv.npcall(fn, ...)
btv.npcall(fn, ...) [alias vim.npcall]: pcall that maps failure to nil — select(2, pcall(...))
on success, nil on error. A neovim helper kept for config/plugin convenience
(wrap a call that may raise and treat failure as "no value").
Defined in stdlib.lua.
btv.nonnil(...)
btv.nonnil(...) [alias vim.nonnil]: the first non-nil argument, or nil (verbatim from neovim’s
vim/_core/shared.lua; the replacement for the deprecated vim.F.if_nil). A general
helper for defaulting an optional value.
Defined in stdlib.lua.
btv.keytrans(s)
btv.keytrans(s) [alias vim.fn.keytrans]: translate the internal terminal-byte
form of a key sequence into readable key notation (\15 → <C-o>, \r → <CR>,
<Space>, …) — the exact inverse of nvim_replace_termcodes, via the native
btv._keytrans (parse_keys + key_to_notation). A string already in notation
round-trips through unchanged.
Defined in stdlib.lua.
btv.print(...)
btv.print(...) [alias vim.print]: pretty-print each argument (via btv.inspect) on the message
line and return them unchanged, so it can wrap a value inline. Strings print
verbatim; tables are inspected.
Defined in stdlib.lua.
btv.iter(src, state, ctrl)
btv.iter(src[, state, ctrl]) [alias vim.iter]: wrap a list-like table OR a Lua iterator triple
in a chainable iterator. The triple form is what vim.iter(vim.fs.parents(p))
passes — vim.fs.parents returns (fn, state, start), which Lua spreads as
three args here — so the ancestors are drained eagerly into the item list.
Defined in stdlib.lua.
iter:find(pred)
Iter:find(pred): the first item for which pred(item) is truthy (or, when
pred is a plain value, the first item equal to it), else nil.
Defined in stdlib.lua.
iter:any(pred)
Iter:any(pred): true iff pred(item) is truthy for some item.
Defined in stdlib.lua.
iter:flatten()
Iter:flatten(): flatten one level of list-valued items into the stream.
Defined in stdlib.lua.
iter:map(f)
No documentation comment in the prelude.
Defined in stdlib.lua.
iter:filter(f)
No documentation comment in the prelude.
Defined in stdlib.lua.
iter:each(f)
No documentation comment in the prelude.
Defined in stdlib.lua.
iter:fold(acc, f)
No documentation comment in the prelude.
Defined in stdlib.lua.
iter:totable()
No documentation comment in the prelude.
Defined in stdlib.lua.
btv.line(expr)
btv.line(expr) [alias vim.fn.line]: a buffer line number (1-based). "." is the
cursor line, "$" the last line (the line count), "v" the fixed end of the live
Visual selection (the cursor line outside Visual mode), and "'x" the line of mark
x (0 when it is not set) — the same expressions btv.pos.get takes. The
window-relative forms ("w0"/"w$") are not modelled and raise an error naming the
expression.
Defined in vimfn.lua.
btv.col(expr)
btv.col(expr) [alias vim.fn.col]: a byte column (1-based). "." is the cursor
column, "$" one past the end of the cursor line (its byte length + 1), matching vim.
Defined in vimfn.lua.
btv.localtime()
btv.localtime() [alias vim.fn.localtime]: the current time in seconds. bemtvi
sources this from a MONOTONIC clock (the server’s btv._mono_secs, the same base
stamped onto undo nodes), not wall-clock unix epoch, so localtime() - node.time
elapsed math (e.g. the undotree visualizer’s "N minutes ago") stays correct and
non-negative across NTP steps and manual clock changes. Only differences matter.
Defined in vimfn.lua.
btv.expand(expr)
btv.expand(expr) [alias vim.fn.expand]: the % (current file) and #
(alternate file) forms autocmd callbacks and statuslines use to resolve paths,
backed by the current-buffer snapshot and the # mirror. % / # are the stored
names; a :<mods> suffix routes through fnamemodify (so %:t, %:p, #:h,
#:r, %:~:., … all work). Any other expression errors loud.
(the override below extends this with cursor keywords / globs, re-binding btv.expand.)
Defined in vimfn.lua.
btv.getmousepos()
btv.getmousepos() [alias vim.fn.getmousepos]: the most recent mouse event’s
position as a dict — screenrow/screencol (1-based global screen cell),
winid (the window the cell is in, 0 if none), winrow/wincol (1-based,
window-relative, gutter included), line/column (1-based buffer line and byte
column, 0 off a window’s text), and coladd (always 0 — bemtvi has no
'virtualedit'). Reads the btv._mouse_pos mirror the server pushes from the
editor’s last mouse cell, so a mouse mapping (<RightMouse>, <MiddleMouse>, …)
can act on the clicked position rather than the cursor.
Defined in vimfn.lua.
btv.cwd()
btv.cwd() [alias vim.fn.getcwd]: the editor’s effective working directory, as an
absolute path with no trailing separator.
This is editor state, not a filesystem read: it is answered from the mirror the
server republishes on every :cd / :lcd, so it costs nothing and is safe on a
hot path. Over a daemon it reports the daemon’s cwd — the directory relative
paths actually resolve against — which is the whole reason a plugin should ask
here rather than at whatever process it happens to be running in.
The fallback root: a config that walks up looking for a project marker and finds none often wants “wherever the user is working” rather than the file’s own directory.
local root = btv.await(btv.lsp.find_root(bufnr, { ".git" })) or btv.cwd()
Defined in vimfn.lua.
btv.pid()
btv.pid() [alias vim.fn.getpid]: this editor process’s id.
What a language server wants when it offers a --hostPID-style flag: it watches
that process and exits when the process does, so a crashed editor doesn’t leave a
server holding a project’s worth of memory. In a daemon session this is still the
process the server is a child of, which is the one it must watch.
Defined in vimfn.lua.
btv.version()
btv.version() [alias vim.version]: the editor’s version, as "bemtvi <x.y.z>".
For the protocols that carry a client identity — LSP clientInfo, a vendor’s
“integration version” telemetry field — and for a plugin reporting what it is
running under. It is a display string, not a comparable version object: bemtvi’s
surfaces are not version-gated, so there is nothing here to branch on.
Defined in vimfn.lua.
btv.stdpath(what)
btv.stdpath(what) [alias vim.fn.stdpath]: the XDG directory bemtvi keeps what
in, as an absolute path with no trailing separator. "config", "data",
"cache", "state", "log", "run".
Where a plugin puts the things that are neither the user’s code nor the editor’s:
a downloaded language server, a parser cache, a scratch workspace. Ask here rather
than composing $HOME by hand — the answer honors $XDG_* and $BEMTVI_CONFIG, so
a hand-built path silently diverges from where the editor itself looks.
local workspace = btv.utils.joinpath(btv.stdpath("cache"), "jdtls", "workspace")
The directory is named, not created: btv.fs.mkdir(dir, { recursive = true })
when you are about to write into it. It raises when the process has no home, no
$XDG_* base and no platform default to put the directory under — rather than
answering a path relative to the working directory.
Defined in vimfn.lua.