Redox Devlog[2] - It's been a minute...
The GitHub repo, and the crate.
cargo install redox-editor
This one's a doozy, so I'd grab a snack and drink first.

The Redox logo, now in full colour.
Well, that escalated.
So... The last Redox devlog was on February 20th, which was only a little while ago in calendar time, but roughly eight geological epochs ago in "randomly opening Helix source files at 1:00 AM and muttering about text objects" time.
Since then, a lot has happened.
Also, minor life update: Redox was initially conceived as the final capstone project for my computer science degree, and I finished that degree a few days ago (yay!). So the project is no longer competing quite as directly with assignments, deadlines, exams, general procrastination, etc.
Naturally, instead of resting, I made the terminal editor more complicated.
Redox has gone from "this can edit a file" to something much closer to "this is starting to behave like an actual modal editor." It now has multi-buffer support, an awesome file explorer, visual modes (standard, line, and block), basic undo/redo functionality, a private copying register, system clipboard integration, search, text objects, syntax highlighting for basically all the good languages, scope guides, delimiter pair highlighting, smart indentation, background analysis, performance instrumentation, MUCH better performance overall, and one feature that is best discovered by typing :rain.
I have been busy. The devlog has not.
Let's fix that.

Version Bump: From 0.2.0 to 0.5.0-ish
The previous post covered the 0.2.0 release. Since then, Redox has moved through:
v0.3.0v0.3.1v0.3.2v0.4.0v0.4.1v0.5.0
A quick diff from v0.2.0 to the current version is, uh:
67 files changed, 15724 insertions(+), 1729 deletions(-)
That is not a normal devlog gap. The good news is that save for a bunch of optimization work, the core architecture from the last post mostly survived. The better news is that it has been stretched pretty hard now, and I'd say it held up.
The Workspace Renaming
Last time, the workspace looked like this:
crates/
├── editor_core/
└── editor_tui/
That has since been renamed and packaged more cleanly:
redox/
├── src/
│ └── main.rs
├── crates/
│ ├── redox-core/
│ └── redox-tui/
The root crate is now redox-editor, and it's basically just a super thin wrapper which publishes the actual installable binary with:
cargo install redox-editor
Don't worry, the binary itself is still called redox.
Internally, the split is still the same idea:
redox-core: UI-agnostic text buffers, positions, motions, selections, text objects, search primitives, and session management.redox-tui: MinUI runtime, input mapping, rendering, popups, widgets, syntax highlighting, cursor projection, and terminal interaction.
This separation has actually become even more important, not less. The project now has enough editor behaviour that stuffing everything into one massive "state blob of destiny" would be emotionally harmful.
Multi-buffer Sessions
Redox is no longer a single-buffer editor.
The core now has an EditorSession that owns multiple buffers, tracks the active buffer, remembers MRU ordering, and keeps file metadata separate from buffer contents.
That enables commands like:
:e <path> # Open/switch buffer for a specified file path
:bn # Go to the next buffer (MRU order)
:bnext # Same thing, more verbose
:bp # Go to the previous buffer (MRU order)
:bprev # Same thing again
:ls # List the currently open buffers, Vim-style
The session tracks:
- stable buffer IDs
- file-backed buffers
- UI/surface buffers
- dirty state
- new file state
- open paths
- MRU navigation
- loading state
That last one matters because Redox no longer has to eagerly swallow the entire file before it can display anything.
The previous post had this note:
I'm currently noticing very significant slowdown when opening any file over ~1MB.
That was a good note, because it pointed directly at one of the bigger changes: incremental loading.
Incremental File Loading
Opening large files (>64 KB) now uses an incremental UTF-8-safe loader.
Instead of forcing the whole file into the rope immediately, Redox reads an initial chunk, shows what it can, then continues loading in chunks. Importantly, it handles UTF-8 boundaries correctly, so a multi-byte character split across two chunks does not become text confetti.
The active buffer has a load phase:
pub enum BufferLoadPhase {
NotLoading,
Loading,
Complete,
Failed,
}
The TUI can then pump loading based on the viewport and show loading progress in the status bar.
Important note: edits and saves are gated until the active buffer is fully loaded, which is the boring correct decision. If you do try to edit while the file is still loading, Redox triggers a command to force-load the entire file there on the spot. Yes, it sucks that you can't quickly edit those massive files, but editing half a file while the other half is still arriving is just begging for corruptions. As such, this is meant more for viewing large files, not really editing them. Sublime Text exists for that.
I think this change is one of the biggest "the editor feels more real now" upgrades. It is not glamorous, but neither is plumbing, and yet I remain a fan of indoor water.

