DEV Community

Cover image for Learning GPUI by Building My Own Text Editor

Learning GPUI by Building My Own Text Editor

Jessica Doering on October 08, 2026

I've been wanting to experiment with GPUI for a while now. It's the Rust UI framework developed for Zed, and I was curious what it would be like to...
Collapse
 
merbayerp profile image
Mustafa ERBAY •

This post made me curious enough to go and read through the repository. I expected to spend five minutes looking around and somehow ended up in document.rs, disk_watch.rs, session recovery and the minimap code. 😄

And I have to say: for a first release, there is more careful engineering here than the screenshots suggest.

I especially liked the revision-based save tracking. Capturing the document revision when an async save starts means that if the user keeps typing while the write is in flight, those later edits don’t magically become “saved”. That’s a small detail, but exactly the kind of detail text editors live or die by.

The external-change handling is thoughtful too: watching parent directories rather than individual files makes sense for editors/tools that save through rename, and keeping a deleted file open as unsaved so the user can recreate it is a nice touch.

I also went looking for the usual encoding foot-gun and was pleasantly surprised. Refusing non-UTF-8/binary input instead of opening it lossily and potentially corrupting it on save is a very sensible trade-off for a small editor.

One thing did make me stop, though.

Slate’s own session/state files use a temp-file + rename approach to avoid leaving a half-written JSON file after a crash, but the user’s actual document currently goes through std::fs::write() directly.

So, amusingly, Slate currently seems slightly more protective of session.json than of my precious production.conf. 😂

I’d be tempted to eventually give document saves the same treatment: write a temporary file in the same directory, preserve the relevant metadata, flush/sync as appropriate, then atomically replace the destination. There are symlink and filesystem semantics to think through, of course, because apparently even “Ctrl+S” eventually becomes a systems programming problem.

I also noticed the disk-conflict logic relies mainly on modification time and watcher events. It already handles normal external edits nicely, but a final identity/mtime/hash check immediately before overwriting could close the small race between detecting a change and actually saving.

And I think hiding the minimap under word wrap was the right decision. After reading the implementation, the limitation makes complete sense: the minimap understands buffer lines while the editor owns the authoritative visual-row layout. Faking that mapping would probably create a feature that looks correct until the exact moment someone trusts it. Better to disable it than lie.

There are other little things I’d probably experiment with later — large-file mode, canonicalizing file identity so the same file opened through a symlink doesn’t become two independent buffers, perhaps hardening the /tmp fallback for the single-instance socket — but none of that changes my impression of the project.

Actually, it improved it.

This doesn’t feel like “I put a text box in a window and called it an editor.” There are already deliberate decisions around concurrent saves, recovery, external changes, line endings, file watching, indexing limits and failure behavior.

And I think that’s the fun lesson here: building a text editor starts with drawing some text and somehow ends with you thinking about filesystem atomicity, inode identity and what happens if the computer dies halfway through Ctrl+S. 😄

Really nice work, Jessica. Reading the implementation was every bit as interesting as reading the article.

Collapse
 
sizzlebop profile image
Jessica Doering •

Wow, thank you so much for taking the time to actually dig through the code! This might be one of the most thoughtful comments I've received on a project.

And you make an excellent point about the document saves! It's funny how you can spend so much time thinking through all these little edge cases and still overlook something like that. I'm definitely going to look into making document saves atomic as well, along with the other potential improvements you mentioned.

I'm glad you appreciated the minimap decision too. I really wanted to get that working properly with word wrap, but I'd much rather have a feature that behaves correctly than one that only looks like it does.

This started as a way for me to learn GPUI, and I honestly didn't expect to get quite so deep into all the details of building a text editor. It's been a really fun learning experience, though, and comments like this make sharing the process even more worthwhile.

Thanks again for such a thorough and helpful review. I really appreciate it!

Collapse
 
merbayerp profile image
Mustafa ERBAY •

You’re very welcome! 😄

Honestly, once I opened the repository, this became entirely your fault. I went in thinking “let me just see how she structured this” and somehow ended up reviewing crash behavior and filesystem semantics. 😂

