riffing with acme

by Sevki — 22 Feb 2020
[pdf and ps]


I get asked "what is that weird arse editor you're using?" by people a lot. I use a bespoke editor, a fork of acme from plan9 from user space from bell labs, that I've modified enough to be called bespoke. Acme is quite different from other editors but some how quite familiar. Anyways more on acme later, since it has become lore to write origin stories for the hacker news cinematic universe, I've decided it would be wise for me to use the format to write a preemptive apology for all the shite decisions I've made that you don't agree with.

Developers tend to argue about stylistic choices quite often; things like tabs vs spaces, snake case vs camel case (python vs perl??), requiring semi-colons/trailing commas vs no semicolons/trailing commas... things that don't impact function, just form. This is because most developers suffer from a condition that requires them to a) always be right and b) do the least ammount of work possible , which as we all know the is to offer unsolicited advice and be shitty when recipient of the adivice doesn't immidiately fall in love with the advice they didn't ask for. Behaviour like this, in addition to enabling extremely mediocre white man thrive, has also led to one of the best displays of superfluous debates tech has ever seen. The editor wars! Which has produced such zingers as

vi has two modes – "beep repeatedly" and "break everything"



I feel bad for the crowd that grew up with millenial text editors, they'll never experience the sense of superiority one feels, towering over a colleauge who is stupid enough to like the wrong or recursive acronym.

Having rambled on quite a bit about how debating editor choices is stupid lets talk about Acme the editor. You see debates don't always need to be about stylistic choices, it's ok to discuss things like speed, memory footprint and usability. So I'll try to do that and keep the subject focused on functionality rather than style. Consider it my experience report.Before we go on if you're not familiar with acme>) take a minute to watch russ's guided tour of acme

Why acme?

I've been using acme as my main editor for a while now. I don't miss much from emacs or vi. I'm ok without syntax highlighting, I don't miss auto-complete all that much and C-x+M-c+M-butterfly is not what it's cracked up to be.

Acme instead of providing an integrated scripting environment, works more as a complimentary system for tools that already exist in your $PATH. If emacs is a operating system, acme would be a terminal emulator. And I've found that to be the correct level of abstraction for most things I want to do with my IDE.

That's not to say there aren't things about it I'd like to change, which is what I intend to write about for the rest of this.

How Acme got it right but got it wrong

At the time it was introduced, acme went all in on the mouse, not any old mouse, a very specific 3 button old mouse.

mouse chords

source: acme mouse

At the time 3-button-mice was ubiquitous, today it's very common for your average computer to have stylus, multi-touch and mouse all on the same device, At the time of writing this using the same device and transitioning between different input methods and screen sizes trough out the day has become very common, you can't work at a startup and not see a bunch of people hastily rip off the cords of from their laptons to run to their next meeting only to discover it's been running over. The reason your average engineer can rip the cords off hastily is that operating systems have been working really hard in trying to make these transitions as smooth as possible. There are many considerations to be made like what happens when you go from two display to one? what happens when you go from a 3 button mouse to a trackpad, etc... but for the most part operating systems get this right and it's abstracted away.

The thing I would suggest acme didn't get quite get right is the specific device type, but exploring a alternative input device was I think the right call. What might not be quite apparent because of their seeming success is emacs and vi aren't all that different from acme. It's just they've been victims of their own success. Let me explain; decades after their respective launches vi and emacs still enjoy the popularity they did when they first came out, but only on traditional form factors. However in the last decade we have seen the emergence of a new form factor; the iPad (does android even have tablets?). And since day one, people have been experimenting with using it as a dumb terminal for development, with varied levels of success. I personally am yet to see iPad adopted as a development device by anyone I know, I think this is because neither emacs nor vi was designed to work with multi-touch input, therefore the experiences using vi or emacs on those devices does not feel as intuitive as the applications designed to run on those devices do. So in that sense I belive vi and emacs suffer from the same flaw, but since no one got code editing on tablet form factor right, it's just not as obvious.

Let me give you a more concrete example, macbooks, and really most notebooks, ship with mutli touch trackpads by default. I've recorded some footage of me using trackpad on a mac to illustrate my point

