i wish i have to never do this again this is so frustrating.

but here we are. i wrote a text editor. in Zig. ported from kilo, the tiny C editor by antirez.

kilo-zig text editor in action

why even do this

kilo is about 1000 lines of C. it does syntax highlighting, search, cursor movement, all directly emitting VT100 escape sequences. no ncurses. no libraries. just raw bytes to stdout.

i thought porting it to Zig would be a weekend project. i was wrong. the Zig compiler has opinions. many opinions. and it will not let you get away with anything.

what i learned

terminal escape sequences are hell

every terminal emulates VT100 differently. cursor positioning, colors, hiding the cursor, showing it again. all of it is just "please do this thing" followed by "maybe it worked."

zig's type system is a strict teacher

in C you can just cast things around and hope for the best. in Zig, the compiler asks you very politely to explain yourself. @intCast everywhere. error unions that you must handle. optional types that force you to deal with null properly.

it's annoying. it's also correct. every single time.

memory management is not a joke

the editor allocates memory for rows, for the render buffer, for the highlight array. every allocation needs a matching free. Zig doesn't have a garbage collector. neither does C, but at least in C you can pretend memory leaks don't exist until your program eats 4GB of RAM.

in Zig you get defer. it's beautiful and it's terrifying because it means you actually have to think about cleanup.

syntax highlighting is surprisingly complex

it starts simple. "if character is digit, color it red." then you add strings. then comments. then multi-line comments that span across rows. then you realize you need to track whether the previous row ended inside a comment. then keywords. then escaped quotes inside strings.

by the end of it you've written a parser and you didn't even realize it.

short learning notes

things i wish someone told me before i started

[1] editorUpdateSyntax is the most important function in the entire editor. it's where all the highlighting logic lives. the order of checks matters a lot. comments before strings before numbers before keywords.

[2] zig's std.mem.eql is your best friend for string comparison. forget strncmp. just use it.

[3] the hl_open_comment flag on each row is the key to multi-line comment highlighting. when a row's comment state changes, you have to propagate that change to all subsequent rows. it's recursive and it's beautiful and it made me want to cry.

[4] nonprintable characters will mess up your rendering if you don't handle them. Ctrl-A through Ctrl-Z should display as ^A through ^Z with inverted colors. otherwise your terminal beeps at you every time you press a key.

[5] the current_color optimization in editorDrawRows saves a lot of unnecessary escape sequences. instead of writing \x1b[39m before every single normal character, track the current color and only emit when it changes.

[6] search highlighting needs to save and restore the hl array. otherwise the blue highlights from your search query stay forever. static variables and malloc/free do the job, even if it feels ugly.

[7] the keyword system uses a pipe | character at the end of secondary keywords to distinguish them from primary keywords. "int|" vs "if". it's a hack but it works and it's clever.

[8] apparently every thing inserted or deleted needs the whole row remade and shit , never thought so much work went into just writing text ngl.

the zig specifics that got me

@intFromEnum to convert enums to integers. @intCast to convert between integer types. @as(usize, @intCast(...)) every time you need an array index. the compiler will catch overflow bugs that would silently corrupt memory in C.

error unions mean you can't just ignore failures. try or catch. no middle ground. it's exhausting and it's the right way to write software.

optional types (?[]u8) replace nullable pointers. orelse replaces null checks. it's cleaner but it means you can't just pretend null doesn't exist.

could i do it again?

could i build this from scratch without a guide? probably. but it'd take longer ( maybe 2-3 months). this time i leaned on the kilo reference. next time i'd move faster, but also next time i'm not doing it.

would i do it again

no. absolutely not. never.

but i'm glad i did it once. building a text editor from scratch teaches you things you didn't know you didn't know. how the terminal works. how memory works. how parsing works. how even the simplest programs are actually deeply complex systems of interacting parts.

and zig, for all its strictness, is a good language for this kind of thing. it makes you think about every byte, every allocation, every error. you come out the other side a better programmer, even if you want to throw your laptop into the sea by the end.

i wish i have to never do this again this is so frustrating.

but hey, at least now i have a text editor.


the code is at kilo-zig on github. it compiles. it runs. it highlights syntax. i'll probably rewrite it in an easier language (nim or golang maybe) or just fork something smaller. either way, the agent-driven editor is coming next.