e: the Scheme editor you edit while it is running
A lightweight terminal code editor written in Chez Scheme, with no build step and no dependencies. Why its self-modifying design matters for developers working on modest hardware.
I found a lightweight terminal code editor this week that does something most editors only pretend to do: it lets you rewrite it from the inside while it is running. The project is called e, written entirely in Chez Scheme, and it landed on GitHub at paveluv/e.
It barely registered on Hacker News, where it picked up 7 points and 4 comments. That is a shame, because the interesting part is not the editor. It is the architecture.
🔍 What "self-aware" actually means here
Plenty of editors call themselves extensible. Usually that means there is a plugin API, a manifest file, and a restart button. e means something stricter. The README puts it directly:
The editor is a Scheme system.
Concretely, that means:
- You hit
M-xand evaluate arbitrary Scheme against the editor's live top level, with symbol completion, parameter hints as you type, history, and a transcript buffer. - Extension modules are plain R6RS libraries living in
lib/with a.eextension. They import(core)and export aninit!procedure. - Saving a module's source reloads it immediately. Old registrations retract before new ones apply, so nothing accumulates.
Here is the entire shape of an extension:
(library (my-mode)
(export init!)
(import (chezscheme) (core))
(define (init!) (register-mode! "my" '(".my") ...)))
The registration hooks are the whole extension surface:
| Hook | What it changes |
|---|---|
bind-key! |
Keyboard bindings |
register-mode! |
File-type modes by extension |
add-highlighter! |
Syntax highlighting rules |
register-indenter! |
Indentation behaviour |
register-descriptions! |
Built-in help text |
That is it. Compare that to the surface area of a modern editor extension API and the difference in ambition is the point.
⚡ Why a 100ms start matters more here than in San Francisco
The README claims no build or installation step. A checkout runs in place. The first start compiles the libraries; later starts take about 100 ms. Dependencies are Chez Scheme and a Unix-like terminal, and nothing else.
If you develop on a machine with 16GB of RAM and fibre, that is a curiosity. If you are a student on a second-hand laptop, or a freelancer SSH-ing into a cheap VPS over a connection that drops during a power cut, it is a different conversation entirely.
Key takeaway: The constraint that makes e interesting is not minimalism as an aesthetic. It is that the entire editor is small enough for one person to read, understand, and modify in an afternoon. That is a property almost no modern tool still has.
What it ships versus what it deliberately refuses:
| Ships | Deliberately does not |
|---|---|
| Buffers, windows, independent scroll positions | General-purpose window tiling |
| Kill ring with accumulated kills | Graphical UI |
| Incremental search with smart case-folding | Full Emacs compatibility |
| Undo with descriptive labels ("Undo insert 'hello'") | A plugin marketplace |
| Syntax highlighting for Scheme, C and Markdown | Multi-column character handling |
Bracket matching, query-replace (M-%) |
Anything requiring a build toolchain |
| File integrity checks against silent overwrites |
The README is honest that it "is not an Emacs clone. It borrows the ideas that stay small and stops there."
🛠️ Design ideas worth stealing even if you never write Scheme
This is where I think the repo earns a read regardless of your language. Four conventions stood out to me:
- Naming encodes side effects. Procedures ending in
!!prompt the user, take no required arguments, and return void. Single-!procedures act immediately and return something useful. You can read a call site and know whether it will block on input. - One library owns the unsafe stuff. All foreign procedures live in
sys.eand nowhere else. Terminal size detection usesioctlwithLINES/COLUMNSas fallback. Every other file is portable Scheme. - Exports are immutable by language enforcement, not by convention or a linting rule. You cannot monkey-patch the core from a module.
- Reload retracts before it applies. The commonest hot-reload bug is duplicate handlers piling up. They designed that out rather than documenting around it.
Point 3 is the one I keep thinking about. Most of us enforce boundaries with review comments and hope. Here the boundary is a property of the module system.
📊 Should you actually use it?
Being fair to a project with 195 commits, 19 stars and 1 fork at the time I am writing this: it is one person's editor that happens to be public. Treat it accordingly.
| If you are... | My honest read |
|---|---|
| Shipping client work on a deadline | No. Use what you already know. |
| Learning how editors work internally | Yes. This is a rare readable example. |
| Curious about Lisp/Scheme but bounced off before | Yes. A working program beats a tutorial. |
| Working over SSH on constrained hardware | Worth an evening's trial. |
| Expecting LSP, Git integration, a debugger | No. That is not what this is. |
There is one genuinely nice touch: a describe command with the R6RS Scheme manual and Chez-specific documentation, roughly 1,400 entries, downloadable on demand. Offline language reference inside the editor is worth more than it sounds when your connection is unreliable.
It is MIT licensed, so reading and borrowing from it is unambiguously fine.
💡 What this means for you
I am not suggesting you switch editors. I am suggesting something cheaper: spend two hours reading a program that is small enough to finish.
Most of us learned to program inside tools we could never open up. That is a real gap. You end up treating your editor, your framework and your runtime as weather rather than as software somebody wrote and you could also write.
A few practical moves:
- Clone it and read
sys.efirst. It is the only file touching the outside world. Everything else is logic you can follow. - Try the naming convention in your next project. You do not need Scheme to adopt "this function prompts" versus "this function acts" as a visible suffix.
- Audit your own hot-reload path for the retract-before-apply bug. If you have ever seen a handler fire twice after a save, you have hit it.
- If you want to try Scheme-adjacent thinking without installing anything first, our online Python compiler runs in the browser, and the regex tester is useful when you get to the highlighter chapter of any editor's source.
Bottom line: Small, readable, self-modifying software is not nostalgia. It is the only kind you can fully own. e is a working argument for that, and it costs you a
git cloneto check.
Not everything worth reading is worth adopting. This one is worth reading.