If using emacs is finger gymnastics using a trackpad to drag something is surely finger figure skating. Genuinely selecting something with a trackpad is just a miserable experience.

But that should not come as a shock, these devices are not point and click devices, infact, they are not click devices at all. They are tap devices.

In addition to it being a miserable experience doing a middle click actualy requires you to hold down the and single tap on the trackpad (or install magicperf). Since we're found ourselves in the unenviable position of having to use the keyboard why not go all in?

✈️ ☕

Another very specific use case for the keyboard is when I'm using a notebook in a confined space, like the counter of a local coffee shop or the traytable of an airliner. There is a clear trend in airtravel where the size of the airline seats are shrinking and with it the space available to flail about your appendages. As one judge over seeing a case against the FAA put it

aircraft seats and the spacing between them have been getting smaller and smaller, while [...] passengers have been growing in size.

The obvious long term solution to this problem is to become zuckerberg rich and fly first class everywhere, but in the short term I'm going to experiment with using the keyboard as the sole input method.

The keyboard experiment is just implementation though, I think there is quite a lot to explore with acme and these newer human computer interfaces, perhaps displaying the tag line on the touch bar is next who knows?


Let's talk pixels! Keyboards and mouse aren't the only things that have changed, so have some of our jokes. Acme came out in the mid-1980s. Back some people would say goodbye to eachother on 31st of december by saying see you next year. Same lame people as they return back January 2nd, "see you next year" when inquired about their new year that 800 × 600 is going to be our new years resolution, 40 years later our ambitions have gone 4K HD!

4k monitors are pretty standard hardwarre that most startups will give out these days. Some (most) use two. On a dual 4k setup, whre each display is holding 4096 × 2304 pixels, the pointer usually of 32 x 32 pixels can be in any of 4096 × 2304 × 2 = 18874368 pixels that would be available. Compare that to the 786432 potential pixels that the pointer could be hiding in a 1024 × 768 resolution, there is a lot more surface area that your eyes need to scan to find the cursor. The cursor that used to take up 0.1 of the screen, has shrunk to take up 0.01 making it 1/10th the size it was in relation to the screen.

Turns out this isn't a problem that is unique to me, osx even has a built in feature that allows you to shake the mouse to locate pointer. If for some reason I'm interrupted and I get back to acme it takes a second or two to figure out which window is active, when it really doesn't have to!

Dark Mode

Another thing that's been bothering me is the light color scheme. Turns out light colours aren't always the best way to go.

pupil response

source: Pupillary response

This is eye dilating to adjust to light. Your eyes do this when the light intensity changes, and repeating it causes strain on your eyes. It's literally a repetative strain injury. The colors themselves don't really matter, the overall light intensity in the overall environment is the major contributing factor in this. So the rule of thumb seems to be when it comes to light vs dark modes the answer is it depends on what environment you're in. If you hangout mostly in light colored websites and you switch between windows frequently use light modes, if the inverse is true use the dark mode. Whic makes sense, the ammount of pixels on the screen does not change, therefore the ammount of information bits our eyes and brain has to process does not either, between different colors.

But don't take my word for it, as renowned computer scientist and californian heliophile Alan Kay puts it;

At Xerox Parc in the early 70s, the Alto’s video display could be used for either, so we did some tests. Those tests indicated less eye strain with predominantly white background under normal ambient conditions, because your eyes would retain roughly the same level of light-dark adaptation going from looking around the room, at paper, etc., and looking at the display.

A dark close up display will induce dark adaptation while looking at it and light adaptation when looking elsewhere. The Engelbart researchers had earlier come to the same conclusion for NLS and went for black text on white screen (and pretty likely for the same reasons — they also thought of their work-station-terminals as being part of larger daily life).

So the general answer to this question seems to be “No”,

It is possible that in a very dark room that a mostly dark background with lighter text on a display might be easier on the eyes (but this would imply a kind of troglodytish existence that is likely not healthy for human beings …)

Unfortunately for me Alan Kay hadn't answered this question at the time I was moving to London. Since the move I have fully dark adapted and my eyes try to #cancel me on twitter when l look at light colors now.

