Beam is a terminal text editor written in Zig. It is now native-first: the editor core owns timing, buffers, layout, rendering, scheduling, and undo history, while built-ins and QuickJS plugins provide the main extension surface.
Beam aims to be a fast editor for the terminal:
- core editing, motion, and buffer management live in Zig
- configuration is read from a TOML file
- built-in modules can register commands, react to events, and update editor state
- QuickJS plugins can extend the editor through a small native bridge
- search, picker, diagnostics, and workspace state all live in the editor runtime
- the editor also exposes
:lsprequest commands for definition, hover, completion, references, rename, code actions, and semantic tokens
src/main.zig- process entry pointsrc/editor.zig- editor runtime, command dispatch, pane orchestration, and UI behaviorsrc/config.zig- TOML config parsing and defaultssrc/buffer.zig- buffer editing logic and undo/redo historysrc/builtins.zig- native built-in registry and event hookssrc/plugin.zig- QuickJS plugin bridge and host integrationsrc/plugin_catalog.zig- plugin manifest discovery and catalog statesrc/diagnostics.zig- diagnostics storage and decorationssrc/search.zig- search helpers and search resultssrc/picker.zig,src/listpane.zig,src/listsource.zig- picker panes and list sourcessrc/lsp.zig- language-server client integrationsrc/workspace.zig- workspace/session persistenceexamples/beam.toml- example configurationexamples/plugins/hello- sample native plugin manifest and implementationexamples/plugins/hello-wasm- sample WASM plugin manifest and implementation
All commands below are run from the repository root.
Build and run the test suite:
zig build testRun the executable and print the built-in help:
zig build run -- --helpIf Zig cannot write to the default cache locations, use:
ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache ZIG_LOCAL_CACHE_DIR=.zig-cache zig build testClone the repository and enter it:
git clone <repo-url>
cd BeamRun Beam with the default config search path:
zig build runBy default Beam looks for beam.toml in the current directory. You can point it at a different config file with --config:
zig build run -- --config examples/beam.tomlYou can also open a file directly:
zig build run -- --config examples/beam.toml path/to/file.txtIf no config file is found, Beam falls back to built-in defaults.
The zig build run target also stages the sample hello plugin artifact and manifest so the example plugin config can be used immediately.
Beam's normal mode is built around composable motions and edits:
- basic cursor movement:
h j k l - line anchors:
0,^,$, plusg0,g^,g$ - local find motions:
f/F/t/T, repeated with;and, - compound motion + operator edits:
dw,diw,d$,ciw,yap - undo / redo:
uandCtrl-R - repeat last change:
. - visual selections can be extended with compound motions too, for example
VGselects from the current line to the end of the file - visual and select-mode deletes are undoable with the same
u/Ctrl-Rworkflow
Beam keeps the motion and editing grammar intentionally small, but the goal is for the pieces above to compose cleanly.
For Zig buffers, Beam also uses Tree-sitter syntax data where it helps the editor stay structural:
- block text objects prefer parser-backed ranges when available
- fold creation prefers Tree-sitter fold queries before falling back to paragraph folds
- open-line actions use syntax-aware indentation when the parser can provide it
- the status bar shows whether Zig parsing is running in fallback, parser, or parser-plus-queries mode
Beam keeps the Tree-sitter integration self-contained:
- the Tree-sitter runtime and Zig grammar are vendored under
vendor/and built directly - Tree-sitter query files are copied into the Zig build and embedded into the executable
- the editor still falls back to its non-parser behavior when parser state or query data is unavailable
The example config in examples/beam.toml shows the supported top-level sections:
[editor]for editor behavior such as tab width, line numbers, status bar settings, theme, and appearance[builtins]for enabling compiled-in modules[keymap]for command remapping and the leader prefix[keymap.leader]for leader-prefixed normal-mode mappings
The leader value can be any byte sequence, and the [keymap.leader] table maps the keys that follow it to direct editor actions.
The sample config includes x = "close_prompt" so leader x asks before closing a split, tab, or buffer.
Undo and redo are not leader-prefixed by default; they stay on plain u and Ctrl-R.
Key defaults to know:
- config file default:
beam.toml - leader default:
:
The sample config enables the native hello built-in:
zig build run -- --config examples/beam.tomlBuilt-ins are compiled with Beam and can:
- register commands
- listen for editor events
- update the status line
- request host-owned actions through narrow native APIs
Plugins are loaded from the configured plugin root and use the native host bridge exposed by src/plugin.zig.
- the example plugin lives in
examples/plugins/hello - the example wasm plugin lives in
examples/plugins/hello-wasm - the sample config points
[plugins].rootatzig-out/plugins zig build run -- --config examples/beam.tomlenables the samplehelloplugin
Plugin manifests now use an explicit schema:
[plugin]
name = "hello"
version = "0.1.0"
manifest_version = 1
api_version = 1
runtime = "native"
[capabilities]
command = true
event = true
status = true
pane = true
decoration = truemanifest_versionversions the manifest schema itselfapi_versionversions the Beam host API expected by the pluginruntimeselects the plugin tier:nativeis the default dynamic library path, whilewasmis recognized and reported cleanly as unavailable in builds without a WASM runtimezig build run -Dwasm-plugins=true -- --helpenables the embedded Wasmtime backend and stages the samplehello-wasmplugin artifact- capability keys are validated strictly; unknown capability names reject the manifest
Supported capability keys in the current host surface are:
commandeventstatusbuffer_readbuffer_editjobsworkspacediagnosticspickerpanefs_readtree_querydecorationlspjob_results
The current WASM host bridge is intentionally narrower than the native one. Today the Wasmtime-backed runtime supports plugins that use:
commandeventstatus
The hello-wasm sample shows the current ABI shape: plugins register commands and events through host imports, export dispatcher entrypoints for command and event delivery, and exchange strings through the module's linear memory.
Please keep changes small and focused. When behavior changes, add or update tests near the code that changed, especially in src/editor.zig, src/config.zig, or src/builtins.zig.
Before sending changes, make sure the project still passes:
zig build testzig build run -- --help
Please avoid editing generated artifacts or vendored third-party code unless the task explicitly requires it. In particular, keep changes out of zig-out/ and treat deps/quickjs_clean as upstream code.
If you are changing config keys, commands, or built-in behavior, update the docs and examples together so they stay in sync.
Beam is licensed under the Apache License, Version 2.0. See LICENSE for the full text.