Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

btv.buf

btv.buf.name(bufnr)

btv.buf.name(bufnr) -> string [alias nvim_buf_get_name / vim.fn.bufname]: the full name (path) of buffer bufnr; 0 / nil means the current buffer. Returns "" for an unnamed or unknown buffer. Read from the buffer mirror, so it can name any open buffer — e.g. a custom ‘tabline’ labelling a buffer shown in another tab.

Defined in api.lua.

btv.buf.current()

The buffer / window / cursor getters read the btv._bufs / btv._cur_* mirror the server refreshes before each Lua entry, so they return live state as of the start of this chunk. There is deliberately no buffer mutation surface here (no btv.buf.set_lines): plugins supply data through the higher-level surfaces, per this file’s header. Throughout, a bufnr of 0 / nil means the current buffer.

btv.buf.current() -> bufnr [alias nvim_get_current_buf]: the current buffer’s number (0 if there is none).

Defined in api.lua.

btv.buf.call(buf, fn)

btv.buf.call(buf, fn) -> any [alias nvim_buf_call]: run fn with buf (0/nil = current) installed as the current-buffer context, then restore the previous context and return whatever fn returned. Use it so buffer-relative lookups inside fn (name, options) resolve against buf. An error in fn propagates.

Defined in api.lua.

btv.buf.is_loaded(bufnr)

btv.buf.is_loaded(bufnr) -> bool [alias nvim_buf_is_loaded]: whether bufnr (0/nil = current) names a buffer that is loaded into memory. Backed by the buffer mirror, which carries every loaded buffer.

Defined in api.lua.

btv.buf.is_valid(bufnr)

btv.buf.is_valid(bufnr) -> bool [alias nvim_buf_is_valid]: whether bufnr (0/nil = current) names a buffer bemtvi knows about. There is no separate “valid but unloaded” state in the mirror yet, so this currently matches btv.buf.is_loaded.

Defined in api.lua.

btv.buf.changedtick(bufnr)

btv.buf.changedtick(bufnr) -> integer [alias nvim_buf_get_changedtick]: the buffer’s change counter (0/nil = current buffer), bumped by the core on every text change and never otherwise. 0 for an unknown buffer.

This is the canonical “did this buffer’s text change” signal: cache derived state against it and recompute only when it moves, instead of redoing the work on every event. A statusline component that enumerates matches, a plugin that parses the buffer, any per-buffer memo — key it on (bufnr, changedtick):

local memo = {}
local function matches(buf)
  local tick = btv.buf.changedtick(buf)
  local m = memo[buf]
  if m and m.tick == tick then
    return m.value
  end
  local value = expensive_scan(buf)
  memo[buf] = { tick = tick, value = value }
  return value
end

Defined in api.lua.

btv.buf.line_count(bufnr)

btv.buf.line_count(bufnr) -> integer [alias nvim_buf_line_count]: the number of lines in bufnr (0/nil = current); 0 for an unknown buffer.

Defined in api.lua.

btv.buf.offset(bufnr, index)

btv.buf.offset(bufnr, index) -> integer [alias nvim_buf_get_offset]: the byte offset at which 0-based line index starts — the sum of every preceding line’s bytes plus its newline. index == line_count gives the buffer’s total byte length. Returns -1 for an unknown buffer.

Defined in api.lua.

btv.buf.text(bufnr, start_row, start_col, end_row, end_col, _opts)

btv.buf.text(bufnr, start_row, start_col, end_row, end_col[, opts]) -> lines [alias nvim_buf_get_text]: the text of bufnr spanning (start_row, start_col) up to (end_row, end_col), returned as a list of lines (the span split on newlines). Rows are 0-based; columns are 0-based byte indices into their line; the end position is exclusive. Use this for a sub-line span — use btv.buf.lines for whole lines.

Defined in api.lua.

btv.buf.lines(bufnr, start, end_, strict)

btv.buf.lines(bufnr, start, end_[, strict]) -> lines [alias nvim_buf_get_lines]: the lines of bufnr (0/nil = current) in the 0-based, end-EXCLUSIVE range [start, end_). Negative indices count back from the end (-1 is one past the last line), so (0, -1) is the whole buffer. With strict true an out-of-range index errors; otherwise indices clamp into range. Returns a list of strings, each without its trailing newline.

Defined in api.lua.

btv.buf.set_lines(bufnr, start, end_, strict, replacement)

btv.buf.set_lines(bufnr, start, end_, strict, replacement) -> promise [alias nvim_buf_set_lines]: replace lines [start, end_) of bufnr (0/nil = current) with replacement (a list of whole-line strings), 0-based and end-EXCLUSIVE. Negative indices count back from the end (-1 is one past the last line), so (0, -1, …) replaces the WHOLE buffer and (n, n, …, { "x" }) appends. With strict true an out-of-range index errors; otherwise indices clamp.

