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.filetype

btv.filetype.match(path)

btv.filetype.match(path) -> the filetype path’s name says, or nil: the same tables a buffer’s name goes through (exact basename, globs, extension), for a name no buffer has, like a file a plugin shows in a view. Nothing is read, so a #! line and btv.filetype.detect have no say.

Defined in filetype.lua.

btv.filetype.detect(src)

btv.filetype.detect(src): decide a buffer’s filetype from its content, or clear the sniffer with nil.

bemtvi’s built-in tables resolve a filetype from the file’s name, a path pattern, or its extension, and deliberately stop there: extensions neovim resolves by looking inside the file (.h C-vs-C++, .r, .v, .m, .pl) are omitted rather than guessed, because guessing in the core would be exactly the kind of heuristic that silently rots. This is the seam that answers them — your rule, not the editor’s guess.

src is a string of Lua source — an expression, not a function value — because it runs in the bounded compute sandbox: a second, pure VM with a wall-clock deadline, no editor state and no btv.*.

Three names are in scope:

name   the file's basename ("" for an unnamed buffer)
ext    its extension, without the dot ("" when it has none)
head   the first few lines of content (bounded, ~2KB)

Return a filetype string, or nil (or "") to decline — declining is a normal answer, and leaves the built-in name/pattern/extension tables to resolve it as before. A returned filetype wins over them, which is what makes the .h case work:

btv.filetype.detect([[
  ext == "h" and (head:find("template", 1, true) or head:find("::", 1, true))
    and "cpp" or nil
]])

The sandbox is stateless: nothing carries from one call to the next, and assigning a global raises. That is deliberate — no call shape is a clean once-per-item traversal (:s re-runs on every keystroke of the live preview, a foldexpr sees only the rows an edit touched, the picker scorer only the top survivors, foldtext is memoized), so an accumulator would be quietly wrong. It runs once per buffer, and its verdict is stored as that buffer’s explicit filetype — the same thing :setf writes — so nothing on the render path ever calls back into the sandbox.

An expression that errors, exceeds its deadline, or returns a non-string reports once and is then uninstalled.

Defined in filetype.lua.