Page Up & Page Down with Kilo Editor

I’ve recently started going through this brilliant article on creating your own text editor (read: nano/pico clone). If you’ve never built one this article breaks it down into easy to follow chunks.

If you do go through it, you might notice the same thing I did. Page Up and Page Down doesn’t seem to work right (in Mac OS’

If you’re also like me, you might end up googling why those keys don’t work right. Let me break it down for you:

  1. Yes, is capturing the keys
  2. Yes, you can hold Shift while pressing Page Up or Page Down
  3. Page Up and Page Down works in vim

Wait, what? How does it work in vim? Is it actually or is kilo not actually entering the elusive “raw mode”?

Alternate Screen

If you google “terminfo alternate screen” you’ll figure out that terminals have a feature allowing programs to switch to, you guessed it, an alternate screen. This is how vim and other programs show you their full screen content while then returning back without disrupting what you were doing. detects this transition and then treats Page Up and Page Down as expected. Some more googling will show you that we can get this to work by sending the terminal smcup and rmcup commands.

Some people hate smcup and rmcup. There’s definitive tradeoffs but my expectation is that when I enter and exit a terminal text editor I don’t lose my previous screen.

Just add the following in the prescribed sections to fix Page Up and Page Down and add in the terminal “saving” stuff.

void initEditor() {
    write(STDOUT_FILENO, "\033[?47h", 6);    // New (smcup)
    EditorConfig.cursor.x = 0;
    EditorConfig.cursor.y = 0;
    if (getWindowSize(&EditorConfig.screenRows, &EditorConfig.screenCols) == -1) die("getWindowSize");

// ...

void disableRawMode() {
    write(STDOUT_FILENO, "\033[?47l", 6);    // New (rmcup)
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &EditorConfig.orig_termios);

Easy peasy!


Now read this

Using() block hell in C#

So the other day I was working on transforming streams for an encryption layer for work. I ended up having some code that looked similar to this: public async Task SendAsync(byte[] data, Delegate next) { using (var stream = ...) { using... Continue →