Textedit

A text editor built from scratch in Rust — GPU rendering via wgpu, rope text buffer, syntax highlighting, and a strict hexagonal architecture with zero GUI framework dependencies.

VS Code is Electron. Electron is Chromium plus Node.js. Chromium has a JavaScript engine, a full HTML renderer, a CSS layout engine, a GPU compositing pipeline. All of that runs just to show you a blinking cursor and some coloured text.

I’m not saying VS Code is bad. But I wanted to understand what a text editor actually needs — stripped of everything else. So I built one from scratch. No framework. No WebView. Just Rust and the GPU.

The rendering pipeline

Every pixel on screen goes through wgpu — the Rust implementation of WebGPU. There’s no GUI framework involved. I built each rendering primitive by hand: glyph cache, text layout, cursor, scrollbar, line numbers, status bar, find modal.

The glyph cache uses fontdue to rasterize glyphs at startup and pack them into GPU textures. When text needs rendering, the engine looks up glyph quads from cache and batches them into a single draw call per frame. A custom primitive renderer handles rectangles for UI chrome. All of it goes directly to a wgpu surface.

This is more work than using a widget toolkit. A lot more. But it means rendering performance is entirely under control — there’s no layout engine making decisions you can’t inspect or override.

The text engine

The buffer is a rope via ropey. Ropes give O(log n) insert and delete at any position — a gap buffer, which most editors use, has to shift data proportional to file size on edits far from the cursor. For a 50-line config file you’d never notice the difference. For a 100MB log file you would. It’s the right data structure and I wanted to understand why.

Cursor movement is Unicode-aware via unicode-segmentation. Pressing the right arrow key should always move one grapheme cluster — not one byte, not one code point. That distinction only matters the moment you have emoji or combining characters in a file, which is exactly when you can’t get it wrong.

Clipboard goes through arboard, file dialogs through rfd, native menus through muda. Syntax highlighting uses syntect with five bundled themes: Dracula, Gruvbox Dark, One Dark, Solarized Dark, and Light.

The architecture

The layers are strictly separated — hexagonal architecture applied to a native desktop app, not a web service:

The editor core has zero dependency on wgpu. Swapping the renderer is a port substitution. I drew this boundary on purpose because I wanted to be certain: the text editing logic and the rendering logic are genuinely separate problems, and conflating them is how editors become unmaintainable.

Key decisions

Built with