Hello, my name is Charles Choi and welcome to my talk:
"Reimagining the Emacs user experience with Casual Suite."
Casual Suite is a set of opinionated user interfaces to
different modes offered in Emacs. Before I get into
describing Casual in detail, let's first talk about the
existing Emacs user experience. To make Emacs go, people
can either invoke commands by name with
execute-extended-command,
run a command directly with a pre-assigned
key binding, finally, use a mouse menu if it's available.
From human-computer interface research, there is a
concept of recall versus recognition in user interface
design. Let's show their distinction by example. A common
recall interface is password entry. Absent any historical
affordances, a user must directly remember information to
succeed with this interface. In contrast, menus offer
immediate visual cues on what commands are available. This
allows a user to recognize familiar behavior to support
successful selection of it. From user interface research,
the key finding is this. Interfaces emphasizing
recognition are much easier to use than those relying on
recall. In this light, we see that the Emacs user experience
leans too much towards recall. Completion in history can
help tip the scales towards recognition, but only by a
little bit.
This reliance on recall is discouraging to users both new
and old, and that's a shame because Emacs has so many useful
commands. But the kicker is that most of them are
infrequently used. You can't recall them all. At least I
can't. So, a conundrum. While I've been using Emacs since
the early 90s, truthfully, it's been only in this past
decade that I've leveled up in using it. Org Mode, Magit,
Eglot, Avy, and many other packages have transformed how I
use it. I can only deal with so much cognitive load and
physically straining key bindings. So, what to do about it?
Let's bring back an old ideal.
Keyboard-driven menus have been around since TTY video
terminals with mainframes. If you're old enough to recall
working with such interfaces, these terms will seem
familiar. They all worked with the limitations of
text-based video displays.
With keyboard-driven menus, if a command exists but nobody
can find it, it's not really useful. A well-designed menu
can make a command discoverable. If the command is
infrequently used, making it recognizable helps a lot. And
for working primarily with text, having keyboard-only
interactions encourages flow. Given the above, the next
steps seem natural:
augment Emacs with keyboard-driven menus. This is not
saying that I want to obsolete name commands, keybindings,
and mouse menus. They all can happily coexist. Emacs is
large. It can contain multitudes.
Conveniently, Emacs has a built-in library for building
such menus. It's called Transient, and it's been around
since Emacs 28. Developed primarily by Jonas Bernoulli as a
UI toolkit for Magit, Transient has an essential feature
for building great keyboard-driven interfaces.
A transient menu can be pinned and their state updated as
commands are issued from them. This lets us build
interfaces that reflect internal state changes made by
commands issued from the user. This is great because many
modes have stateful behavior, and guess what? Emacs has a lot
of modes.
If you think about it, Emacs modes are akin to the
ecosystem of apps that we see today, but with far less
structure and packaging. A mode, like an app, focuses on
delivering specific behavior to the user. There are many
built-in modes in Emacs, and these modes are complex with
dozens, if not hundreds, of commands. Calc itself has over
1,000 of them. It's frustrating to know that these commands
are there, but I really can't access them via recall.
So I decided to do something about it, and that was to transient
all the modes, or at least the most major ones. This past
summer, I had the time and resources to start building
Transient interfaces for modes that I wanted to more
elegantly use. I decided to call this work Casual. Given its
definition, it seemed like a good fit for the vibe that I
wanted these interfaces to embody.
Design principles that I embraced up front were
handcrafted information architecture and layout. This is
largely an exercise in mapping a mode's command set to a
hierarchical menu structure. I wanted these menus to make
sense to most people. Ideally, users would not have to read
documentation to get at the command that they wanted. Early
on, I quickly learned that it was impossible to maintain the
existing default key bindings when mapping them over to a
hierarchical menu. Also, some bindings I just flat out
disagreed with. I resolved to be friendly, but not
beholden to them. In all of the above, I've gone out of my way
to make clear that my design decisions are opinionated.
Using casual.
To reinforce habit, a common key binding is used per mode to
raise a main menu. This key binding is left to user
preference. For me, that binding is C-o.
Command bindings are mnemonic when possible.
Mode-specific settings are given their own menu. Since
transient menus can be pinned, we can support repeat or
stateful behavior in a mode.
As of this writing, there are 11 modes supported by Casual,
with several more on the way.
Let's look at the Casual menu
for Dired to highlight the design conventions previously
mentioned.
In a Dired Emacs window, the user can invoke their preferred
key binding to call a top-level Casual main menu. This main
menu is displayed at the bottom of the Emacs frame. Zooming
into this menu, we see the commands offered in it
categorized into different sections. Each command has a
key binding, usually a single character shown before its
label. The File section holds commands that act upon the
currently selected item or marked items. The Directory
section holds commands that affect the current directory
or its subdirs within it. The Mark section has marking
commands that allow for aggregate operations. The
Navigation section shows commands that move the point in a
direct buffer. The quick section provides access to
bookmark and buffer list commands. Search and replace
commands are grouped in the search section. New directory
and file creation are given their own section. Finally, at
the bottom of the menu are commands dedicated to Casual menu
navigation.
Casual is conformant to Transient conventions where the
key binding C-g for dismiss one and C-q to dismiss all
menus are honored. Another transient convention is to
reserve the key binding q to quit the current mode. For most
main menus, casual uses the , key binding to invoke a
mode-specific settings menu. Casual also adopts the
common UI convention of using ... >
symbols to denote required input and submenus
respectively.
Some commands are more global or non-mode specific in
nature. A great deal of these commands relate to editing,
which I find to be a prime motivation for using Emacs. Let's
examine one such menu that supports this.
The main menu for Casual EditKit is designed to provide easy
access to editing and editing-related commands. Like the
previous Dired menu, it organizes commands into different
sections.
Commands related to file and buffer operations are in the
File section. Commands for editing text are in the Edit
section. S- or balanced expression commands are given a
dedicated section for their own. More often than not, in
many modes, I find them to do what I want.
The tools section provides access to common tools.
Bookmarks I consider to be an essential feature. If you
haven't used them, it's never too late to start. Emacs
window management commands are given this section.
Commands for search and replace, macros, and projects can
be accessed from here. Finally, the menu navigation
section. Note that register commands can be accessed from
here.
Okay, enough screenshots. Let's look at Casual in action
with a demo of the EditKit menus. Let's start our demo of
casual-editkit with raising the menu, which is bound to
C-o. You'll see the menu pop up here. In
particular, we want to look at the edit operation. We'll
press e and we'll see a number of menu items that allow you to
make editing transformations to the text, be it marking,
copying, killing, transposing, transforming, moving, or
deleting the text. You'll see also that there is a submenu
for rectangle operations. Let's first...
Let's actually dig through and look at what's in the Mark submenu.
You'll see that there are increments of text in which you can
mark. You can mark a word, a sentence, a paragraph, and
balanced expression. If we go back, you'll see a similar
pattern for copying as well as killing. Transposing.
Let's go and try to move a sentence. We have the point there at
hello there. We'll move that sentence around. If we
press s, we can move it backward or forward. In this case,
let's move it forward. We'll press f. You'll see hello
there move up a sentence. Then we can also press b to move
it back. Then press RET to dismiss. Also, if we wanted
to, we can... In this menu particularly, you'll see that we
also have cursor navigation, so we can move the point there.
That's not in all the menus, but in a good part number of the
menus in Casual Edit Kit, you'll see that here. Let's press
RET to dismiss that.
Let's actually look at some rectangle operations here.
In this case, we have a list with
items x, y, and z. Let's say we wanted to prefix each item
here with a string. We'll say we want to put in there
hello. One way of doing that is to make a rectangle. So
if we go into our rectangle menu, first off, what we need to do
is define that rectangle region. We'll press m to mark
where the point is right there. Then we can use our cursor
operation to move the point to define the rectangle. In this
case, it's right at the start there. We can use the string
insert command, i, to insert hello, colon, and then we'll
put a space there to make it look a little nicer. Sure
enough, that's in there.
We can have access to a number of rectangle commands here.
If we wanted to, let's say, number, we can go through that same
operation here, define a region, a rectangle region that
is, and press n. You'll see that it has incremented a
number for each item in that rectangle region. We can also
tap u to undo these operations
and leave that at that.
Sorting. If we select a region here, And we go back. You'll
see that the sort submenu is now enabled. Sorting won't work
unless you have a region started. That's one of the nice
things about transient is that it allows you to visually
enable or disable command items with regards to whatever
the current state or context is here. In this case is
whether or not you have a region highlighted. Let's say we
want to sort these two columns of numbers and so there's a
command called n here which is numeric fields. Let's choose that
here. Sure enough we get that. But there's a nice twist
there. Let's say we wanted to sort on the second column.
Let's move our point back up to here and we'll mark that.
Since everything is in a continuous line, we can sort of
pretend that this region is actually a paragraph
and mark that.
We'll go and select our sorting routine. But now we need to
figure out how to make numeric fields sort on the second
column. In transient, if we press a ?, that
gives us basically a intermediate help section where, if we
press a key binding, it will tell us or load the docstring for
the command that's there. That command in this case is
sort-numeric-fields. It requires an argument. That
argument can be passed using the prefix argument,
C-u. Press q. Let's do that. In this case, we
want to check or use the value 2 and press n. Sure enough,
that region is sorted with respect to the second column.
Before Casual, so many powerful Emacs commands were not
available to me because they were too hard to recall or I
could not discover them. Making Casual has changed that,
letting me reimagine more positively my user experience
with Emacs. If you're interested in any of what I've shown
today, I invite you to try out Casual.
Before I leave, my thanks and acknowledgments
go out to the following people.
First, to Jonas Bernoulli for making Transient and Magit.
Casual would not be possible without your work. Next, to
Psionic-k for writing Transient Showcase. It showed me how I
could build casual. To all the casual users and their
support, I am genuinely appreciative. Finally, to Jon
Snader for writing the kind posts on Casual on the Irreal
website. Thank you.
Casual can be found on MELPA,
and its repository is hosted on GitHub.