This is the editor’s ONE buffer-text mutation, and it is ASYNCHRONOUS: the Lua VM cannot touch the live buffer mid-chunk, so the edit is QUEUED and applied right after this chunk. The returned promise fulfils (with nil) on the next tick — once the edit has landed and the buffer mirror reflects it — so btv.await(btv.buf.set_lines(…)) is the point at which a following btv.buf.lines read sees the new content. The shape and the buffer’s modifiability are validated SYNCHRONOUSLY and raise (fail loud) before anything is queued: a non-table replacement, a non-string / newline-bearing line, an unknown buffer, a nomodifiable buffer, or (under strict) an out-of-range index. A read-only buffer KIND (terminal / btv.view / quickfix) is refused loudly server-side.

Defined in api.lua.

btv.buf.set_text(bufnr, start_row, start_col, end_row, end_col, replacement)

btv.buf.set_text(bufnr, start_row, start_col, end_row, end_col, replacement) -> promise [alias nvim_buf_set_text]: replace the CHARACTER range (start_row, start_col)..(end_row, end_col) of bufnr (0/nil = current) with replacement (a list of lines). 0-based; rows are line indices, columns are byte offsets within the line; the range is end-EXCLUSIVE. Unlike set_lines this is a precise SUB-LINE edit: set_text(0, 4, 0, 7, { "xy" }) replaces bytes 4..7 of line 0 in place, and replacement is spliced verbatim (its lines joined by \n, with NO trailing newline added). The precise edit a snippet / structural-edit plugin uses to swap a trigger word for an expanded body, or to update a mirror inline.

Like set_lines this is the editor’s ONE buffer-text mutation and is ASYNCHRONOUS: the edit is QUEUED and applied right after this chunk, and the returned promise fulfils (with nil) on the next tick — once the mirror reflects it — so btv.await(btv.buf.set_text(…)) is the point a following read sees the new content. The shape and modifiability are validated SYNCHRONOUSLY and raise before anything is queued (a non-table replacement, a non-string / newline-bearing line, an unknown or nomodifiable buffer); the server refuses a read-only KIND (terminal / btv.view / quickfix) and an inverted span loudly. Out-of-range coordinates clamp into the buffer.

Defined in api.lua.

btv.buf.attach(buffer, opts)

btv.buf.attach(buffer, { on_bytes = fn, on_reload = fn }) -> detach() [partial alias nvim_buf_attach]: subscribe to buffer’s (0/nil = current) change stream. At least one callback is required; an unsupported key fails loud (only on_bytes / on_reload are wired — on_lines / on_detach / on_changedtick stay deferred). Returns a detach() closure that removes this subscription; a callback may also return true to detach itself (neovim’s convention). The callbacks fire on the editor thread AFTER every mirror is consistent, so one may read btv.buf.lines / btv.buf.extmarks and see the post-edit state.

on_bytes is called on_bytes("bytes", bufnr, changedtick, start_row, start_col, start_byte, old_end_row, old_end_col, old_end_byte, new_end_row, new_end_col, new_end_byte) — the exact neovim tuple (rows 0-based, *_byte a total buffer byte offset, the old_* / new_* the removed / inserted extent). on_reload is called on_reload("reload", bufnr) when the whole rope was replaced (the delta stream is invalid — re-read the buffer whole).

Defined in api.lua.

btv.buf.search(bufnr, pattern, opts)

btv.buf.search(bufnr, pattern, opts) -> match | nil: find pattern in bufnr (0/nil = current) over the buffer mirror, line by line. The native counterpart to scanning lines in Lua — it runs the match in Rust (the regex crate or the vim engine) so a plugin can jump straight to a section (a conflict marker, a heading).

opts (all optional):

plain      = false             -- literal substring (ignores `engine`)
engine     = "pcre" | "vim"    -- regex dialect (default "pcre")
from       = { line=1, col=0 } -- start position: 1-based line, 0-based byte col
backward   = false             -- search upward from `from` instead of down
ignorecase = false             -- case-insensitive match

Returns nil when there is no match, else:

{ line, col, end_line, end_col, text, captures }

with line/end_line 1-based, col/end_col 0-based byte offsets (end exclusive), text the matched substring, and captures the submatch strings (\1.., "" for a group that didn’t participate). Matching is line-by-line, so a multi-line (\n-spanning) pattern is not supported.

Defined in api.lua.

btv.buf.set_extmark(buffer, ns, line, col, opts)

btv.buf.set_extmark(buffer, ns, line, col[, opts]) -> id [alias nvim_buf_set_extmark]: place (or update, via opts.id) an extmark in buffer under namespace ns (see btv.ns.create) at 0-based line / col (col a byte offset). opts carries the highlight-relevant attrs — end_row / end_col for a ranged mark, hl_group, priority, … — and an unsupported decoration key fails loud rather than being ignored. Returns the mark id. The mutation is queued for the server, but the mirror is written through, so a read later in this chunk sees it.

