██████╗ ██╗███╗   ██╗██╗   ██╗██╗███╗   ███╗
██╔══██╗██║████╗  ██║██║   ██║██║████╗ ████║
██████╔╝██║██╔██╗ ██║██║   ██║██║██╔████╔██║
██╔══██╗██║██║╚██╗██║╚██╗ ██╔╝██║██║╚██╔╝██║
██████╔╝██║██║ ╚████║ ╚████╔╝ ██║██║ ╚═╝ ██║
╚═════╝ ╚═╝╚═╝  ╚═══╝  ╚═══╝  ╚═╝╚═╝     ╚═╝

vim grammar.
batteries included.

the first vim IDE — vim-native, in one binary.

A modal TUI editor for people who want to stop maintaining their vim-based editor. Tree-sitter grammars, multi-server LSP, DAP debuggers, an integrated test runner across five toolchains, GitHub Copilot, a dozen formatters — already linked into one binary. No plugin manager. No config archeology.

$ brew install bgunnarsson/binvim/binvim
+--

§ WHY CHOOSE BINVIM

four reasons
§01

One binary, no plugins

curl | sh  ·  ~42 MB

Tree-sitter parsers, twenty-plus language servers, four DAP debuggers, GitHub Copilot, a dozen formatters — all linked into one binary.

  • Tree-sitter grammars compiled in — ship with binvim, no version drift after an upgrade
  • LSPs wired by extension: rust-analyzer, gopls, tsserver, pyright, clangd, jdtls, csharp-ls, kotlin-language-server, … (24 total)
  • Formatters dispatched per language: biome, csharpier, gofmt, ruff, clang-format, prettier, stylua, taplo, ktfmt + 8 more
  • DAP adapters auto-picked from the workspace: netcoredbg, dlv dap, debugpy, lldb-dap
  • curl | sh and you're done — ~42 MB, zero config required
§02

LSP is the editor

gd  ·  gr  ·  K  ·  :messages

Not a plugin layer bolted on top. The whole protocol surfaces through the same buffer, the same status line, the same key map.

  • Completion · hover · diagnostics · goto-def · references · rename · code-actions · signature-help · doc & workspace symbols
  • Inlay hints + semantic tokens layered over the tree-sitter pass — LSP modifiers paint let mut foo red, async fn lavender, std:: symbols sapphire
  • Document highlight on cursor settle, snippet expansion with $N / ${N:default} placeholders, multi-file WorkspaceEdit
  • Multi-server fan-out: Tailwind + Emmet attach on top of CSS / HTML / JSX / TSX / Vue / Svelte / Astro / Razor automatically
  • :messages captures window/showMessage + window/logMessage + raw stderr; :health flags stuck-at-init clients
  • gd · gr · <space>a · <space>r · K
§03

Batteries, included

^N  ·  sessions  ·  F5–F11

Multi-cursor, sessions, format-on-save, fuzzy pickers, debuggers, AI assist — present the moment you launch.

  • Real multi-cursor: Ctrl-N selects next occurrence (Sublime-style), Ctrl-click anchors extra cursors, Backspace / Enter mirror across every site
  • Sessions persist buffers + cursor + viewport on shutdown, restore on launch. Tab bar; H / L cycle
  • Window splits with per-buffer layouts. <C-w>v splits and opens the file picker so the new pane lands on a different file in one keystroke
  • Format-on-save dispatched by extension; .editorconfig rules apply too. :fmt or <space>f drives it manually
  • Four DAP debuggers built in. <space>ds picks the adapter from your workspace; F5 · F9 · F10 · F11 work like VS / Rider in every mode
  • Integrated test runner across five toolchains (cargo, vitest, pytest, go test, dotnet test) — :testnearest walks up for the enclosing test fn, results stream into an overlay, failures populate the quickfix list
  • GitHub Copilot opt-in: [copilot] enabled = true; accepts the ghost, accepts the LSP popup
  • Agent CLIs in a right-side pane: :claude, :codex, :opencode each open the matching tool in a dedupe'd PTY tab
§04

Just enough opinion

config.toml  ·  ~40 lines

