A modern, modal text editor built in Rust. Red combines Vim-inspired editing with modern features - Language Server Protocol support, tree-sitter syntax highlighting, and a sandboxed JavaScript/TypeScript plugin system - in a single self-contained binary that works with zero setup.
- Modal Editing: Vim-inspired Normal, Insert, Visual, Visual Line, Visual Block, and Command modes
- Language Server Protocol: Code completion, diagnostics, hover documentation, goto definition, find references, document and workspace symbols, and inlay hints, with sensible defaults for seven common language servers
- Syntax Highlighting: Tree-sitter based highlighting for Rust, Markdown, JavaScript, TypeScript/TSX, JSON, TOML, YAML, Python, and Bash
- Windows and Buffers: Horizontal/vertical splits with independent viewports, multiple buffers, and a jump list
- Plugin System: JavaScript and TypeScript plugins in a sandboxed Deno runtime, with a typed API - the file tree, project search, and theme browser are all plugins
- Theme Support: VSCode theme compatibility, with a large collection of themes built in
- Self-Contained: Default config, themes, and plugins are bundled into the binary - no setup required
- Async Architecture: Built on Tokio for responsive, non-blocking operations
- Cross-Platform: Works on Linux, macOS, and Windows
This editor is being actively built on a series of streams and videos published to my CoderSauce YouTube channel here:
https://youtube.com/@CoderSauce
It is my intention to keep it stable starting at the first alpha release, but there are no guarantees. As such, use it at your discretion. Bad things can happen to your files, so don't use it yet for anything critical.
If you want to collaborate or discuss red's features, usage or anything, join our Discord:
- A recent stable Rust toolchain (install via rustup)
- Git
- Clone the repository:
git clone https://github.com/codersauce/red.git
cd red- Build and install:
cargo install --path .That's it. No configuration step is needed - the default config, themes, and plugins are bundled into the binary.
Once installed, you can start editing files immediately:
red <file-to-edit>On the first interactive run, Red offers to create a starter config at ~/.config/red/config.toml. You can decline (or run non-interactively) and Red launches with its embedded defaults - a config file is entirely optional.
Red uses Vim-style modal editing. Everything below is the default; all of it can be remapped (see Configuration).
h/j/k/lor arrow keys - Move left/down/up/rightw/b- Move forward/backward by word0/$- Move to beginning/end of linegg/G- Go to first/last lineCtrl-b/Ctrl-f- Page up/downzz- Center the current line in the view%- Jump to the matching bracket (g%backward,[%/]%for unmatched brackets)Ctrl-o/Tab- Jump back/forward through the jump listgj/gk- Move by screen line when long lines wrap
i/a- Insert before/after the cursor (I/Afor start/end of line)o/O- Open a new line below/abovex- Delete character;dd- delete line;dw- delete wordu/Ctrl-r- Undo/redop/P- Paste after/before>>/<<- Indent/unindent the current lineEsc- Back to Normal mode
v- Visual mode (character-wise)V- Visual Line modeCtrl-v- Visual Block mode (rectangular selections;Iinserts on every selected line)- In Visual mode,
i/aselect inside/around a text object:iwfor a word,i(,i[,i{,i<,i",i', ori`for delimited text, anda%for a matchit pair - In a selection:
ycopies,xdeletes,ppastes over it
/and?- Forward/backward search with live preview and highlighted matchesn/N- Repeat search in the same/opposite direction*- Search for the word under the cursor:noh- Clear search highlights
Search patterns use Rust regex syntax. Behavior (incsearch, hlsearch, wrapscan, ignorecase, smartcase) is configurable. The bundled cool_search plugin clears highlights automatically once you move away from a committed match or enter Insert mode.
K- Hover documentationgd- Go to definitionCtrl-Space- Trigger completion (in Insert mode; completion also triggers as you type)Ctrl-t- Document symbols pickerSpace w- Workspace symbols pickerSpace k- Find references
Diagnostics from the language server are displayed inline, and LSP progress is shown by the bundled fidget plugin.
Ctrl-p- File pickerCtrl-e- File tree (neotree)Ctrl-jorSpace b- Buffer pickerSpace g- Project-wide search (requiresrgon your PATH)Space t- Theme browser with live preview
Ctrl-w s/Ctrl-w v- Split horizontally/verticallyCtrl-w h/j/k/l- Move between windowsCtrl-w w- Next window;Ctrl-w c- close windowCtrl-w =/Ctrl-w _/Ctrl-w o- Balance/maximize/keep only the current windowSpace SpaceorSpace n/Space p- Next/previous buffer
Enter Command mode with : (or ;).
:w [file]- Save (optionally to a new name);:wq- save and quit:q/:q!- Quit / quit discarding changes:e <file>- Open a file;:e!- reload the current file:<number>/:$- Jump to a line / the last line:bn/:bd- Next/delete buffer (Space pgoes to the previous buffer):sp [file]/:vs [file]- Split horizontally/vertically:close/:only- Close the current window / keep only the current window:noh- Clear search highlights:wrap/:nowrap- Enable/disable line wrapping
Red works out of the box with sensible embedded defaults. To customize it, use a TOML configuration file at ~/.config/red/config.toml.
Your config is layered on top of the embedded defaults: you only need to write the settings you want to change, and everything else keeps its default value. The starter config that Red offers to create on first run is a commented template to get you going - it is not the source of truth for defaults, so deleting it (or any setting in it) simply falls back to the built-in behavior.
# ~/.config/red/config.toml — only what you want to change
theme = "atom-one-dark.json"
scrolloff = 8
[search]
ignorecase = true
smartcase = true
# Remap or add keybindings; everything not listed keeps its default
[keys.normal]
"Ctrl-s" = "Save"For the full set of options - cursor shapes per mode, wrapping and scrolling behavior, plugin settings, logging, and more - see the commented default_config.toml, which documents every default exactly as the binary ships it.
Every mode has its own table ([keys.normal], [keys.insert], [keys.visual], …). Bindings map a key to an editor action, a sequence of actions, a nested table for chords, or a plugin command:
[keys.normal]
"u" = "Undo" # single action
"a" = [ { EnterMode = "Insert" }, "MoveRight" ] # sequence
"g" = { "d" = "GoToDefinition" } # chord: g then d
"Ctrl-j" = { PluginCommand = "BufferPicker" } # plugin commandBuilt-in LSP defaults cover Rust (rust-analyzer), TypeScript/JavaScript (typescript-language-server), Python (pyright), Markdown (marksman), JSON, TOML, and YAML. Servers start only when a matching file is opened, and each one must be installed and on your PATH. Add or override servers in your config:
[lsp.servers.go]
command = "gopls"
language_id = "go"
file_extensions = ["go"]
root_markers = ["go.mod", ".git"]Bundled plugins are enabled by default. Disable any of them by name, and configure the ones that take options:
disabled_plugins = ["barbecue"]
[plugin_config.lsp_symbols.icons]
enabled = falsePlugins that spawn external processes need an explicit allowlist, e.g. project_search ships with [plugin_permissions.project_search] process = ["rg"].
red [files...] # open one or more files
red -r <path> # set the working directory root
red -c 'wrap = false' # inline TOML config override (repeatable)
red --runtime-files # list visible plugins/themes and their sources
red --eject <asset> # copy a bundled plugin/theme into your config dir
Red ships with a collection of VSCode-compatible themes bundled into the binary - they work without any files on disk. Reference a theme in your config:
theme = "your_theme_name.json" # the theme's filenameTo add your own theme, place a .json theme file in ~/.config/red/themes/. Run red --runtime-files to see every theme Red can currently load, or browse them interactively with Space t.
Red's default plugins and themes are embedded in the binary, so a fresh install has everything it needs and upgrades automatically pick up newer bundled versions. Nothing is copied to your config directory unless you ask for it.
The bundled plugins:
| Plugin ID | File | What it does |
|---|---|---|
barbecue |
barbecue.js |
Breadcrumb bar showing your current location in the code |
buffer_picker |
buffer_picker.js |
Quick switcher for open buffers |
cool_search |
cool_search.js |
Clears search highlights automatically when you move on |
fidget |
fidget.js |
LSP progress indicator |
indent_guides |
indent_guides.js |
Vertical indentation guides |
inlay_hints |
inlay_hints.js |
Inline LSP type and parameter hints |
lsp_symbols |
lsp_symbols.ts |
Document and workspace symbol pickers |
neotree |
neotree.js |
File tree with git status and file icons |
project_search |
project_search.js |
Project-wide text search powered by ripgrep |
session_restore |
session_restore.js |
Reopens your files and layout from the last session |
theme_browser |
theme_browser.js |
Theme picker with live preview |
To turn one off, add its plugin ID to disabled_plugins in your config, e.g. disabled_plugins = ["fidget"].
red --runtime-filesThis lists every plugin and theme Red can see and where each one comes from (your config directory, $RED_RUNTIME, or the embedded assets). When the same filename exists in more than one place, the listing shows which source wins.
Files in your config directory take precedence over bundled ones with the same filename. For example, ~/.config/red/plugins/fidget.js replaces the bundled fidget.js.
To start from the bundled version, eject a copy into your config directory:
red --eject plugins/fidget.js # copy a bundled plugin for editing
red --eject themes/mocha.json # copy a bundled theme for editing
red --eject fidget.js # the plugins/ or themes/ prefix is optionalEject refuses to overwrite an existing file; use red --eject-force <asset> to replace your copy with the bundled version.
Keep in mind that an ejected file shadows the bundled one permanently - if a later Red release improves that plugin or theme, your copy still wins. Delete the file from your config directory to go back to the bundled version.
Packagers and developers working from a source checkout can point $RED_RUNTIME at a directory containing plugins/ and themes/ subdirectories. Assets are resolved in this order:
- Your config directory (e.g.
~/.config/red/plugins/foo.js) $RED_RUNTIME/plugins/foo.jsor$RED_RUNTIME/themes/foo.json- The assets embedded in the binary
Normal users don't need to set this - the embedded assets cover everyday use.
Plugins are JavaScript or TypeScript files running in a sandboxed Deno runtime. A plugin exports an activate function and gets a typed red API object:
export async function activate(red) {
red.addCommand("HelloWorld", async () => {
const { x, y } = await red.getCursorPosition();
red.insertText(x, y, "Hello from a plugin!");
});
}Commands registered this way can be bound to keys with { PluginCommand = "HelloWorld" }.
The API covers buffer access, cursor control, pickers and dialogs, per-window bars, virtual text decorations, events, LSP queries, timers, persistent storage, and permission-gated process spawning. TypeScript definitions are available in types/red.d.ts (published as @red-editor/types).
See docs/PLUGIN_SYSTEM.md for the full plugin development guide, and the bundled plugins for real-world examples.
# Clone the repository
git clone https://github.com/codersauce/red.git
cd red
# Build debug version
cargo build
# Build release version
cargo build --release
# Run tests
cargo test
# Run with debug logging
RUST_LOG=debug cargo run -- test.txtWhen iterating on bundled plugins or themes, point $RED_RUNTIME at your checkout to use the working-tree files without rebuilding:
RED_RUNTIME=. cargo run -- test.txtred/
├── src/
│ ├── main.rs # Entry point and event loop
│ ├── editor.rs # Core editor state machine
│ ├── buffer.rs # Text buffer management
│ ├── lsp/ # Language Server Protocol implementation
│ ├── ui/ # Terminal UI components
│ └── plugin/ # Plugin runtime
├── plugins/ # Built-in plugins (bundled into the binary)
├── themes/ # Default themes (bundled into the binary)
├── types/ # TypeScript definitions for the plugin API
├── docs/ # Plugin system and internals documentation
└── tests/ # Integration tests
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Red logs to /tmp/red.log by default. Override log_file in your config to change the path:
log_file = "~/red.log"Then check the log file:
tail -f /tmp/red.log- LSP not working: Ensure the language server for your file type is installed and in your PATH
- Plugins not loading: Run
red --runtime-filesto check that the plugin is visible and which source (config directory,$RED_RUNTIME, or embedded) is being used - Theme not found: Run
red --runtime-filesto confirm the theme name; custom themes in~/.config/red/themes/must be valid JSON - A bundled plugin/theme behaves like an old version: An ejected copy in your config directory shadows the bundled one - delete it or re-eject with
red --eject-force
If you find any issues, please report them at:
https://github.com/codersauce/red/issues/
Check existing issues first to avoid duplicates.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with love for the Rust community
- Inspired by Vim, Neovim, and Helix
- Special thanks to all contributors and the CoderSauce community
Thank you for trying Red! ❤️