A File Explorer That Tastes Like Oil
Redox now has a file explorer, toggled with:
<space>e
:ex
:explorer
It is inspired by my favourite Neovim file explorer, oil.nvim. This means the directory is represented as an editable buffer rather than a separate tree widget.
That gives Redox a very editor-native way to do file operations:
- edit a file name to rename it
- add a new line to create a file or directory
- delete a line to delete a file or directory
- write the explorer buffer to apply the changes
This turned out to be both lovely and terrifying.
Lovely, because it feels extremely natural inside a modal editor.
Terrifying, because "the user deleted a line" can mean either "remove a character from a buffer" or "recursively delete this directory from disk." Those are slightly different levels of severity.
As such, the explorer has a bunch of safety behaviour built-in:
- delete confirmation for destructive operations
- recursive directory deletion handling
- path sync for open buffers after renames/deletes
- hidden dirty buffers are protected
- cursor position is preserved where possible
- navigating to a parent directory reselects the directory you came from
One thing that Oil does that Redox currently doesn't is easy nested/file/creation.txt. In Oil, writing this as a line entry would automatically create a directory structure like:
nested/
└── file/
└── creation.txt
In Redox, it just gives you an error because it thinks that's an invalid file name. Don't worry though, I'm definitely planning to add this in the near future because it's super handy!
Visual Mode Exists Now!
In the last devlog post, visual mode was still "coming soon." This time, I'm happy to report that it has actually arrived.
Redox supports the standard:
v visual mode
shift+v visual line mode
ctrl+v visual block mode
The visual modes support Vim classics like:
- yanking
- deleting
- changing
- replacing
- indentation
- outdentation
- moving selected lines with
shift+j/shift+k - yanking to the system clipboard with
<space>y
Character-wise, line-wise, and block-wise selections now flow through shared edit-plan logic in redox-core. That means the TUI can ask the core for a plan and then apply it consistently.
In other words: selections became data, not a pile of special edge cases wearing a trench coat.
Registers, Paste, Undo, Redo
Redox now has a private register, plus basic system clipboard support via MinUI.
Normal paste works:
p Paste after/below cursor
P Paste above cursor
Pasting from the system clipboard works with:
<space>p
Visual selections can be yanked to the system clipboard with:
<space>y
There is also undo/redo:
u
ctrl+r
The undo model is still intentionally simple (read: sub-optimal). It's literally taking snapshots of the full buffer every time you make an edit, and overwriting the working buffer with those snapshots when you undo/redo. This is obviously not ideal for memory consumption since it's storing multiple full copies of the buffer, as opposed to a diff-based system which just tracks what changed.
The difference is that a diff-based system needs to be 100% accurate or you risk data corruption, and I didn't have time to get it that polished for the capstone project. The snapshots may be clunky, but it also naturally guarantees correctness. And besides, the files you're editing probably aren't that big anyway (we're generally talking 64 KB max), and the overhead for a rope buffer is only about 10-15% of the original file size. So unless you're working on a Cold War era computer, you should be fine.
Still, I am planning to add diff-based tracking and a stored undo-tree soon. It's nothing special right now, but it is enough to make your typical editing experience feel far less permanent.
One nice implementation detail: repeated insert-mode typing is coalesced into a single undo step. So typing a word and pressing u removes the word, not one sad little character at a time.
The Motion System Grew Up
The input mapper from the last post was already a tiny state machine for counts and gg, but it's eaten a few protein bars since then.
It now tracks:
- pending key sequences
- count prefixes
- pending operators
- pending text object scopes
- pending
f/tsearch motions - pending replace actions
This enables normal Vim-ish command shapes like:
dd Delete full line
yy Yank full line
cc Change full line
d$ Delete from cursor to end of line
D Alias for the same thing ↑
diw Delete in word
daw Delete around word
ci" Change in quotes
ca} Change around braces
dap Delete around paragraph
...
The important architectural bit is that input mapping still produces high-level actions instead of directly editing text.
For example:
InputAction::OperateTarget {
operator,
target,
}
The target might be a motion:
OperatorTarget::Motion {
motion: Motion::LineEnd,
count,
}
Or it might be a text object:
OperatorTarget::TextObject(TextObjectSpec {
scope,
kind,
count,
})
That separation has made it much easier to add behaviour without turning the input layer into a haunted switch statement.
I mean, it is still a switch statement. It is just less haunted.
Text Objects
Redox now supports a first round of text objects:
iw inner word
aw around word
iW inner big word
aW around big word
ip inner paragraph
ap around paragraph
i" inner double quotes
a" around double quotes
i' inner single quotes
a' around single quotes
i` inner backticks
a` around backticks
i( inner parentheses
a( around parentheses
i[ inner brackets
a[ around brackets
i{ inner braces
a{ around braces
The core resolves these into edit plans, which include:
- the ranges to delete
- the text to yank
- the register kind
- whether the operation is char-wise or line-wise
This matters because delete, change, yank, and visual selection can all share the same underlying target resolution.
For example, ci" and visual i" should agree about what "inside this string" means. If they do not, the editor starts to feel haunted again, and we are trying to keep the haunting budget under control.
Search
Redox now has local search too.
Slash search works:
/
And search repetition works with:
ctrl+n Jump to next instance
ctrl+p Jump to previous instance
There are also same-line search motions:
f<char> "Find" (up to the specified char, inclusive)
t<char> "To" (up to the specified char, exclusive)
Those motions work as operator targets too, so commands like these are possible:
df, Delete to comma (also deletes the comma)
dt) Delete to closing parentheses (does not delete the parentheses)
Search results are cached in editor state and rendered as highlights. Moving the cursor manually hides the visible highlights while keeping the cached query around, which lets repeat search continue behaving predictably.
Under the hood, redox-core has literal search helpers that scan Ropey chunks without first flattening the entire buffer into a single giant string. That was one of the performance fixes after the first version of search was, technically speaking, "working," but with the same amount of enthusiasm as myself by the end of the semester.
Syntax Highlighting
This is probably the flashiest change.
Redox now has Tree-sitter-backed syntax highlighting. The first version only supported Rust. Now the language list is much larger, with pretty much everything I personally need:
- Rust
- Markdown
- C
- C++
- Go
- Lua
- Python
- HTML
- CSS
- JavaScript
- TypeScript
- TSX
- JSON
- TOML
- YAML
I'll add a few more languages later on, but I think I want to keep this relatively minimal and focused to my personal stack in order to limit the number of dependencies for this project. I don't really want cargo install redox-editor to take four hours, and I don't imagine you want that either. If there's something you really need support for, it should be easy enough to fork the repo and copy the basic structure of Redox's Tree-sitter syntax modules yourself.
There is also a fallback highlighter for languages where Redox does not have a Tree-sitter parser configured. The fallback is intentionally modest, but it can still pick out useful code shapes like comments, strings, numbers, keywords, and things that look like functions.
The syntax system now has per-language configuration modules, so adding a language is not just a matter of stuffing another case into one massive function. Each language can define their own:
- grammar
- highlight queries
- capture mapping
- role refinement
- optional inline grammar support
- scope node behaviour
Markdown got some extra love too. Because I use it a lot for personal note-taking, it got things like links, frontmatter, list markers, and inline emphasis customized a bit more to fit my preferences.


More about the theme
This colour scheme is the one I use everywhere, in case you hadn't noticed. I've ported it over to Ghostty, Neovim, Zed, this very website, and now Redox. It's very intentional too.
Everything that doesn't need to be your primary focus is a dimmed shade of grey. Comments, relative line numbers, scope overlays, the colour column, and most of the statusline. They're nice and visible when you're looking for them, but they stay out of your way when you aren't. The statusline modes have distinctive colours so you can identify them even in your peripheral vision.
Even the shade of white for basic text is dimmed with a very subtle blue tint (0xE2E2E3) so it doesn't look quite as harsh against the background. I've put a lot of work into refining this colour scheme, and I absolutely love it!
Background Analysis Worker
Syntax highlighting and delimiter analysis can get expensive, especially as files grow, so Redox now has a background analysis worker thread.
The editor can request analysis for a buffer/version pair, and the worker computes:
- syntax highlight cache
- delimiter analysis
The request channel keeps only the latest pending request. If the user is typing quickly, Redox does not lovingly compute syntax data for every obsolete intermediate version of the buffer. It just keeps the newest one and moves on with its life, which is behaviour I'd like to personally adopt.
Analysis results are versioned and buffer-scoped, so stale results can be dropped safely. That's important because without it, background work can create gross bugs, such as old delimiter highlights briefly appearing on new text.
Delimiter Highlighting and Scope Guides
Redox now highlights matching delimiter pairs:
- parentheses
- brackets
- braces
- quotes
- backticks
It also draws scope guide lines for active multi-line structures. This is another feature inspired by an existing Neovim plugin, mini.indentscope.
This started as delimiter scanning, then grew into something smarter. The editor can use Tree-sitter's very fast scope information where available, and manual delimiter analysis where it makes sense. The two systems can also cooperate: syntax can identify the active structural scope, and delimiter analysis can map that scope back to the visible guide column.
I'm intentionally not drawing indentation guides for every single indentation level (which is what most IDEs do), since I think that clutters the UI very quickly. I think the more minimal approach results in nested code that's much easier to read, since it only shows the scope for the area immediately surrounding the cursor. That's probably the only place you care about, anyways.

Smart Indentation
Redox now does smarter indentation on newline and open-line commands (enter, o, O), and correctly indents/outdents when moving a selection up and down with shift+j/shift+k.
The indentation system uses language-aware syntax information when possible, and falls back where needed.
It also cleanly handles delimiter-splitting cases like:
fn main() {
|
}
It also handles quotes and backticks for supported cases, and Markdown list continuation got specific handling too. It's small things like this that make the editor feel like it's correctly predicting your next move.
Rendering and Performance Work
A lot of the work since the last post has been performance-oriented.
Some highlights:
- incremental file loading
- viewport-driven loading
- long-line cursor fast paths
- reduced per-frame allocations
- optimized visual line movement
- optimized undo/redo snapshots
- optimized dirty tracking
- chunk-based rope search
- optimized delimiter pair calculation
- cached syntax rendering paths
- explicit frame budget targeting
There is also a :perf popup now, so you can very easily spot what's slowing the editor down.
The editor loop targets 60 FPS, with a lower target when idle. Redox also tracks frame timing so expensive paths are easier to notice.
This has been one of the recurring themes of the project: the editor can be architecturally clean but still feel awful if hot paths allocate too much, scan too much, or render too much. Even one or two occasional dropped frames can make the editor feel sluggish.
Correctness is table stakes. Responsiveness is the thing your hands actually notice.

| Metric | Description |
|---|---|
| frame | Total frame time from start to finish |
| flush | Terminal output flushing time |
| load | File content loading/pumping time |
| snap | Buffer snapshot creation time |
| syn | Syntax highlight processing time |
| ovr | Overlay rendering time |
| line | Line drawing and rendering time |
| stat | Status bar rendering time |
| input | Input processing time |
| oth | Other miscellaneous operations |
The "budget" value at the top shows 16.7 ms, which represents the target frame time for maintaining 60 FPS (1000ms ÷ 60 frames).
UI Polish
There has been a lot of UI polish too.
Some of it is obvious:
- improved status bar styling
- relative line numbers
- a subtle colour column at column 80
- a tiny minimap/scroll progress indicator
- popup background dimming
- improved command mode popup
- an
:aboutscreen - a
:perfscreen
Some of it is less obvious but matters more than it sounds:
- popups have better size guards on tiny terminals
- command mode moved into a nicer popup (inspired by noice.nvim)
- popup backgrounds dim the editor behind them
- ephemeral status messages clear more predictably
escandctrl+cnow cancel popups/modes more consistently- normal-mode cursor positioning clamps to the last real (non-whitespace) character on non-empty lines
The Secret Rain Button
There is a :rain command.
I am not going to over-explain it.
Some features are not roadmap items. Some features are what happens when you have been staring at terminal rendering code for too long and the terminal starts staring back. This one was inspired by one of my favourite Neovim plugins, cellular-automaton. You can now break your code in more ways than one! Don't worry, the buffer can be reset with a simple escape/ctrl+c.
Testing, Testing
The test suite has grown quite a bit too.
At the time of writing, the workspace test run passes:
108 redox-core tests
282 redox-tui tests
390 total tests
The tests now cover a much broader surface area:
- buffer editing
- selections
- visual modes
- text objects
- search
- motion parsing
- multi-buffer sessions
- file loading
- explorer operations
- syntax highlighting
- delimiter analysis
- scope guides
- status bar behaviour
- popup wrapping
- rendering fast paths
- undo/redo behaviour
- smart indentation
What Works Now
Here is the current "actually usable editor" list:
- open files directly
- open full directories with
redox .orredox <path> - multi-buffer editing
- buffer switching with
:bn,:bp, and:ls - file explorer with editable directory operations
- normal, insert, command, search, visual, visual line, and visual block modes
- private register yanks/deletes/pastes
- system clipboard yank/paste support
- undo/redo
- slash search and repeat search
f/tcharacter search motions- common motions like
h/j/k/l,w/b/e,gg,G,0,_,$ - operator commands like
dd,yy,cc,D,d$,diw,ci",dap - Tree-sitter syntax highlighting for a bunch of languages
- fallback syntax highlighting for unsupported languages
- delimiter pair highlighting
- active scope guides
- smart indentation
- relative line numbers
- colour column
- performance popup
- about popup
- incremental file loading
In case you didn't notice, this is a lot more than last time.
Last time Redox was "basically functional."
Now it is "functional enough that missing features are starting to feel like editor design decisions instead of obvious holes."
That is a dangerous stage. This is where opinions begin to form.
Still Simple, On Purpose
There are still plenty of intentionally simple pieces.
Redox still does not have:
- an undo tree UI
- persistent undo history
- fuzzy file finding
- project-wide grep
- LSP support
- tabs/windows/splits
- configuration
- macros
- command history
- regex search
- syntax-aware editing beyond indentation/highlighting
- a plugin system
...but those are all planned additions. There are some things that I'm either unsure about adding, or definitely won't add. For example, even though MinUI has full mouse support (cursor position, clicking, dragging, etc.), I highly, highly doubt I'll add mouse support to Redox. I've always had my mouse disabled in Neovim, and I similarly want Redox to stay focused on the keyboard.
The next obvious areas are probably fuzzy finding, stronger search, more complete Vim motions, and an undo tree. I also want to keep tightening performance and correctness before piling on too much new surface area.
Roadmap Snapshot
| Stage | Status | Main Focus | Highlights |
|---|---|---|---|
| Core editor foundation | Done | Editing model | Basic implementations of: rope buffer, positions, motions, selections, undo/redo, file I/O |
| Language awareness | Done | Syntax and structure | Tree-sitter highlighting, delimiter pairs, scope guides, smart indentation |
| Real editor workflows | Mostly done | Daily editing flow | Multi-buffer sessions, explorer, visual modes, registers, search, text objects |
| Performance and scale | Done for now (but always ongoing) | Large files and responsiveness | Incremental loading, cached rendering, background analysis, search optimizations |
| Power features | Planned | Editor depth | Undo tree UI, fuzzy finding, grep, more motions, stronger command workflows, LSP support, Git integration, etc. |
Closing Thoughts
This project started as a capstone idea, which meant the original goal was something like:
Can I build a text editor with a clean architecture and enough functionality to prove the design?
As far as I'm concerned, that goal has now been met and surpassed. It was technically "good enough" in the last devlog. Now the question is more like:
Can I keep growing this into an editor that feels good enough that I actually want to use it?
That is a much harder question, but I think I'm already mostly there. The only things that really feel lacking from my comfy Neovim config are fuzzy finding, LSP support, and Git integration.
The project has crossed an invisible line where the architecture is no longer just supporting an MVP; it is supporting real editor behaviour. Things like multiple buffers, async-ish background analysis, syntax-aware features, visual editing, file operations, and enough Vim-shaped grammar that editing feels blazingly fast.
There is still a long way to go, but Redox has become much more than the basic little terminal editor from the first post. I love this editor, and I'm pumped that I now have more free time to work on it.
It has organs now.
Possibly too many.
But they are mostly passing tests.