Catppuccin Mocha out of the box. ~/.config/binvim/config.toml controls everything optional — every section optional, every key defaults sensibly.

  • Set just the background and the four chrome neutrals auto-derive by luminance — a one-line theme yields coherent chrome. See themes here
  • Namespaced colour overrides for fine control: notification.{info,warning,…} · git.{added,modified,deleted} · diagnostic.{error,warning,…} · tab.{active_bg,…}
  • [colors] · [start_page] · [whitespace] · [line_numbers] · [hover] · [lsp] · [copilot] — each block independent
  • ~40 lines of TOML if you want to override the palette, comment glyphs, or formatter list. Defaults work
  • No config file at all? Still loads. Catppuccin Mocha, sensible chrome, every binding wired

// caveat: binvim is opinionated and ships without a plugin API. If you need infinite extension, you want a different editor. binvim is for the developer who opened their config file last week and felt tired.

+--

§ FEATURES

26 cards · folded
01 / EDITING

Modal editing, faithfully

Normal · insert · visual (charwise, linewise, blockwise). Operators, text objects, marks, registers, dot-repeat, undo tree, macros. The grammar you already speak.

w b eiw a"ciw@q
02 / SYNTAX

Tree-sitter, compiled in

Rust, TS / TSX / JSX, JS, JSON, Go, Python, C / C++, Java, Ruby, PHP, Lua, TOML, Svelte, Zig, Nix, Elixir, Dockerfile, SQL, HTML, CSS, Markdown, C#, Razor, YAML, XML (incl. .csproj / .fsproj / .props), Bash, plus byte-level passes for .editorconfig and .gitignore. Grammars baked into the binary — upgrade binvim and your parsers come with it.

rustts/tsxgohtmlcssmdc#razoryamlxml.editorconfig
03 / LSP

Real LSP, multi-server + multi-root

