Zenith: a console-based text editor in Zig

I created a text editor called Zenith, which started as a small 2kloc project for me to learn Zig but has since grew to include most of the features I need in a real text editor. It’s inspired by Vim and Micro, aiming to offer quick navigation like Vim while maintaining familiar keyboard shortcuts in graphical word processing programs like in Micro.

Zenith is designed to be lightweight, with no extra dependencies. It is in development and currently supports basic text editing, syntax highlighting, and pattern matching through a custom regex-like language. It currently only supports Linux, and works best with xterm-compatible terminals.

Source: zenith-editor/zenith: A very minimal text editor in Zig - Codeberg.org


Original post

Hi folks.

I made a very minimal text editor in order to learn Zig in 10 days. It’s called zed and it’s inspired by antirez’s kilo editor. Clocking in at ~2k lines, this is a terminal editor that supports basic text editing, a clipboard, undos (no redo’s yet unfortunately), text search and line numbers. No syntax highlighting or anything else. It should run on any UNIX system, although I’ve only tested it on Linux.

24 Likes

The naming is a bit unfortunate: https://zed.dev/

5 Likes

Haha, I don’t really have a better name for this project. It’s an editor written in Zig, so zig-editor, but that’s probably too generic of a name. I don’t know of any good names, I’m open to suggestions.

I have renamed the project to zenith. Since the last update I have added UTF-8, a shortcut to duplicate lines, redoing, text replacement, indenting and dedenting, and a lot of changes under the hood.

My plan is to turn zenith into an actual fully fledged text editor. I’m probably going to add syntax highlighting and custom user config. For syntax highlighting, I’m not sure if there are any good regex implementations yet, so I’m gonna implement a custom pattern matching language inspired by Lua’s patterns to match source tokens. I’m gonna experiment with JSON5 as the format for the config file. It seems easy to parse and there are implementations in other languages.

4 Likes

From what I’ve read and seen in videos related to Neovim, I get the impression that Treesitter is to editor developers what LLVM is to compiler developers. Have you considered integrating it into your editor? I understand it handles indenting, highlighting, formatting and a slew of other stuff.

2 Likes

I have heard of treesitter before. It seems like it performs tokenization and grammar parsing not unlike an LSP library, if I understand it correctly. Although it looks way too complicated to be included in a minimal text editor, and it does a lot more than what is needed for highlighting.

From what I have heard it also has some level of support of handling code that is partially broken, which is especially helpful for editors.

1 Like

Once you have written your own pattern matcher and a whole bunch of patterns for different languages you’ll have at least as much complexity as tree-sitter. The tree-sitter core library is actually pretty tiny and very much ideal for a minimal text editor.

Flow’s syntax highlighter uses tree-sitter and is about 100 lines of code. You can have a look here.

I highly recommend you start off with tree-sitter and spend your valuable time and effort adding features to your text editor that actually make it unique and useful.

5 Likes

These two seem to be a little bit of a contradiction, I don’t intend to argue either way, go with what you want to do. (Although keeping things both minimal and highly functional is also an admirable and worthy goal, so maybe they don’t necessarily contradict)

It sounds like you want to reinvent and design everything from the ground up and I think that this especially makes sense, if it is also a project for personal, learning and research. (But doing a lot of research can eat up time quickly)

Overall I am not sure, if it really needs to be completely “fully fledged”, seems more important that it does a sizeable number of things in a very excellent / satisfying way.
Things like vscode basically do everything, but a lot of things in not particular good ways. I think something more geared towards minimalism can shine, by focusing on quality over quantity, even if the quantity slowly increases over time too.

So personally I wouldn’t focus on the “fully fledged” too much, that said I am curious what you will come up with!

3 Likes

So I’ve implemented the LPeg VM in Julia, the project is feature-complete with respect to LPeg, with a few flourishes of my own, I have a ways to go before I’ve added everything I want it to have.

Lately I’ve been reading up on Zig for related reasons, and I’m coming around to the idea of translating this engine into Zig to have a nice fast linkable library. That will be at its own pace, but the result should be quite usable for syntax highlighting, among many other tasks.

