ked(1) is self-hosting now

I've been working on my own text editor; loosely, I've followed the destroy all software "text editor from scratch" and the kilo tutorials, taking bits and pieces and making them mine. The result is ke(1), or kyle's editor. It's now at the point where it's mostly stable to the point where I am writing this post in it.

A rough comparison of where I am: I have roughly 900 LOC, and mg [1] clocks in at just under 15,000 LOC.

When I went to install it on my netbook, a Lenovo N22 that has a long battery, there was this constant screen flicker. That's because the main loop looked like

while (1) {

You can see how this might cause a lot of flickering (and a higher CPU usage than is strictly necessary) :). Changing process_input to return whether a key was pressed and using that to decide to update seems to work pretty well. If I end up putting a clock in the status bar, that will end up needing to be revisited but I don't want to put the cart before the horse, to speak. The update-on-keypress mechanism seems reasonable to me, and it seems like mg(1) does something similar, so there's that.

int     update = 1;
while (1) {
        if (update) {
        update = process_input();

Another bug that I ran into was caused by confusing the number of rows on the display with the number of rows in a file, e.g. the editor.rows on line 240 should have been editor.nrows (like in line 235).

(gdb) list
235           editor.row = realloc(editor.row, sizeof(erow) * (editor.nrows + 1));
236           assert(editor.row != NULL);
238           if (at < editor.nrows) {
239                   memmove(&editor.row[at+1], &editor.row[at],
240                       sizeof(erow) * (editor.rows - at + 1));
241           }
243           editor.row[at].size = len;
(gdb) bok$ ^D

I'm running into another weird bug that shows up occasionally when deleting files whereby things get all kinds of garbled. I still need to figure that out, but as a fun preview (I've formatted it nicely, which gdb doesn't do):

(gdb) p editor
$1 = {entry_term =
    {c_iflag = 11010, c_oflag = 3, c_cflag = 19200, c_lflag = 536872395,
     c_cc = 0x79cf7a92010, c_ispeed = 9600, c_ospeed = 9600},
   rows = 30, cols = 113,
   curx = -538976267, cury = 44,
   rx = 0, mode = 1, nrows = 45,
   rowoffs = 15, coloffs = 0,
   row = 0x79fe2022800,
   filename = 0x79f51db60a0 "",
   dirty = 366,
   dirtyex = 1,
   msg = 0x79cf7a92068 "C-k q to exit / C-k d to dump core",
   msgtm = 1581440758}

The rows and cols are the current size of the screen, and curx / cury is the cur{rent,sor} position. Somehow, curx has been allowed to go negative, which is most definitely a bug.

I'm planning on separating out a buffer struct from this and separating the concerns (plus support being able to open multiple files). Among the other things I'm going to do is figure out appropriate data types for everything. I already did this with key presses (which are int16_t - plenty of keys and also I can send -1 to indicate an error reading the key).

The more I think about it, the more I have to do. Yanking, selections, undo, etc. And to be honest? I'm really looking forward to it.

[1]This is based on Dr. Callahan's portable mg repo at

Tags: , ,