(2023-07-29) Switching from (Neo)Vim to... my own text editor
-------------------------------------------------------------
Right now, I kinda feel like a Jedi having built his own lightsaber.

Yes, I got used to writing some tooling myself before, but those mostly were
simple scripts (or not so simple, like FrugalVox or Bopher-NG) and not a 
friggin' fullscreen text editor for VT100-compatible terminals on POSIX 
systems written in under 777 SLOC of pure ANSI C that I'd switch to since 
day one I considered it more or less stable and complete. This is not _the_ 
largest project I have written in pure C (I reckon Equi is the largest so 
far), but it surely is the most important for me personally. Because I use a 
text editor literally every day I use any of my personal computers. And 
writing own text editor for fun is one thing (and it surely has been fun), 
but writing it to replace Vim/NeoVim/Vis/busybox vi/etc as the main tool for 
daily usage is totally different. And there really was a lot to consider, as 
well as a lot to sacrifice. But let's begin from the beginning.

During my week of exploration (two posts before), I also stumbled upon sta.li
and Oasis Linux. I wasn't able to fully build either of them but I became 
more interested in all the lightweight permissive-licensed software that 
could be built statically. Before that, I also had discovered Zig project 
(that itself is based on an LLVM derivative) and its zig cc subcommand that 
allows easy cross-compilation of C code into a bunch of different 
architectures. Among the targets, there was static linking with musl libc. 
So, I started taking whatever pieces of MIT-licensed, BSD-licensed or public 
domain software I could and building them statically against musl (and glibc 
whenever musl was impossible). I was generally satisfied with the resulting 
binaries size, with one notable exception: text editors. Statically built 
Vim binary, for instance, weighs 3433600 bytes, and Vis weighs 644288 bytes. 
Really? An entire programming language runtime (Lua 5.3) weighs 363088 
bytes! Anyway, this was the first time it hit me that, unless I find a 
decent lightweight editor, I must create one myself.

For some more time though, I continued searching. My first options were to
separate busybox or toybox vi, but either of those is too cumbersome to use 
and doesn't even offer line wrapping. And having to use horizontal scrolling 
really makes me puke. There also were some microEMACS derivatives like mg, 
but an editor that requires a double combo to exit simply cannot earn my 
trust. Also, even mg was full of sheer nonsense and functionality I never 
found myself using. I also found a very ancient (ca. 1991) public domain 
editor called ue with puny codebase around 345 SLOC of C, but it physically 
had been unable to handle any terminal size other than 80x25 and didn't 
handle it very well overall, the code required several fixes just to be able 
to compile it with anything at my disposal, and on top of all that, it used 
Ctrl+SDEX for arrows, and other keybinding were no less unusable. I liked 
the idea of such small codebase though, I just needed something more 
practical. This is where I gave up searching and finally started my own 
design, only setting a single hard limit: no more than 1000 SLOC of C.

At first, I wanted to make my editor a vi clone. Yep, that silly. I planned
to only implement a subset of POSIX vi that I actually was using day to day. 
Then, I understood two simple things: first, even Busybox or Toybox 
implementations of vi greatly surpass 1000 SLOC, second, why recreate 
someone else's experience if I can tailor the entire application to my own? 
Key chords are awful, modality is not very obvious, but what else is left? I 
spent a good day or two thinking just about the control scheme I want to 
implement. And I settled on the semi-modal controls: it's like chords but 
you have a prefix sequence (I call it "modstring" in the docs) instead of 
having to keep a modifier key like Ctrl pressed. The modstring I chose 
doesn't conflict with any other application: it is double Escape key press. 
I found it a no-brainer to get used to. Pressing Esc Esc w to save the text 
file is faster than pressing Esc :w Return in vi. It's fascinating how far 
you can go when you ditch all the dogmas imposed onto you over all these 
years.

After the control scheme had been defined, the process went on much quicker.
Besides the usual routine work about terminal I/O and memory management, 
another techincal challenge arose: unlike most "lightweight" alternatives, I 
wanted my editor to be fully Unicode-aware as I also write poetry, mostly in 
Ukrainian. That's why I decided to not perform any internal codepoint 
decoding but just store every UTF-8 character in a 32-bit integer as a 
sequence of 1 to 4 bytes, little-endian. Why little-endian? Because they are 
much easier to output to the terminal or a file with a single loop with 
shifts. And when the shift result is zero, you know that the character 
ended, because no valid UTF-8 sequence can contain a null byte. It's a 
simple but elegant solution that made a firm distinction between a byte and 
a character, and all my further functions operated on characters in 4-byte 
integer boxes instead. And for the lower part of ASCII (<128) these 
operations aren't different from the usual char type anyway.

Several days had also been spent on implementing and perfecting line wraps,
scrolling and cursor positioning. I had to make a sacrifice though and not 
implement whole-word wrapping, as this part already was complicated enough. 
As I write code and poetry much more often than I write posts like this 
(which are then auto-wrapped with the gmi2txt.sh script), I don't mind not 
having whole-word wrapping at all. After this had been done, I fixed the 
file loading process and a bazillion of other small things, implemented 
useful features like bracket matching and external shell runner and also 
added the icing on top of the cake: an in-app help screen that can be called 
with Esc Esc h. This way, you can learn this editor even if you have a 
single C file or a static binary without the readme.

Given all that, I decided to call this editor just what it represents: nne,
no-nonsense editor. You can see in action by building it from my SourceHut 
repo here: [1]. Not only is it below 1000 SLOC but managed to get it under 
the limit of 777 (at the time of writing, it's 774 or so). And I have fully 
switched to it myself and writing this very post within it. Also, it is 
released into public domain, no compromises. Just like public domain 
deserves oksh and SQLite, it also deserves a decent lightweight text editor. 
Of course it's missing a huge number of features (such as line end 
conversion), but that's because I don't really need them in my daily routine 
(e.g. if I need to convert the endings, I use dos2unix first). As such, I 
consider it feature-complete and will only focus on bugfixes and 
optimizations from now on. Although it already is quite fast and small: the 
musl-linked static binary for x86_64 currently weighs just 68880 bytes 
(compare it to Vis or mg, uh-huh).

Of course, nne is highly opinionated. It doesn't have a way of changing the
tabwidth without recompiling, it auto-replaces all tabs with spaces (to type 
a literal tabulation character, you have to press Esc Esc Tab), you can't 
turn off autoindentation but you don't have any syntax highlighting (I 
explained why in my previous post) or visual line numbering (only in the 
status bar). It doesn't even have a real undo, only the modcombo Esc Esc u 
to discard unsaved changes. I explained most of these aspects in nne's 
README and its FAQ, but this vision is something you just have to accept if 
you want to feel comfortable with nne. And if you don't... again, it's 
public domain, it's very small (for ANSI C and this set of features), 
well-commented and comprehensible code, feel free to make any changes to 
make it more suitable to your personal workflow.

For me though, being finally untied from both keychord and modal paradigms,
from any visual overhead such as colors and line number columns and, most 
importantly, from the feature creep that I would never use, made me feel 
much freer than I was before. I really hope this particular lightsaber makes 
a nice addition to my statically built collection and will stay in use as 
long as it can.

--- Luxferre ---

[1]: https://git.sr.ht/~luxferre/nne