This is a fairly flimsy argument but since the advent of smartphones we have done a lot with OLED panels to make them more efficent in conseving energy in dark modes. At the time of writing this they aren't very popular but it's quite likely that thtey will become standard in a couple of years at which points it's quite likely the darkmode will improve battery life.


Namesake of this blogpost refers to a psuedo programming language/instruction set that I affectiontely call riffing. riffing was originally designed to make acme more useable with a keyboard but as it'll become clear it's not really about keyboards at all.

riffings allows acme to be controlled by writing arbirary byte streams to it's riff file.

what's with the name?

Riff naturally emerged out of necessity to group chords, in the context of acme a riff refers to "brief, relaxed chords repeated over changing scales".

Let's define some of the terminology:

For instance playing the note 0x1B, which is the Esc key, will put acme in riff scale. In riff scale, playing the note 0x73 (s) will select the node under the cursor and put acme in select scale.

Playing the note 0x74(t) will change the scale back to type.


I've modified the windowbutton to change it's color to indicate which scale acme is in.


For the select scale I didn't want to just do hjkl, not that there is anything wrong with it, but it's not a huge improvement over the mouse selection. Infact, the mouse selection will expand the word to it's boundaries which is the whitespace surrouding it. So if we can navigate the document not byte by byte but by it's tokens that would be a more acme-ey (IMWO) way of doing things :)

There are a bunch of different ways to tokenize a document; regexp, yacc and linking against the language specific tooling.

The criteria for this evaluation was the least ammount of language specific code to be written, perhaps allowing us to remove some from the existing code base as well. Find the fastest and most accurate way to do this.

Out of aforementioned 3 options, regexp and grammar parsers offer the same interface regardless of the language, regexp is unfourtunately horrendous for accuracy and language specific parsers are not fast, and don't offer a unified interface (*for static linking). If I were to plot out the options it'd look like this.

ease of use

|            x language agnostic
| x regexp
|              x language specific
+----------------> accuracy

As always, we choose the Joe Rogan of options, the one that's high and to the right, I've started looking in to resources for learning the yacc grammar. During that research I've stumbled across tree-sitter.

Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. Tree-sitter aims to be:

Exactly what I was loking for! Adding tree-sitter is relatively easy, it's written in C99 so it'll also be possible to backport it to harey. Tree sitter consolidates all functionality in a single file called lib.c so adding libtree-sitter to 9hd is very straight forward and requires a single mkfile. Each additional language to support is added in a similar fashion, so acme ends up linking against libtree-sitter, libtree-sitter-c, libtree-sitterr-go and so on... All languages generated by tree-sitter expose the same API, so based on the file extension we select a language and set the parser to tthat language.

Let's dive in to more of the internals and how I'm hoping to keep the code manageable.

// dat.h
struct Window
	QLock	lk;
	Ref	ref;
	Text		tag;
	Text		body;
	Scale 	*next;
// rest of the file omitted 

Each window remembers it's own scale. There is no global scale as of writing this.

// riff.h
typedef struct Scale Scale;
struct Scale
	// riff func here is really 
	rifffunc fn; 

	Rune	*name; // useful for printing out which mode acme is in
    Image *i;
void changescale(Window *w,Scale *r);

We interact with tree-sitter by using a TSParser object. Each acme Text has a pointer to it's own TSParser, tho programatically only the body can be changed, since the tagline parser should always be rc grammar. So to the winsetparser func acme passes the current window.

static Rune Ec[] = { '.', 'c', 0 }; // the file extension in runes
// rest of the languages

TSLanguage* tree_sitter_c(); // forward decleration for the c lib. 
// rest of the languages

Language languages[] = {
        .ext=Ec, // .c files, we repeat this for the headers and c++ files too.
    },  // rest of the languages

winsetparser(Window *w, Rune *name) 
    int i;
    for (i=0; i < 5; i++) {
        if (fileislang(name, &languages[i])) {
            ts_parser_set_language(w->body.parser, languages[i].lang());
            w->body.lang = languages[i].l; // so we can check what language we've set the text to

select scale keys and their notes are as follows

🎹 🎵
c go to first child
n go to next sibling
p go to parent
t back to text mode


Here is a recording of me finding, selecting and changing a node in a C file. If it is the active window it'll draw the buttons depending on the state it's in.

Active Window