1 Like

I’ll be honest and say that I don’t have a strict set of principles in mind when writing zenith. At this point, it’s more of a personal challenge to explore Zig, but I envision zenith as a single-file executable that can be easily plopped in and ran. Although I mentioned it being feature-rich, perhaps “feature-packed” better captures my intent. I’m aiming for something compact but with essential features that I deem relevant, although I will admit it’s going to take a long time for the project to mature.

This month, I ended up implementing a regex engine for my text editor. Was it a good idea? Honestly, looking back at it, I should have used an existing library. I would prefer if that library was in Zig, unfortunately, Zig is a young language (I haven’t seen a suitable regex engine written in Zig for my purposes yet), and I don’t really want to call into external libraries like PCRE. As I’ve stated before, I intended to write a pattern matching engine akin to that from Lua, but I’ve since realized that Lua’s patterns lacked groups, which meant that I couldn’t match, say, escape sequences in string literals. So, what was once 300ish LOC evolved into a basic regex engine with group support and backtracking. I don’t think it was a waste, as the current regex engine is currently used for text search, with plans for syntax highlighting to be implemented.

Initially, I considered using the treesitter library as suggested, but I’m not sure how to integrate it into the project. Since language support for Treesitter comes from external C code separate repos, I have thought about compiling those modules into separate dynamic libraries and loading them on-the-fly. However, I’m not certain if this approach would be ideal, so the plan is scrapped for now.

If anyone’s interested, you can try out the new regex-based search on v0.1.2 by typing a regex onto the find text command, and hitting ctrl+e.

2 Likes

Well if you want me and @AndrewCodeDev we’ve made this if that can be useful for you : Fluent :slight_smile: It has a PCRE regex implementation (although it’s still new and purposefully incomplete), And it’s a single file, so it can be easily embedded if you copy paste the code

What a coincidence! Under the hood the regex engine in zenith is also a bytecode VM, with a design based on this article.

1 Like

Fascinating! I’m familiar with Russ Cox’s work, but had missed this article, or more likely forgotten it.

The Split operator is interesting, LPeg and VMs like it use a stack (not the program stack, heap-allocated) and push a choice frame to achieve what the Split operator does. The stack is already necessary because PEGs support recursion. I’m sure that overhead would prevent using OS threads to implement the split, but I bet there are some interesting opportunities there once async comes back.

I found Roberto Ierusalimschy’s paper on LPeg to be excellent, and was critical for reimplementing the VM in Julia (the codebase was also very much necessary, there are some subtle differences which reflect further optimizations). You’ll enjoy it if you like such things.

3 Likes

It took me a while, but I finally got text wrapping into zenith. Here’s a screenshot showcasing text wrapping with multi-byte (and full-width) characters:

In the UX department I also added a help screen, which you can bring up with Ctrl+H (if only this feature was standard in other editors, I think only nano shows the user the default keybindings):

I will be adding syntax highlighting in the next version.

5 Likes

After a week of work, I’m releasing v0.3.0, with support for syntax highlighting and line wrapping.

For anyone’s interested in trying it out, you should setup zenith’s configuration as outlined in here. Simply copy the following into ~/.config/zenith/zenith.conf

[[highlight.zig]]
path = "highlight/zig.conf"
extension = ".zig"

And copy the sample highlighting file into ~/.config/zenith/highlight/zig.conf.

2 Likes

I have released v0.3.4, with an accompanying binary tarball for x86-64 Linux. I would consider Zenith to be feature complete, and it’s pretty much usable as an everyday editor.

Link to the latest release: Releases - zenith-editor/zenith - Codeberg.org

It would be nice to know if this requires Zig 0.12.0, or master (or some other version), before trying. Maybe something for the README?

The zig version requirement is in the Building from source section. It’s the collapsible detail box just below the Build section.

Sorry, I skimmed through that, looking for a version number, but missed that the triangle indicated an unfoldable section.