Completion, hover, diagnostics, goto-def, find-references, rename, code-actions, signature-help, document & workspace symbols, code lens (virtual text above the anchor — server-driven, with a synthesised fallback for "Run test" / "Debug test" rows on languages whose servers don't ship the capability). Primary + auxiliary servers fan out per buffer — Tailwind and Emmet layer on top of CSS / HTML / JSX / TSX / Vue / Svelte / Astro / Razor automatically. Two dozen servers wired by extension, from rust-analyzer / tsserver / gopls to pyright / clangd / jdtls. Multi-root: opening files from a sibling project root attaches the new folder to the running server via workspace/didChangeWorkspaceFolders instead of spawning a second process — one rust-analyzer can hold both halves of a monorepo, and cross-workspace go-to-definition works. :workspaces dumps the attached folder set per client. :messages surfaces server logs in a severity-coloured overlay; :health flags stuck-at-init clients and shows per-kind pending-request + cache counts.

rust-analyzertsservergoplspyrightclangdjdtlscsharp-lstailwindemmetsemanticTokens/fulldocumentHighlightcodeLensworkspaceFolders:workspaces:messages
04 / REFACTOR

Rename, code actions, snippets

<space>r renames a symbol across every file the server knows about — and opens a modal preview overlay first, listing every edit with a per-row checkbox and a file:line snippet. j/k navigate, <Space> toggles, a/n flip all on/off, o jumps to the edit site, <Enter> applies only the enabled edits, <Esc> cancels — no more blind apply on a 40-file rename. <space>a opens code actions — quick fixes, organise imports, extract method, anything the LSP exposes. LSP snippet items get their $N / ${N:default} placeholders parsed and the cursor lands at $1. Multi-file WorkspaceEdit applier with workspace/applyEdit round-trip.

<space>rpreview<space>agr$1 $2 $0
05 / AI ASSIST

AI assist, opt-in

Two paths, neither baked in: inline via GitHub Copilot, conversational via terminal-native assistants. Copilot ships through copilot-language-server as an aux LSP — set [copilot] enabled = true in config.toml, device-flow auth surfaces in the status line, ghost completions render as muted italic on a ~250 ms idle pause. <Tab> accepts the ghost (wins over the LSP popup), <Enter> accepts the LSP popup. For chat, :claude / :codex / :opencode open the matching CLI in a right-side PTY pane. Each invocation spawns a fresh tab — multiple sessions per tool are supported, switched via the tab strip in the pane header. Shift-pair leader bindings extend this: <space>jc opens a Claude tab as-is, <space>jC opens Claude AND pre-types @<active-buffer cwd-relative path> into the input field once it's ready (same shift-pair for jx/jX Codex, jo/jO opencode). <space>jf focuses the pane; <space>jp hides/shows it without killing PTYs. binvim itself carries no HTTP client; networking and auth stay in the language server / external tool.

copilot:claude:codex:opencodejC / jX / jO@path handoff⇥ acceptdevice-flow
06 / MULTI-CURSOR

Real multi-cursor

Sublime-style. In Visual-char, Ctrl-N finds the next literal occurrence and adds it as a parallel selection — repeat to keep selecting. c deletes them all and drops a mirrored cursor at every former start; typing, Backspace, and Enter all mirror at every site simultaneously. Ctrl-click in Normal mode anchors extra cursors at click positions.

^N^clickc · d · y↵ mirror
07 / FORMAT

Format on save

One canonical formatter per language, dispatched by extension — biome, csharpier, gofmt, ruff, prettier, taplo, stylua, ktfmt, and a dozen more. Prettier prefers project-local node_modules/.bin before global. .editorconfig rules (final newline, trailing whitespace) apply every save. :fmt or <space>f drives it manually.

biomecsharpiergofmtruffclang-formatstyluaprettiertaploktfmt.editorconfig<space>f
08 / SURROUND

Surround ops

ds<char> strips the surrounding pair, cs<old><new> swaps it, visual S<char> wraps the selection. Pairs balance through nesting; quotes do nearest-enclosing on the line.

ds"cs'"cs(]VS{
09 / FOLDS

Code folding

Indent-based fold ranges, toggled with za/zo/zc and zR/zM for everything. Folded blocks render as ⏷ N lines so you never lose context.

zazozczRzM
10 / SESSIONS

Sessions & tab bar

Open buffers + cursor + viewport persist on shutdown and restore on launch when no file argument is passed. A tab row across the top shows every open buffer; H/L cycles, click to switch, click × to close. Persistent undo too — each file's history serialised per content hash.

H · Ltabsu / U
11 / SPLITS

Window splits, pick-on-split

<C-w>v / <C-w>s split vertically / horizontally and open the file picker so the new pane lands on a different file in one keystroke. Uppercase <C-w>V / <C-w>S keep Vim's same-buffer behaviour. Per-tab split layouts — splitting one tab doesn't bleed into others. Focus moves geometrically with <C-w>h/j/k/l; each pane keeps its own cursor, viewport, fold state, and diagnostics. <C-w>T promotes a split companion to a real tab.

<C-w> v / s<C-w> V / S<C-w> h/j/k/l<C-w> q / o / =<C-w> Tper-tab layouts
12 / INLAY + SEMANTIC

Inlay hints & semantic tokens

Inlay hints render inline in dim italic — type hints on let bindings, parameter names on call sites. Parameter hints lean a shade warmer than type hints so they scan apart. Semantic tokens layer over the tree-sitter pass — LSP modifiers paint let mut foo red, async fn lavender, std:: symbols sapphire, all without user config. Both refetched per buffer-version with a one-in-flight cap so fast typing can't pile up. Toggleable via [lsp].

: Typearg:let mutasync fnstd::[lsp]
13 / VISIBILITY

Visible structure

Every space, tab, NBSP, and EOL surfaces as a muted glyph (·   ¬). Bracket and HTML-tag matching highlights the partner as the cursor moves. Hover popup parses LSP markdown and runs tree-sitter on fenced code blocks — signatures render the way they would in the buffer.

·¬{ }<tag>K hover
14 / ERGONOMICS

Quality of life

Smart indent on Enter (splits paired openers / closers). HTML tag auto-close (<div><div>|</div>). macOS Insert backspace: Alt+ peels a word, Cmd+ deletes to start of line. Ctrl-A/Ctrl-X on numbers (decimal, hex, bin, oct). Double-click selects a word; middle-click closes a tab. Auto-reload on external file changes.

⌥⌫ word⌘⌫ line^A / ^X<div>|</div>gt / gT
15 / DEBUG

Debuggers via DAP, four languages

Built-in adapter-agnostic DAP client. .NET via netcoredbg (auto-builds, reads launchSettings.json profiles). Go via dlv dap (enumerates package main dirs). Python via debugpy (active .py buffer wins, else picks main.py / manage.py / app.py / …). Rust / C / C++ via lldb-dap (parses Cargo.toml for [[bin]] targets, prelaunch cargo build). <space>ds (or F5) picks the adapter and drives initialize → launch → setBreakpoints → configurationDone. Live prelaunch output: the build child (dotnet build / cargo build) is spawned async with piped stdout/stderr, streamed line-by-line into the Console tab while it runs, and the real initialize+launch only fires once the build exits successfully — no more "frozen editor, no feedback" while a solution rebuilds. Conditional + hit-count breakpoints: :dapb if <expr> attaches a condition, :dapb hit <expr> attaches a hit count, :dapb plain strips both — conditional breakpoints render as in the gutter (plain stays ) and the breakpoints pane lists each row's expression inline. Bottom pane shows call stack + locals on the left, debug-console on the right. VS / Rider F-keys (F5 / F9 / F10 / F11) work in every mode. :debugtest (alias :dt) walks up from the cursor for the enclosing test, then routes through the DAP layer instead of the test runner — pytest (module: pytest) and go (delve mode: test) wired end-to-end. Android JDWP attach lives behind <space>Ad — see ANDROID below.

<space>dsF5/F9/F10/F11netcoredbgdelvedebugpylldb-dap:dapb if/hit/plain:debugtest▶ ◆ ●
16 / PICKERS

Fuzzy with match highlighting

Files, recents, grep, document / workspace symbols, code actions, references, debug project / profile — every picker uses the same fuzzy matcher with bonuses for word-boundary hits and consecutive runs. Matched characters render Yellow + Bold so it's obvious which letters earned the row's rank. Path rows prefix a Nerd Font file-type icon.

fuzzyfile iconsmatch hllive grep
17 / CLIPBOARD

System clipboard, both ways

Yanks mirror to the OS clipboard via arboard when the unnamed register is the target. p / P read from the OS clipboard first — anything you Cmd-C'd in another app wins over the in-memory yank. Visual p / P swaps the selection with the register's contents. Named registers ("ay) stay local.

"+yp / Pv_parboard
18 / FILES

Sidebar file explorer

<space>e opens yazi by default — set [file_explorer] tree = true to swap it for a built-in left-side tree pane rooted at the cwd. j / k navigate, Enter / l opens a file or expands a folder, h collapses or jumps to the parent, g / G top / bottom, R rebuilds. File ops inside the pane: a creates an entry under the cursor's parent dir (trailing / makes a folder, intermediate dirs auto-created); r renames the cursor entry with a basename-prefilled prompt — if the renamed file is open in a buffer, the buffer's path is rewritten so saves keep landing in the right file; d arms a delete and the next key consumes the y/N confirmation (any non-y cancels, so a stray double-tap doesn't unlink). Three-state toggle: closed → focused → unfocused-but-visible → closed.

<space>ea / r / dy/N deletesidebarno plugin
19 / TESTS

Integrated test runner, five adapters

One adapter pattern, five toolchains. cargo (Rust), vitest (JS / TS), pytest (Python), go test, and dotnet test (.NET) each ship as a TestAdapterSpec with a streaming parser. :test opens a fuzzy picker of discovered tests; :testnearest walks the buffer up for the enclosing test fn and runs it; :testfile derives a file-scoped filter from the active path; :testlast re-runs the most recent invocation. Results stream into a :health-style scrollable overlay (pass / fail / ignored counts in the status line on completion); failures populate the quickfix list with parsed file:line locations so ]q / [q walks them.

cargovitestpytestgo testdotnet test<space>sn]q · [q
20 / SPELL

Spell check, no external library

:spell toggles spell-check on the active buffer; ]s / [s jump between misspelled words; z= opens a suggestion picker (single-edit neighbours filtered against the dictionary, capped at 12). Wordlist loads from ~/.local/share/binvim/words (user override) or /usr/share/dict/words (system default). The tokeniser splits camelCase / snake_case / kebab-case so identifiers only trip on unknown constituents; pure-uppercase abbreviations and tokens under 3 chars are skipped to keep the false-positive rate low on source code. Per-buffer enable flag, version-keyed cache.

:spell]s · [sz=camelCase splitper-buffer
21 / PACKAGES

Package manager, four ecosystems

<space>p opens a per-ecosystem manifest picker that drives an install / search / version flow through whichever package manager owns the active workspace. NuGet via the dotnet CLI (parses dotnet list package / dotnet package search --exact-match / dotnet add package JSON, tolerates the first-run banner and honours each project's nuget.config); npm via npm list / npm view / npm install; cargo reads Cargo.toml directly for installed crates and falls back to the crates.io HTTP API for the version list (no cargo command lists them all), with cargo search / cargo add for the rest; Go parses go.mod for direct requires, scrapes pkg.go.dev for search, and uses go list -m -versions / go get for everything else. <space>pi picks a manifest → installed packages → versions (the installed one highlighted, Tab toggles prereleases) → install. <space>ps picks a manifest → searches the registry → package → version → add. Network + restore calls run on background threads so the editor stays responsive; an epoch guard drops results from cancelled flows.

<space>p<space>pi · psNuGetnpmcargogocrates.io / pkg.go.devbackground
22 / LAZYGIT

Lazygit integration

:lazygit / :lg / <space>gg suspends the editor, hands the full terminal to lazygit, and on exit reclaims the terminal AND refreshes the git gutter for every open buffer so stages / commits / checkouts show up immediately. Not a PTY-embedded pane — lazygit gets the whole screen (its UI hard-codes panel widths and the bottom :terminal pane caps at 20 rows). Same yazi-style takeover model: pop kitty keyboard protocol, drop mouse capture, leave alt screen, run, reclaim. Exit detection is free — when the blocking call returns, lazygit is done.

:lazygit:lg<space>gggutter refreshfull-screen takeover
23 / TASKS

Integrated task runner

:task / <space>mm discovers workspace tasks from five sources, unioned per workspace: npm scripts (npm / pnpm / yarn auto-picked from the lockfile), Justfile recipes (skips _private + [private]), cargo aliases + builtin verbs (build / check / test / clippy / run / fmt / doc), Makefile top-level targets, and dotnet verbs (build / run / test / restore / clean / publish). Picker rows tag the source for disambiguation; selecting a task spawns it in a fresh bottom-terminal tab labelled with the task name. :tasklast / <space>ml re-runs the most recent invocation.

:tasknpm / pnpm / yarnjustcargomakedotnet<space>mm
24 / CMDLINE

Cmdline, history and tab

Tab / Shift-Tab cycle candidates in the : cmdline — command names before the first space, filesystem entries after :e / :w (directories get a trailing /, dotfiles hidden unless the basename starts with .), open-buffer basenames after :b. Up / Down walks the cmdline + search histories (cap 100 entries each, deduped against the immediate previous, persisted to the per-cwd session file); the first Up snapshots whatever you'd already typed so walking off the bottom brings the draft back.

Tab cycle: history/ historydraft restore
25 / ANDROID

Android emulators & JDWP debug attach

<space>A opens a which-key submenu for the Android SDK side of a project. <space>Al lists installed AVDs (avdmanager list avd) and connected devices (adb devices); <space>Ar launches an AVD; <space>Ac scaffolds a new one from the available system images. Listings + launches run on a background thread so the editor doesn't block on the SDK tooling. For debug, <space>Ad attaches the DAP layer to a running Android process over JDWP — adb forward wires the JDWP port, the jdtls-hosted java-debug adapter accepts the attach over TCP, and DapManager::start_attach_session skips the usual spawn / launch handshake and goes straight to initialize + attach. Same stack + locals + breakpoints UI as the four built-in DAP languages.

<space>A<space>Al · Ar · Ac<space>AdavdmanageradbJDWP attachjdtls / java-debug
26 / TERMINAL

Terminal pane, scrollback + drag-copy

:terminal / <space>tt opens a PTY-backed shell tab in a bottom pane. Multi-tab (the task runner labels its tabs), tabs sized to the pane body, SIGWINCH propagates on resize. Scrollback view: the scroll wheel, Shift+PageUp / Shift+PageDown (or Shift+↑ / ↓ for one line) walk back through history even at an idle shell prompt where the program isn't capturing the mouse — the user stays anchored to the same content as new output streams in (tmux-style), and typing snaps the view back to live. Drag selects + auto-copies: plain left-drag inside the pane body highlights cells, release lands the text on the system clipboard. Works across the scrollback view too. <space>tp toggles the pane without killing the PTY so a long-running pnpm dev / cargo watch tucks out of the way while editing.

:terminal<space>tt<space>tptabs · SIGWINCHscroll wheel · Shift+PageUpdrag selectsauto-copy
+--

§ COMPARED · binvim vs the rest

5 editors · 9 dimensions

All five edit code. They take very different shapes. binvim is the only one that ships as a single TUI binary — the rest are full GUI applications with bundled or pluggable LSP / debug stacks. Numbers below are typical defaults; pick what fits your job.

// dimension
binvim
VS Code
Cursor
IntelliJ Rider
Zed
Size on disk
~42 MB
~370 MB
~500 MB
~1.5 GB
~80 MB
Cold-start to ready
~14 ms
1–3 s
2–4 s
10–30 s
~50 ms
Runs in a terminal
TUI
Electron GUI
Electron GUI
JetBrains GUI
GPU-native GUI
Modal editing
native
~extension
~extension
~IdeaVim
~Vim mode
Plugins for full IDE
zero
20–50
10–30
bundled
optional
Config
TOML · ~40 lines
JSON + UI
JSON + UI
XML + UI
JSON
Built-in debugger
4 adapters
~via ext
~via ext
first-class
~partial
AI assist
Copilot (opt-in) + agent side pane
extension
first-class
AI add-on $
first-class
License · cost
BSAL · free
MIT · free
subscription
subscription
GPL-3 · free

// disclaimer: sizes and startup times are approximate, measured against typical defaults; your mileage varies by platform, plugin load, project size, and indexing state. binvim isn't the right tool for every job — pick what fits.

+--

§ KEYS · cheatsheet

46 bindings · / to filter

//pickers·nav

file picker — fuzzy across project, recents on top
glive grep — ripgrep-grade, in-process
efile explorer — yazi, or built-in sidebar tree via [file_explorer] tree = true
gdgoto definition
grfind references — opens results in a picker
KLSP hover (markdown + tree-sitter)
H / Lprevious / next buffer · gt/gT aliases

//refactor·format

acode actions — quick fixes, organise imports
rrename — LSP WorkspaceEdit across every file
Rreplace all — literal, prompt pre-filled with word
fformat buffer — biome / gofmt / ruff / prettier / …
/toggle line comment — per-language prefix
]s / [snext / previous misspelled word (after :spell)
z=spell — open suggestion picker for the word under the cursor

//edit·multi-cursor

^Nvisual-char: select next occurrence
^ clicknormal: add a secondary cursor at the click
^Vvisual-block — rectangular column selection
ds"strip the surrounding pair
cs'"swap the surrounding pair (' → ")
vpvisual paste — swap selection with register
za / zRtoggle fold / open all folds
Copilot — accept ghost (wins over LSP popup)
Copilot — accept LSP popup item

//splits

^W v / ssplit vertical / horizontal + open picker
^W V / Ssplit, keep the same buffer
^W h/j/k/lfocus neighbour pane
^W q · o · =close · close-others · equalize
^W Tpromote split companion to a real tab

//debug

dsstart — picks adapter from buffer's workspace
d q · b · cstop · breakpoint · continue
d n · i · Ostep next · in · out
d o · S · pdoc / workspace symbols · pane
:dapb if/hit/plainconditional / hit-count breakpoint (◆ in gutter)
F5F11VS / Rider — start · break · next · step in
AdAndroid JDWP attach (adb forward + java-debug)

//tests

sstest picker — fuzzy across every discovered test
snrun the test enclosing the cursor
sfrun every test in the active file
slre-run the most recent test
:debugtestdebug the nearest test (pytest · go)

//ex commands

:s/pat/repl/grsubstitute · g global · r regex ($1 / $2 refs)
:S/pat/repl/grproject-wide — ripgrep walks every matching file
:dap*:debug · :dapstop · :dapc · :dapb · :dappane · :debugtest
:test*:test · :testnearest · :testfile · :testlast · :testresults
:spelltoggle spell-check on the active buffer
:fmtformat active buffer
:lazygitfull-screen lazygit takeover (refreshes gutter on exit)
:task / :tasklastworkspace task picker (npm / just / cargo / make / dotnet)
ppackage manager (NuGet · npm · cargo · go) — pi install · ps search
AAndroid — Al list · Ar run AVD · Ac create · Ad JDWP attach
:terminal · tPTY pane — tt open · tp toggle · drag selects · Shift+PageUp scrolls
:workspacesattached folder set per LSP (multi-root sessions)
:healthscratch health buffer (server status + cache counts)
:install · :updateoverlay — :install adds missing tools, :update bumps installed ones to pins
:nohclear search highlight

//git·ai·tasks

gglazygit — full-screen takeover, refresh gutters on exit
h p · s · u · rhunk — preview · stage · unstage · reset
j c · x · oopen Claude / Codex / opencode in side pane (new tab)
j C · X · Osame, AND pre-type @<active path> into the input
j f · p · qfocus side pane · toggle visibility · close tab
m m · ltask picker · re-run last task
+--

§ INSTALL · :install & :update

two commands · one overlay
new

:install — pick a language, binvim picks the installer

LSPs  ·  formatters  ·  DAP adapters

One command brings up every external toolchain binvim drives. Run it once. Tick the languages you care about. binvim detects the package managers on your $PATH (brew, apt-get, npm, cargo, rustup, go, pipx, pip, gem, dotnet, nix, composer), picks the right installer per tool, dedupes shared deps across languages, and shows you the plan before anything runs.

01 inside the editor :install full-screen overlay · suspends lazygit-style on confirm
02 from a shell $ binvim-install second binary, ships alongside binvim from every install path
upgrade

:update — refresh what's installed to the catalog's pins

already on $PATH only  ·  binvim self-update included

Same three-stage overlay as :install, but the plan only touches tools already on $PATH — pinned-version managers (npm, cargo, go, gem, pipx, dotnet) reinstall at the catalog's pin; native managers (brew, apt, nix) upgrade via their own command. The first row is binvim itself — auto-detects how the binary was installed (brew / cargo / install script / scoop / nix) and runs the matching self-update.

+--

§ … or install by hand

all optional

Prefer to install by hand? The complete per-tool reference is below — every binary binvim spawns on demand, all optional. When a tool isn't on $PATH (or in a relevant node_modules/.bin/) the editor just skips that capability.

  1. $ rustup component add rust-analyzer
  2. $ go install golang.org/x/tools/[email protected]
  3. $ npm i -g [email protected]
  4. $ npm i -g [email protected]
  5. $ npm i -g @tailwindcss/[email protected]
  6. $ npm i -g [email protected]
  7. $ npm i -g @astrojs/[email protected]
  8. $ dotnet tool install --global csharp-ls --version 0.24.0
  9. $ drop the official tarball at ~/.local/bin/omnisharp/
  10. $ npm i -g [email protected]
  11. $ brew install llvm (v22.1.5 · or apt install clangd)
  12. $ npm i -g [email protected]
  13. $ npm i -g [email protected]
  14. $ brew install lua-language-server (v3.18.2)
  15. $ npm i -g @vue/[email protected]
  16. $ npm i -g [email protected]
  17. $ brew install marksman (v2026-02-08)
  18. $ cargo install taplo-cli --version 0.10.0 --features lsp
  19. $ gem install ruby-lsp -v 0.26.9
  20. $ npm i -g [email protected]
  21. $ brew install jdtls (v1.58.0)
  22. $ brew install zls (v0.16.0)
  23. $ nix profile install nixpkgs#nil (v2025-06-13)
  24. $ brew install elixir-ls (v0.30.0)
  25. $ brew install kotlin-language-server (v1.3.13)
  26. $ npm i -g [email protected]
  27. $ go install github.com/sqls-server/[email protected]
  28. $ npm i -D @biomejs/[email protected] (project-local)
  29. $ dotnet tool install --global csharpier --version 1.2.6
  30. $ go install golang.org/x/tools/cmd/[email protected]
  31. $ pipx install ruff==0.15.13
  32. $ brew install llvm (v22.1.5 · ships with the toolchain)
  33. $ brew install shfmt (v3.13.1)
  34. $ cargo install stylua --version 2.5.2
  35. $ npm i -g [email protected] (svelte needs prettier-plugin-svelte in project node_modules)
  36. $ gem install rufo -v 0.18.2
  37. $ composer global require friendsofphp/php-cs-fixer:3.95.2
  38. $ brew install google-java-format (v1.35.0)
  39. $ nix profile install nixpkgs#nixfmt-rfc-style (v1.2.0)
  40. $ brew install ktfmt (v0.62)
  41. $ npm i -g [email protected]
  42. $ build from github.com/Samsung/netcoredbg (v3.1.3-1062 · needs libdbgshim sibling)
  43. $ brew install ripgrep (v15.1.0)
  44. $ brew install yazi (v26.1.22)

// resolution order: project-local node_modules/.bin/  →  $PATH. Global installs lose to project-local devDependencies, which is what you want.

+--

§ CONFIG · ~/.config/binvim/config.toml

optional · ~40 lines
» ~/.config/binvim/config.tomltoml · every section optional# missing or malformed → defaults to baked-in Catppuccin Mocha
schema_version = 1

[colors]
# themes available here: https://github.com/bgunnarsson/binvim/tree/main/themes

[start_page]
lines = [
    "  hello, world  ",
    "  press : to start ",
]

[whitespace]
# space=· tab=→ nbsp=⎵ eol=¬   on by default
show = true

[line_numbers]
# cursor row shows absolute, others show distance. on by default.
relative = true

[hover]
# wrap long source lines inside hover code blocks. on by default.
wrap_code = true

[lsp]
# semantic tokens layer over tree-sitter — LSP modifiers paint
# `let mut foo` red, `async fn` lavender, `std::` symbols sapphire.
semantic_tokens    = true
# documentHighlight on cursor settle — Surface2 bg on every
# occurrence of the symbol under the cursor.
document_highlight = true

[copilot]
# attach copilot-language-server (install: npm i -g @github/[email protected]).
# sign-in is device-flow surfaced in the status line on first launch.
enabled = false

[file_explorer]
# <space>e opens yazi by default; set true for the built-in left-side sidebar tree.
tree = false
+--

§ FAQ · common questions

six answers

Is binvim a Neovim replacement?

binvim is a from-scratch modal TUI editor written in Rust — not a Neovim distribution, fork, or config. It implements Vim grammar (operators, text objects, registers, macros) and ships tree-sitter, multi-server LSP, four DAP debuggers, and a dozen formatters in one binary. It is for developers who want to stop maintaining a vim-based editor config. If you want a 200-plugin platform, Neovim is the better tool for that job.

Does binvim need a plugin manager or a large config?

No. There is no plugin manager. Tree-sitter grammars, 24 language servers wired by file extension, four DAP debuggers, a dozen formatters, and opt-in GitHub Copilot are all compiled into a single ~42 MB binary. Configuration is an optional ~40-line TOML file at ~/.config/binvim/config.toml; with no config, binvim falls back to baked-in defaults.

What languages does binvim support?

Tree-sitter grammars are compiled in for Rust, TypeScript/TSX/JSX, JavaScript, Go, Python, C/C++, Java, Kotlin, Ruby, PHP, Lua, TOML, Svelte, Vue, Zig, Nix, Elixir, Dockerfile, SQL, HTML, CSS, Markdown, C#, Razor (.cshtml/.razor), YAML, XML, and Bash. 24 language servers are wired by file extension, including rust-analyzer, tsserver, gopls, pyright, clangd, jdtls, and csharp-ls.

Can binvim debug code?

Yes. binvim has four built-in DAP debuggers: .NET via netcoredbg, Go via delve, Python via debugpy, and Rust/C/C++ via lldb-dap. It supports VS-style F5/F9/F10/F11 keys, conditional and hit-count breakpoints, and live prelaunch build streaming into the debug Console.

How is binvim different from Helix?

binvim uses classic Vim grammar — verb-then-object operators, text objects, marks, registers, dot-repeat, and macros — rather than Helix's selection-first model. It also ships opt-in GitHub Copilot, a right-side AI assistant pane (:claude / :codex / :opencode), four DAP debuggers, and integrated test and task runners across five toolchains, all in one binary.

What platforms does binvim run on and how do I install it?

binvim runs on macOS, Linux, and Windows. Install with Homebrew (brew install bgunnarsson/binvim/binvim), a curl | sh script, PowerShell (iwr … | iex), Scoop, cargo (cargo install --locked binvim), or a Nix flake.