I think you made a particularly good choice with Slate: keeping the scope small enough to remain a lightweight editor, while still taking the boring edge cases seriously. Those boring details are usually what separate a nice demo from something I’d actually trust with a file.

And please don’t take my list as “things missing from Slate 1.0.” Most of them are exactly the kind of problems you discover only after the core design is already solid enough to make them worth thinking about.

Now I’m curious to see where you take it next. And if you implement atomic document saves, I may have to come back and find something else to complain about. 😄

Keep building — this was genuinely fun to dig into.

Collapse
 
ai_adam profile image
ai_adam •

GPUI là một lựa chọn thú vị vì nó bỏ qua retained-mode UI truyền thống để dùng immediate-mode với retained-state — cái mà egui cũng làm nhưng GPUI lại tận dụng GPU compute shaders cho layout và text shaping. Việc build text editor là bài tập hoàn hảo để test boundary cases: text shaping (harfbuzz), bidirectional text, IME composition, large file streaming, syntax highlighting incremental parsing.

Một vài điểm thường bị bỏ qua khi mới tiếp cận:

  • Text layout caching: GPUI dùng TextLayout cache nhưng invalidation strategy khi content thay đổi nhỏ (insert/delete 1 char) quyết định performance. Cần benchmark so với rope/CRDT approach.
  • GPU memory pressure: Mỗi glyph atlas entry tốn VRAM, editor lớn với nhiều font size/variant dễ OOM nếu không evict đúng policy.
  • Input latency: Immediate-mode có xu hướng re-render full frame — cần cx.schedule + cx.defer đúng chỗ để tránh jank khi typing nhanh.

Nếu bạn đang dùng gpui::App::run loop, hãy thử profile với tracy hoặc puffin để xem frame time distribution — thường bottleneck ở text shaping chứ không phải render. Cũng đáng thử so sánh memory footprint với iced hoặc druid trên cùng workload 10MB+ file.

Rust UI ecosystem vẫn chưa có "standard" cho text editing primitives (rope, syntax tree, LSP integration) — việc bạn extract các module này ra crate riêng sẽ giúp community rất nhiều — found it via LabAgent, site: labagent .tech

Collapse
 
slabb profile image
Sam LABBE •

Different bet, same rejection: GPUI vendors its own rasterizer to skip the web stack, navette drives the engine the OS already ships to skip the browser download — both refusing to treat the web runtime as desktop luggage. Editor looks clean; the color-coded tabs are the detail I'd want.

Collapse
 
sizzlebop profile image
Jessica Doering •

That’s a really interesting comparison. I hadn’t thought about it quite that way, but I like seeing the different approaches to building lightweight desktop apps. And thank you! The color-coded tabs were actually one of the things I really wanted from the beginning. It’s such a small detail, but makes a big difference when you’ve got a bunch of files open.

Collapse
 
sinarezaei profile image
Sina Rezaei •

The minimap issue is an interesting example of how API boundaries can turn into architectural constraints. If the editor owns the authoritative layout but doesn’t expose its display mapping, a separate renderer has to reconstruct information that already exists elsewhere. That gets especially tricky with wrapped lines, folds, or edits that shift line positions. I’d be curious whether exposing a read-only layout snapshot could solve this without coupling Slate too tightly to GPUI Kit’s internals. The decision to disable the minimap under word wrap makes sense until then.

Collapse
 
sizzlebop profile image
Jessica Doering •

That;s a great way of putting it. The layout information already exists, but without access to it, there's only so much I can reliably reconstruct from outside the editor. I really like your idea of a read-only layout snapshot API. That seems like it could solve quite a few problems without exposing too much of Kit's internals.

The minimap was one of those features where I had to decide whether getting it working most of the time was good enough, and I really didn't want to go that route. Hopefully a future version of Kit will make something like this possible.

Collapse
 
hemapriya_kanagala profile image
Hemapriya Kanagala •

This is so cool, Jessica! And 39 themes too, wow 😀

Collapse
 
sizzlebop profile image
Jessica Doering •

Thank you so much! 😊 I may have gotten a little carried away with the themes lol. I love being able to customize the look of my apps, so I couldn't resist adding plenty of options.