right_gravity (default true) and end_right_gravity (default false) set the anchor gravity — the direction each edge is dragged when text is inserted at it — and are HONORED against the live rope, not just stored. The default is a range that does NOT grow when you type at its edges (a highlight span); right_gravity = false with end_right_gravity = true makes an (even empty) range GROW to swallow text typed at either edge — the anchor shape a live snippet tabstop needs.

gutter_text (btv-native) fills the gutter text column, between the line numbers and the text, on the mark’s line: a non-empty list of { text, group? } chunks, such as a diff’s old and new line numbers. The column is as wide as the widest gutter_text in the buffer, in screen cells, but never wider than the window has left beside its other gutters, and a shorter one is padded on the right. A chunk with no group paints as LineNr (CursorLineNr on the cursor line). The highest priority mark on a line wins, then the higher mark id; they don’t concatenate. A malformed list raises, naming the chunk, and so does a newline, a tab or any other control character in the text.

btv.buf.set_extmark(0, ns, 4, 0, { gutter_text = { { "  12 ", "DiffDelete" }, { "     " } } })

Defined in api.lua.

btv.buf.del_extmark(buffer, ns, id)

btv.buf.del_extmark(buffer, ns, id) -> bool [alias nvim_buf_del_extmark]: remove mark id of namespace ns from buffer. Returns whether the mark existed.

Defined in api.lua.

btv.buf.clear_namespace(buffer, ns, line_start, line_end)

btv.buf.clear_namespace(buffer, ns, line_start, line_end) [alias nvim_buf_clear_namespace]: drop namespace ns’s extmarks in buffer whose line is in the 0-based range [line_start, line_end) — line_end == -1 means to the end of the buffer. ns == -1 clears every namespace a plugin made; the editor’s own highlights stay.

Defined in api.lua.

btv.buf.extmarks(buffer, ns, start, end_, opts)

btv.buf.extmarks(buffer, ns, start, end_[, opts]) -> list [alias nvim_buf_get_extmarks]: the extmarks of namespace ns in buffer within the position range start..end_ — each bound is 0 (buffer start), -1 (buffer end), or a {row, col} pair. Entries come in (row, col, id) order, each {id, row, col} (or {id, row, col, details} with opts.details). ns == -1 returns marks from every namespace. Reads the mirror, so it reflects marks set earlier in this chunk; positions are current as of chunk start.

Defined in api.lua.

btv.buf.set_option(buf, name, value)

btv.buf.set_option(buf, name, value) [alias nvim_buf_set_option]: set buffer-local option name to value on buf (0/nil = current). A pre-0.10 accessor kept because plugins call it pervasively (bufhidden / modifiable / filetype / buftype on scratch buffers); in new code prefer btv.option.set(name, value, { buf = buf }), which this wraps.

Defined in api.lua.

btv.buf.get_option(buf, name)

btv.buf.get_option(buf, name) -> value [alias nvim_buf_get_option]: read buffer- local option name from buf (0/nil = current) — the read counterpart of btv.buf.set_option. In new code prefer btv.option.get(name, { buf = buf }).

Defined in api.lua.

btv.buf.list(opts)

btv.buf.list([opts]) -> list of bufnr [alias nvim_list_bufs, which always lists all]: the buffer handles the mirror knows, ascending. By default every buffer across every layer (main area + all docks). Pass { focused = true } to list only the focused layer’s buffers — the per-region list (:ls is scoped the same way), so a dock reports just its own buffers and the main area just its own.

Defined in api.lua.

btv.buf.alternate()

btv.buf.alternate() -> bufnr | nil: the alternate buffer (vim’s #, the <C-^> target), or nil when there is none. This is the handle, not the name: vim.fn.expand("#") answers what :e # would reopen — a name that outlives a :bdelete of the buffer it came from — while this answers which open buffer a list flags # (what :ls and the buffers picker mark). A buffer that has been closed is nobody’s alternate here, so the two disagree exactly when they should.

Defined in api.lua.

btv.buf.getline(buf, lnum, lend)

btv.buf.getline(buf, lnum[, end]) [alias vim.fn.getbufline]: lines lnum..end (1-based inclusive) of a buffer, or just lnum when end is omitted. Wraps btv.buf.lines (0-based, end-exclusive). An out-of-range request yields {} (vim). buf may be a number or a btv.buf.nr expression ("%", a name); lnum / end may be "$" for the last line.

Defined in api.lua.

btv.buf.nr(expr)

btv.buf.nr(expr) [alias vim.fn.bufnr]: the buffer number for expr. "" / "%" / nil / 0 -> current buffer; "#" -> the alternate buffer (-1 when there is none); "$" -> the last (largest) buffer number; a string -> the loaded buffer whose name matches (exact, else suffix), -1 when none. Backed by the Phase-6 btv._bufs mirror.

Defined in api.lua.