Generated Computers
Locals: an Apple Silicon engine that runs local LLMs up to 3x faster. A product of Generated Computers.
翻译准备中
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A fast and extensible vim-style editor
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗bemtvi (pronounced ben-tee-vee) is a modal, vim-style editor architected from scratch — modal editing, Lua plugins, treesitter highlighting, and LSP, built on a fully async, client-server design.
You may be thinking: why another editor instead of using or improving neovim?
Neovim is amazing. It was my main editor for a while, and bemtvi owes it a lot: the client-server split, Lua as the config language, built-in treesitter and LSP. But some of neovim's limits come from decisions built into its core, and rewriting parts of it in faster C or Rust won't remove them. I tried working around them. In the end, fixing them meant starting again from a clean slate.
The problem isn't that Lua is slow. LuaJIT is fast. The problem is where
neovim runs it. Plugin Lua runs on the editor's main loop and reads and changes
live editor state directly. Some of it runs during redraw: decoration
providers and %! statusline expressions are called while the screen is being
drawn. Some of it blocks: vim.wait pumps the event loop, getcharstr() waits
for a key. So one slow plugin, or one big decoration pass, delays every frame
and every keystroke. Neovim can't change this without breaking the plugins that
depend on it.
bemtvi turns this around:
Neovim doesn't separate its core from file and process I/O. Over the network you have two options. Run neovim on the remote
machine (with ssh or --remote-ui), and every keystroke makes a round trip.
Or edit locally over scp:///sshfs, and you lose the remote's LSP, terminals,
and project tools. A VS Code Remote-style experience has been a top community
request for years (neovim#21635);
the proposed route is still a remote UI, because editing locally against a
remote host would need a VFS layer the core doesn't have.
bemtvi has an edit-host / daemon split. The editor, your config, and your
plugins run locally, so typing is always instant. bemtvi --daemon on the
remote machine provides the file system, processes, terminals, LSP servers, and
file watching. :connect user@host starts that daemon over ssh. A dropped link
reconnects without losing the session. The web build uses the same split from a
browser tab. Remote is a first-class target: anything that works locally works
over the wire.
Vim was designed for a grid of terminal cells. Neovim has added a lot since
then: floating windows, extmarks, virtual text, and ext_* UI events. But the
basic unit a plugin works with is still a buffer in a window. Every picker,
popup menu, file tree, and dashboard is a scratch buffer in a float, with
borders, padding, and icons drawn out of text and highlight groups, and plugins
each re-solve the same layout, focus, and teardown problems.
In bemtvi, rich UI is part of the editor. The picker (with preview), completion
menu, statusline segments, tree docks, and btv.ui.input/select/confirm
are server-owned widgets. Clients receive a semantic View (not a raw cell
grid), so a terminal and a native GPU GUI can each render the same frame well.
Neovim has removed a lot of vim's legacy, but its compatibility with vim is
deliberate: Vimscript, the huge vim.fn inventory, vim regex, and decades of
option semantics all have to keep working, because users and plugins rely on
them. That's a reasonable commitment for neovim. It also limits what neovim can
change.
bemtvi keeps vim's keystrokes (modes, motions, operators, ex-commands, and
options behave the way your fingers expect) and drops everything else.
There's no Vimscript. Config and plugins use one designed Lua API, btv.*,
with a small list of vim.* aliases for muscle memory. Search uses PCRE syntax
by default, and vim-flavored regex is opt-in.
Neovim officially supports Windows, but the ecosystem assumes a Unix machine.
Plugins shell out to POSIX tools and quoting rules. Grammar installers expect a
C compiler on PATH. Many features silently depend on git, make, rg, or
sh. Everything can work on Windows, but getting there takes a pile of
workarounds.
bemtvi avoids depending on external tools in the first place:
git binary is needed.:TSInstall builds grammars with a pinned, checksum-verified Zig toolchain
that bemtvi downloads itself, so no C compiler is needed.:terminal runs on ConPTY.The same binary and the same config behave the same way on Windows, macOS, and Linux.
Neovim's plugin ecosystem is excellent, but it has limits of its own. Writing a
good neovim plugin is hard: you have to know the API, the vim.fn inventory,
the event model, and a lot of undocumented edge cases.
Plugin authors often don't shy away
from breaking compatibility, so a config that worked last month can break after an
update, and keeping a setup working takes regular effort.
bemtvi plugins are designed so that an AI agent can write them. There's one
extensive, documented API (btv.*). The book
documents every function, and examples/ has over a hundred
runnable, tested examples. Plugins test themselves with the built-in btv.test
runner (bemtvi --test-plugin <dir>), so an agent can check that what it wrote
actually works. With all of that, any AI agent can build almost any plugin you
might want in a couple of hours, made 100% according to your needs.
I'm very grateful to neovim. It's an awesome piece of software, and bemtvi wouldn't exist without the path it opened. But these are structural issues, not missing features. Sometimes the only real fix is a clean slate, built with everything neovim taught us.
bemtvi is a headless, asynchronous editor server with thin UI clients
talking over msgpack-RPC. The editor logic lives in one place; the
terminal UI and the GPU GUI are just two clients of the same protocol.
It speaks vim at the keyboard: keystrokes, modes, ex-commands, and options
track vim/neovim's observable editing behavior. Everything
else is bemtvi's own: configuration and plugins target bemtvi's btv.* Lua API
(design), where the server owns
every UI surface and plugins provide data and behavior. Colorschemes are
bemtvi's own too — pure-Lua modules that fill the highlight registry through the
btv highlight API.
Test the client-only live demo at https://bemtvi-demo.netlify.app. Use the :eo
command to open a local file or :luao to load a local lua file as config.
Use :setf LANG to activate treesitter if not auto detected or :TSInstall to
install the highlighter for your chosen language.
📖 The bemtvi Book is the full
documentation — the user guide, the "beyond vim" feature guides, the plugin
development chapters, and the complete btv.* API reference, generated straight
from the Lua prelude so it can't drift from the running editor.
Grab a pre-built binary from the latest release
(or the rolling edge
prerelease built from main). Binaries are published for five targets:
| OS | Terminal editor (bemtvi) | GUI (bemtvi-gui) |
|---|---|---|
| Linux | x86_64 / aarch64 (.tar.gz, glibc ≥ 2.17) | x86_64 / aarch64 (.tar.gz or .AppImage) |
| macOS | x86_64 / aarch64 (.pkg, signed & notarized) | x86_64 / aarch64 (.dmg) |
| Windows | x86_64 (.zip) | x86_64 (.zip) |
The GUI uses each platform's own toolkit through mitsuami: on Linux it needs GTK 4.10+ and libadwaita 1.4+, and on Windows the Windows App Runtime 2.4+.
Then run it on a file (the argument is optional):
bemtvi file.txt # terminal editor
bemtvi-gui file.txt # native GUI (mitsuami + wgpu)
Downloads ship with checksums and SLSA build provenance — see docs/verifying-downloads.md to verify them.
Truecolor terminal recommended. bemtvi emits 24-bit color escapes; use a modern terminal with truecolor support.
Distinct modified keys need the kitty keyboard protocol. Chords the legacy terminal encoding can't tell apart —
<C-i>vs<Tab>,<C-m>vs<CR>,<C-[>vs<Esc>, and combos like<S-CR>/<C-CR>— are only distinguishable when the terminal speaks the kitty keyboard protocol. bemtvi turns it on automatically when the terminal advertises support: kitty, Ghostty, and foot enable it by default, but WezTerm needsenable_kitty_keyboard = true. Without it those chords fold onto their legacy twin (<C-i>acts as<Tab>), exactly like classic vim — so bindings still work, they just can't be told apart. (Unlike neovim, bemtvi's terminal layer only speaks the kitty protocol, not xterm's oldermodifyOtherKeys.) Force the decision either way withBEMTVI_KITTY_KEYBOARD=1/=0.
The clipboard works over ssh.
"+y/"*yuse the real host clipboard when it can actually reach you (the Windows clipboard,pbcopy,wl-copy,xclip/xselwith a display,tmux). On a remote machine none can — so bemtvi asks your terminal to set its own clipboard with an OSC 52 escape, which travels back down the ssh connection. It is used only when the terminal advertises support (queried at startup), so a yank never silently copies into a void — with one exception: inside tmux or screen the multiplexer answers the query itself and can only speak for itself, so its silence is taken as "yes" (both forward the escape outward by default). Force the decision either way withBEMTVI_OSC52=1/=0. Pasting into bemtvi from another app on your machine is your terminal's own paste (Ctrl+Shift+V, middle-click) — reading the clipboard back over OSC 52 is a round trip most terminals refuse, so"+ppastes what this session copied.
Ctrl+C/Ctrl+Vwork out of the box, on the system clipboard. They ship as built-in keymaps:<C-c>copies the visual selection,<C-v>pastes — at the cursor in normal mode, at the caret in insert mode, and into the line being typed on the command line. Nothing vim-ish is displaced (bemtvi has no blockwise visual mode for<C-v>to open and noi_CTRL-Vliteral insert), and each has a<C-S-…>twin for the clients that can tell the two apart. They are registered as defaults, so your own map wins and an empty function turns one off.
The terminal editor is the whole thing in one binary: it embeds the server on its own thread and runs the client on the main thread, joined over the same msgpack-RPC the UI clients speak.
You need a Rust toolchain. Then:
# Build and run (the file argument is optional)
cargo build --release
./target/release/bemtvi file.txt
# …or straight from cargo
cargo run -p bemtvi -- file.txt
# the native GUI
cargo run -p bemtvi-gui -- file.txt
bemtvi-edithost) — runs entirely in the browserbemtvi-edithost compiles the whole editor — bemtvi-core + the PUC Lua 5.4
VM + the full server tick — to WebAssembly (via emscripten) and drives it in a
Web Worker, fully client-side, no server. The page is the renderer and
input layer; the editor (including your init.lua) runs in the tab. File open/save
go through the browser's File System Access API (:eo / :wo, the in-browser
analogue of the GUI's dialogs) or persist to OPFS — so you really edit local
files (no upload, no backend), and a static host is enough to put it online. It
can also reach a real bemtvi --daemon over WebTransport for remote access,
vscode server style. Opt into btv.shada.save_layout(true) and a browser session
comes back the way you left it — windows, tabs, buffers, and plugin views
restored from the origin's OPFS.
It's a separate, wasm-targeted crate, deliberately excluded from the Cargo
workspace (so cargo build/test --workspace never touches it). Build it with
its own script (needs the emscripten emcc and Node):
rustup target add wasm32-unknown-emscripten
crates/bemtvi-edithost/build.sh # → dist/eh.{mjs,wasm} + web/vendor/
node crates/bemtvi-edithost/web/serve.mjs # cross-origin-isolated dev server
See crates/bemtvi-edithost/README.md for the
full architecture (the Worker run loop, the OPFS/daemon filesystem legs, and the
client-side tree-sitter highlighter).
u / <C-r> walk the tree, :undo {N} jumps
across branches, and g- / g+ / :earlier / :later walk the states in the
order they were made — by count, by time (:earlier 10m) or by file write
(:earlier 1f returns to what is on disk). :undolist lists the branches and
vim.fn.undotree() exposes the whole thing; 'undolevels' bounds what the
(cheap, full-rope) snapshots retain.:e/:b/:ls, the
<C-w> split family (:split/:vsplit, focus, resize, close), tab pages
(gt/:tabnew/<C-w>T), and floating windows (nvim_open_win with borders
and titles). All four laststatus modes ship.wrap lays long lines across screen
rows with display-line motions (gj/gk, g0/g^/g$), and scrolling
animates smoothly across screen rows. The window-local
number/relativenumber, numberwidth, signcolumn, cursorline,
breakindent/showbreak, and horizontal-scroll options are honored.zo/zc/za, zR/zM, zj/zk, and a fold column, with
four fold methods selected by foldmethod: manual (zf), indent,
marker (literal {{{/}}}), and expr — whose foldexpr can be the
native tree-sitter expression (v:lua.btv.treesitter.foldexpr()), a generic
Lua expression, or the LSP marker driving textDocument/foldingRange./ and ? with /, ,
and , plus with the //// flags. Two
interchangeable regex dialects, selected by the option:
(the default) — standard perl-compatible patterns via the Rust crate,
with // replacements — or , the real vim "magic"
dialect ( groups, /, /, / back-refs, the
/ case modifiers, …) matched by the embedded vim regexp engine.If you know vim, your muscle memory transfers. Concretely, what's wired today:
h j k l, words (w W b B e E), line (0 ^ $), find-char
(f F t T + ; ,), go-to-line (gg G), search (/ ? n N, plus * # g* g#),
marks (`/'), and the soft-wrap display motions (gj gk g0 g^ g$).
Viewport z-commands (zt zz zb, z./z-) reposition the screen.iw aw iW aW, quotes (i" a", i' a', and backtick),
brackets (i( a(, i{ a{, i[ a[, i< a<, plus ib/ab and iB/aB),
paragraph (ip ap), sentence (is as), and the tree-sitter objects
(if/af function, ia/aa argument, ic/ac comment, it/at
type/class).d c y, paste (p P), shift (>>/<<, >motion/<motion,
>ip), reindent (=, ==, =motion, gg=G), comment toggle (gc/gcc,
with a per-filetype commentstring), replace (r), case-toggle (~), join
(J), and the line shortcuts (x X D C s). Counts and dot-repeat (.) work
throughout.v) and linewise (V), o/O to swap ends,
operators and text objects over a selection, and p/P to put a register
over it. (Blockwise <C-v> is a deliberate non-goal — use
multi-cursor instead.)"a–"z), append ("A–"Z), the yank/delete ring
(–), small-delete (), black-hole (), the read-only specials
( ), the system clipboard ( ), and the expression
register ( / ), whose contents are a Lua expression the
evaluates.Not yet wired (see the roadmap): % match-pair,
the paragraph/sentence motions ({ } ( )), the screen motions (H M L), the
gu/gU/g~ case operators, gq reflow, and HTML tag objects (in bemtvi
it/at are the tree-sitter type/class object). Macros are wired, but on
<F2>/<F3> rather than q/@ — see
keyboard macros.
Things bemtvi has that neovim doesn't. The full index — with a guide per feature — is Beyond vim: what bemtvi adds; the highlights:
Helix mode (selection-first editing). An opt-in noun→verb model
(docs/features/helix-mode.md) alongside the vim
grammar: :helix (or btv.helix.enable()) enters HELIX, where every cursor is
a persistent anchor..head selection, motions re-select as you move (w
selects the next word), and verbs act on the selection immediately with no
operator-pending wait (wd deletes a word). Multi-selection is the default;
match mode (m + tree-sitter text objects), surround, regex-select (s/S),
per-selection insert/paste, and content rotate/align are all there. The engine
is native; the key layout is the bundled btv.helix plugin, so every verb is
rebindable by name (btv.helix.actions.*).
Multi-cursor (Helix-style placement mode). <A-c> enters a MULTICURSOR
placement mode and drops a cursor at the current position. There, motions move
only the active cursor — you navigate (including /-search) and drop more
cursors with c, or place a run with {count}c{motion} (e.g. 10cj). <Esc>
keeps the placed cursors and returns to Normal, where motions and edits act on
every cursor at once; a second <Esc> collapses back to one. Cursors are
extmarks, so they stay correct across edits and ride undo/redo for free.
A native GUI client. bemtvi-gui (mitsuami + wgpu) is a first-party GPU
client — not a third-party frontend — rendering the same View the terminal
client does. It wires the OS's native file dialogs into the command line
(a GUI-only affordance, on sessions whose files are on this machine: the editor
recognizes these lines as it runs them and asks the GUI for the dialog):
…o open family — :eo, :spo, :vso, :tabeo, :newo, :vnewo,
and a bare :e (an alias of :eo) — pops a system open dialog, then
runs the base command (:e/:sp/:vs/:tabe/…) on the chosen file,
preserving its edit / split / tab semantics.bemtvi reads a Lua config. On startup it resolves a config
directory — the first of $BEMTVI_CONFIG, $XDG_CONFIG_HOME/bemtvi, or
~/.config/bemtvi — and sources <config>/init.lua before the first frame. The
runtimepath is that dir plus every pack/*/start/* entry under it, so a
colorscheme checkout is drop-in:
~/.config/bemtvi/
├── init.lua # sourced at startup
└── pack/
└── plugins/
└── start/
└── catppuccin/ # a plugin; its lua/ and colors/ are found here
-- ~/.config/bemtvi/init.lua
require("catppuccin").setup({ flavour = "mocha" })
vim.cmd.colorscheme("catppuccin")
The editor's own config API is the btv.* namespace
(design). The only vim.* is a
closed whitelist of muscle-memory aliases (vim.g,
vim.o/vim.opt, vim.cmd, vim.keymap.set, autocmds, vim.notify, and
friends — 1:1 over btv, so config can be written in familiar spellings like
vim.g.mapleader = " " and vim.o.number = true; the
full whitelist is in
ADR 0002). A colorscheme reaches
for a handful of those aliases (notably the nvim_set_hl highlight helper) and
nothing more.
The examples/ directory has ~105 self-contained, end-to-end-verified
configs — one per feature (treesitter, LSP, floats, registers, tabs, mouse,
statusline, completion, picker, snippets, decor, docks, quickfix, image
previews, …). Each is a config dir you point bemtvi at:
BEMTVI_CONFIG=examples/treesitter cargo run -p bemtvi -- examples/treesitter/sample.rs
A plugin is pure Lua over the btv.* API — a lua/<name>/init.lua module that
exposes setup(opts) and wires keymaps, commands, autocmds, and UI through btv.*
(the server owns the surfaces; the plugin supplies data and behavior). Install one
by declaring it with the built-in manager and running :PluginSync:
btv.plugins({
{ "bemtvi/bemtvi-keys-helper",
config = function() require("bemtvi-keys-helper").setup({}) end },
})
The first-party set — a statusline, a file tree, a which-key popup, :help,
lspconfig, snippets, a diff viewer, a DAP debugger, and more — is catalogued in
Recommended plugins, each with the spec to
paste into your config.
Plugins are testable end-to-end with the native framework — write test/*_spec.lua
and run bemtvi --test-plugin .. See the full guides:
Writing bemtvi plugins and
Testing bemtvi plugins.
Writing one with a coding agent? Install
bemtvi-plugin-skills
(npx skills add https://github.com/bemtvi/bemtvi-plugin-skills) — agent skills
covering the btv.* model and one surface each, so the agent doesn't reach for
neovim APIs that don't exist here.
The authoritative design doc is docs/architecture.md —
read it first for the crate layout, the client-server model, the RPC + View
protocols, the rope text model, the Lua bridge, treesitter, LSP, and the roadmap.
(It is also a chapter of the book, which
book/gen/generate.py renders from docs/ plus the Lua prelude.)
The short version:
| Crate | Responsibility |
|---|---|
bemtvi-core | The editor model — buffers, modes, motions, operators, ex-commands, undo, and the renderable View. Pure & synchronous. |
bemtvi-server | The headless server — owns the core + Lua, hosts the nvim_* API, runs the async main loop. |
bemtvi-rpc | Async msgpack-RPC transport (bemtvi's own protocol). |
bemtvi-lua | Embedded PUC Lua 5.4 runtime, the btv.* API prelude, and the vim.* glue. |
bemtvi-ts | The in-process treesitter engine (loads grammars, parses incrementally). |
bemtvi-lsp | The native LSP client (protocol, transport, manager) — bemtvi's own stdio spawning. |
bemtvi-regex | The vendored vim regexp engine (regexp.c as C), shared by search and :s. |
bemtvi-tui | The terminal UI client (ratatui + crossterm). Owns no editor state. |
bemtvi-gpu | The native GUI client with no toolkit: wgpu + glyphon renderer, sessions, the RPC IO thread. |
bemtvi-mitsuami | The GUI client as a mitsuami view an app embeds (Editor, EditorHandle). |
bemtvi-gui | The native GUI binary: a mitsuami window around bemtvi-mitsuami's view. |
bemtvi-edithost | Fully client-side WebAssembly build of the whole editor (core + Lua + server tick, in a Web Worker; no server). Excluded from the workspace. |
bemtvi-view | Frontend-neutral decode/input layer shared by the native clients. |
bemtvi | The nvim-style entry point: wires an embedded server + the TUI client. |
The editor core is pure, synchronous, and !Send; all async, RPC, and Lua live
above it, so every front end shares identical editing behavior.
cargo build # debug build of the whole workspace
cargo test --workspace # everything is a black-box integration test
cargo test -p bemtvi-server --test editing <name> # run a single test by substring
cargo fmt --all # format
cargo clippy --all-targets -- -D warnings # lint
lua54, baked
into the shared mlua dep) — there is no backend feature to select or thread.
Lint and test on default features.cargo fmt --check + cargo clippy -D warnings.
After a fresh clone, run pre-commit install once.bemtvi tracks vim/neovim's observable editing behavior, but it is a fresh
rust-native implementation, not a port — so some things differ by design and many
things simply aren't built yet. The canonical, always-current list of gaps lives
in docs/known-approximations.md (and, more
precisely, in INCOMPLETE: comments and btv._notimpl raises in the code). The
highlights:
btv.* is the only API.
Configuration and plugins target bemtvi's own btv.* API, built around its
snapshot + effect-queue, client-server design. Colorschemes are bemtvi's
own — Lua modules that fill the highlight registry through the btv highlight
API. A closed whitelist of muscle-memory aliases (vim.g, vim.o/vim.opt,
vim.cmd, vim.keymap.set, autocmds / user commands / nvim_set_hl,
vim.notify, the vim.tbl_*-style helpers, … — canonical list in
ADR 0002)
maps 1:1 onto btv so config can be written in familiar spellings —
see the design and
ADR 0002..vim files, the
eval.c language) is an explicit non-goal. vim.fn.* is a hand-written
set of helper aliases, not an interpreter. Colorschemes must be Lua.View. There is no ext_linegrid / grid
protocol; clients receive a semantic View and lay out their own widgets. The
client-protocol verbs are btv_* (btv_input, btv_ui_attach, …); the
editing-API methods keep neovim-faithful spellings (nvim_buf_get_lines, …)
as muscle-memory names, but the whole surface is bemtvi's own protocol.syntax.vim
highlighter — all highlighting comes from treesitter grammars.<C-v>). Charwise and linewise visual modes are
supported; blockwise selection is a deliberate non-goal, not a roadmap item.
Use the multicursor mode instead.% match-pair, the paragraph/sentence motions
({ } ( )), and the screen motions (H M L) aren't wired yet.wrap, number/relativenumber,
numberwidth, signcolumn, cursorline, colorcolumn,
breakindent/showbreak, the fold options
(foldenable/foldcolumn/foldlevel), and the horizontal-scroll
options are honored; the rest (cursorcolumn, list, …) are not.:set knows just over 80 options — the search booleans,
the window options above, the buffer-local indentation options plus
commentstring, the fold / mouse / statusline / encoding families, the
editing-feedback pair showcmd / report, undolevels, and
more — and fails loud (E518) on anything else; the bulk of vim's hundreds
of options are missing. (The Lua surface is lenient instead: a vim.o/btv.o
write to an unmodeled name is recorded with a warning but inert.)gu/gU/g~ case operators and the :map-family ex-commands. Case
is toggled via ~; keymaps are set via vim.keymap.set / nvim_set_keymap.
Both intentionally postponed.indentexpr) is deferred. Each approximation is
tagged at its call site.A core project rule: everything unimplemented fails loud. An unsupported path
raises with the name of what's missing (bemtvi: not implemented: <name>) rather
than quietly returning a fake value — so a half-working feature never masquerades
as a whole one. You can enumerate exactly what a given config trips at runtime.
I started this project on a whim just to test the limits of current day vibe-coding. I wanted to see how far I could go, but I didn't expect the answer to turn out to be as far as I wanted. I got so excited I decided to speed run it for enough features to use it as my daily driver. I love neovim and I used it as my daily driver, so bemtvi is heavily inspired by it and my favorite plugins.
I've been using mainly Claude Code and the experience has been pretty frustration free. Of course Claude didn't do all of it by itself, I'm a pretty seasoned software engineer, so I constantly steered it in the "right" direction and I also made all architecture decisions, although with Claude's help. I also didn't review the code, apart from skimming it while it was being generated as a coarse sanity check, and interrupting and correcting Claude on egregious mistakes. At times I had 4 Claude Code instances implementing 4 features in parallel, thankfully Claude doesn't care about conflicts. lol. It's frankly insane how fast I could get to a fully working editor.
I intend to keep this code-base entirely 100% free of human written code. Create an issue if you have feature requests or bug reports. Claude doesn't care about the quality of your message, so write whatever you want. lol
I won't accept PRs, only feature requests and bug reports by text description. As I can have a better quality control this way.
The colorschemes ported from neovim (crates/bemtvi-server/runtime/colors/, all
but bemtvi and neutral) keep their authors' licences, mostly the Vim license;
see that directory's README for
credits and LICENSE.txt for
the licence text.
vendor/neovim is a git submodule kept purely as a behavioral/source-layout
reference — it is never built or linked, and is not needed to build bemtvi.
nNhlsearchincsearch:sgiInc'regexsyntax'regex$0$1${name}vim\(\)\<\>\zs\ze&\1\u\U<Tab> on the : line opens a wildmenu over
commands, file paths, options, and plugin/user commands, with a docs sidebar
and live selection preview."+/"*), buffer-local and global marks, the special marks, and '{mark}
ranges.:TSInstall <lang> to fetch + compile a grammar on
demand. A full tree-scripting Lua API (parsers, queries, predicates,
injections) runs on bemtvi's primitives. Grammars load off the editor thread,
so opening a file in a language you haven't used yet never stalls a keystroke.Makefile, .bashrc, go.mod, .editorconfig), a small set of
globs (.env.*, Dockerfile.*, config under .ssh/), and finally the
interpreter named by a #! line for an extensionless script. Detection is a
derive, so a rename retypes the buffer immediately.'showcmd' paints the partly-typed command
(register, counts, operator, a withheld mapping prefix) or the selection size in
the corner, and 'report' echoes vim's line-count messages once a delete, yank,
paste or shift passes the threshold.require/init.lua, keymaps, user
commands, autocmds, an async event loop (timers, process spawn, scheduling),
and a colorscheme runtime — a colorscheme is a pure-Lua module that fills
the highlight registry (see Intentional deviations).$/progress, and
didChangeWatchedFiles are wired. Every cursor verb merges across the servers
attached to a buffer, in a stated priority order. Ready-made configs for 407
language servers — nvim-lspconfig reimplemented natively, nothing blocking —
ship as the
bemtvi-lspconfig plugin.'mousemodel' menus, and middle-click paste, in both clients.btv.* native plugin surfaces — the server-owned extensibility API is
built: a completion engine (btv.complete, with buffer / LSP / snippet
sources), a fuzzy picker (btv.picker, with a preview pane), a snippet engine
(btv.snippet, LSP snippet syntax + tabstop navigation), composable statusline
segments (btv.statusline, lualine-shaped, per-window), viewport decorations
(btv.decor, off-tick providers driving extmarks / virtual text, plus a
frame-time btv.decor.expr paint for decorations that are a pure function of
the line), and the floating-widget UI layer
(btv.ui.input/select/confirm/float, promise-based). Every widget's keys
are rebindable through the real keymap engine. See examples/.:terminal) with a
vt100 emulation layer, end-to-end backpressure for runaway output, and
scrollback, in both native clients and the web build.errorformat
parsing (or a Lua line parser in its place, btv.qf.parse) and rows you can
render yourself (btv.qf.text, vim's 'quickfixtextfunc'), window-local
location lists (:lopen/:lvimgrep), named and dynamic (function-sourced)
lists with picker integration, and VSCode-style permanent edge docks
(btv.dock) with per-region tablines and spatial <C-w><C-w> navigation.bemtvi --workspace <dir> opens a directory
as a session that persists its tabs, windows, unnamed buffers, and
per-workspace option overrides (btv.wso) across restarts. bemtvi --daemon
runs the editor as a headless server a thin client attaches to over
QUIC/WebTransport (local or remote, vscode-server style), choosing local or
remote config on connect.<img> in the web build (btv.o.imagepreview)."0"9"-"_"%"/":"."+"*"=<C-r>=m / ` / ', the automatic special marks, the
jumplist (<C-o> / <C-i>), and the changelist (g; / g,).<C-r> register insert (<C-r><C-w> for the word under the
cursor), auto-indent, native completion (<C-n>/<C-p>/<C-y>), and
snippets with tabstop navigation.<F2>{reg}, replay with {count}<F3>{reg}
(why not q/@). A recording captures the keys you
typed, mappings included, and replays through the keymap matcher, so a macro
over a Lua keymap re-fires it.:e :w :q (and the split/tab variants), :s (with the
g i I n c flags), :g/:v, :d :m :t, :normal, :put, :undo/:redo,
the undo-travel family (:undolist, :earlier/:later),
:set/:setlocal/:setglobal, the listings (:marks :registers :jumps :changes :messages), and :vimgrep/:lvimgrep.:e <dir> (and :sp/:vs/:tabe <dir>) pops the open dialog in that
directory instead of the in-window listing.:wo (and a bare :w on an unnamed buffer) pops a system save dialog
and writes to the chosen path.The pixel clients also render Nerd Font icons at the size the font drew
them. An icon is designed a full em wide where a coding font's cell is about
0.6 em, so a one-cell reservation either shrinks it or lets it collide; when
the cell to its right holds a space there is nothing to paint over, so the
glyph may take that room. btv.o.guiglyphoverflow picks the mode —
never / always / when-followed-by-space — and it is a server option
relayed on the redraw, so it reaches the browser too, which has no CLI to carry
a flag (--glyph-overflow / BEMTVI_GUI_GLYPH_OVERFLOW for the GUI,
?glyph-overflow= for the page, are the client-side defaults). Only the ink
moves: the glyph still occupies one cell, so every column the server computes
is untouched.
The native GUI can let the window's backdrop show through the editor — its
Mica on Windows, the desktop through vibrancy on macOS:
btv.o.guiopacity (0–100, default 100) is how opaque it paints
the colorscheme's Normal background. Every other background — selections,
search matches, the cursor line, line tints, status lines, a dock's own
Normal, floats and popups — follows btv.o.guihlopacity instead (same range,
same default); cursors, underlines and the text itself always stay solid. A
float clears the editor beneath it rather than blending over it, so below
100 its text sits on the backdrop and its own thinned background, never on
the buffer underneath. Both need Direct3D 12 on Windows (the GUI's default) or
Metal on macOS; with WGPU_BACKEND=vulkan, or on Linux, a value below 100 is
an error in the message line and the backgrounds stay solid. The TUI and the browser
have no backdrop and ignore both. See examples/gui-opacity.
tabstop defaults to 4, with
shiftwidth=0 and softtabstop=-1 (both "follow tabstop"), so one knob drives
the indent width.select! pick for
fairness) rather than neovim's deterministic multiqueue order — TBD if it affects
correctness, or only which independent background source lands first.