btv.treesitter
btv.treesitter.foldexpr(_lnum)
No documentation comment in the prelude.
Defined in btv.lua.
btv.treesitter.highlight(lang, text)
btv.treesitter.highlight(lang, text) -> promise of the tree-sitter highlight
spans for the off-buffer snippet text in language lang — the same stateless
highlighter (injections included) the picker preview uses, exposed so a plugin can
token-colour an arbitrary snippet without opening a buffer (the help window’s
>lua code blocks are the motivating case). Resolves with an array of
{ line = <0-based row>, col_start = <byte>, col_end = <byte>, group = <capture> }
(col_end exclusive); the columns are byte offsets within each snippet line, which
a caller maps to extmark columns. Resolve-only: a language with no installed grammar
(and the wasm serverless build, whose highlighter is JS-side) settles with an empty
array, so the caller simply paints nothing.
Defined in btv.lua.
btv.treesitter.reload(lang)
btv.treesitter.reload(lang): look for lang’s grammar again, and repaint what’s open in
it. An editor remembers that a language has no grammar, and :TSInstall only tells the
editor that ran it (TSInstalled), so this is for another editor that installed one: an app
embedding several hears of it from the one, and tells the rest. Native only: the browser
has one highlighter, outside the editor, and reports that this isn’t there.
Defined in btv.lua.
btv.treesitter.installed()
btv.treesitter.installed() -> promise of the list of languages with a grammar that
can highlight: on native, a parser in bemtvi’s data dir or a borrowed neovim site/
with a highlights query (beside the parser, on the runtimepath, or from
btv.treesitter.set_query); in the browser, what its highlighter has. :TSInstall adds to it.
So a plugin can tell what’s missing before asking for it:
btv.treesitter.installed():next(function(langs)
if not btv.tbl.contains(langs, "rust") then
btv.cmd("TSInstall rust")
end
end)
Defined in btv.lua.
btv.treesitter.set_query(lang, name, text)
btv.treesitter.set_query(lang, name, text): use text as lang’s name query, in
place of its query files, in every buffer. name is one of highlights, indents,
injections, folds or textobjects. nil for text goes back to the files. A query
that won’t compile says so on the message line and changes nothing.
Not in the browser, whose highlighter is JS-side: it reports that on the message line.
Defined in btv.lua.
btv.treesitter.fragment_context(lang, templates)
btv.treesitter.fragment_context(lang, templates) — teach the fragment
highlighter how to make sense of a code block that is not a whole program.
The code blocks inside LSP documentation (hover, completion docs) are usually
fragments: a struct field, a bare statement, a body-less signature. Parsed as a
whole file they land in tree-sitter’s error recovery, which doesn’t merely
under-highlight them — it names constructs that aren’t in the text (Vec in
field: Vec<String> comes out @constructor). bemtvi’s fragment highlighter drops
those guesses; a framing is how it gets the real structure back instead.
templates is an ordered list of framings, each a string with one %s marking
where the snippet goes. A snippet that doesn’t parse on its own is tried inside
each in turn, and the first framing that parses cleanly wins — its highlight
spans are mapped back onto the snippet’s own lines and columns:
btv.treesitter.fragment_context("rust", {
"struct __btv {\n%s\n}", -- a field hover: `field: Vec<String>`
"fn __btv() {\n%s\n}", -- a statement or expression
})
Only a clean parse is accepted, so a framing that doesn’t fit costs one throwaway
parse and nothing else — the snippet falls through to the conservative repaint
(keywords, strings, numbers, comments, punctuation; no guessed constructs), which
is also where an annotation dialect ends up: lua_ls writes
function f(t: table) into a ```lua fence, and that is not a fragment of any
Lua program.
Indentation-sensitive languages. When a template’s %s follows only
whitespace on its line, that whitespace is the block level the fragment sits at,
not just an opener the first line continues — so every line of the fragment is
indented to match:
btv.treesitter.fragment_context("python", { "class __btv:\n %s" })
Without that a multi-line python fragment would be framed as a header, one indented line, and then a dedent — a syntax error rather than a block. The spans come back with the indent taken off every line, so the caller still sees the fragment’s own columns.
A same-line framing works too ("fn __btv() { return %s }") — there the prefix’s
width comes off the first line only. The wrapped text always ends in a newline,
because some grammars (go) treat a missing final terminator as a parse defect.
Two shapes the ladder is run for you on. A hover often carries the server’s
own display label in front of the code — pyright sends
(method) def join(self, x: str) -> str, tsserver (property) Foo.bar: number
— and that label is what stops an otherwise framable signature from framing. It
is peeled off, the ladder runs on the rest, and the label itself is painted as a
comment. And a block that is a list rather than one fragment — ty sends
every overload of a function as its own signature line — is resolved line by
line, each through its own ladder. Both are all-or-nothing: a peel or a split
that doesn’t end in a clean parse leaves no trace, and the block falls to the
repaint whole.
Calling this replaces the language’s list; passing {} turns the ladder off for
it. bemtvi ships defaults for rust, python, go, lua, javascript, typescript, tsx,
c, cpp, java, c_sharp, kotlin, swift, scala, dart, zig, ruby, php, elixir, css,
scss and json.
Defined in btv.lua.