From Lunar Phases to Yank-Pop
Tiny Book Club
We have a tiny book club that meets every weekend to read and discuss the book Mastering Emacs, 2022 edition written by Mickey Petersen. We go through a few pages of the book every time we meet, do some demos, and talk about the concepts we learn from the book. In the 36 meetings that we have had so far, we have spent approximately 26 hours together carefully reading every line of the book, trying out the lessons on an actual editor, and experimenting with the new concepts. In the last 3½ months, we have completed four chapters of the book. We are currently reading the fifth chapter. In this post, I'll share what the journey has been like so far and a few interesting things we have learnt.
A big thanks to Mickey Petersen who very graciously granted me the permission to share his book on screen while we discuss the lessons from the book and try out the examples on the editor.
This is the second series of such meetings I have been hosting. The first one was about analytic number theory that began in March 2021. It ran for seven months and finally concluded in October 2021 after 120 meetings. I chose Emacs as the topic for the next series. The book Mastering Emacs by Mickey Petersen seemed like a great choice for it.
Our new discussion group for Emacs began on 16 Dec 2022. We have been meeting over Jitsi during the weekends. Each meeting is approximately 40 minutes long. With my desktop shared via Jitsi, I demonstrate all the concepts we find in the book in my Emacs editor. On an average, we see about 7 participants in each meeting. Some participants are regulars who join the meetings every weekend, follow the lessons, share their comments, etc. It has been a fun experience so far.
Contents
- Tiny Book Club
- Variety of Professions
- Lunar Phases
- Returning to Mark
- Working With Other Windows
- Tab Bars
- Back to Indentation
- Exchange Point and Mark
- Search Toggles
- Word Search Mode
- Occur Mode
- Imenu
- Helm
- Appending Kills
- Yank-Pop
- Join Us
Variety of Professions
Most members of our discussion group come from
the #emacs
channels of Libera and Matrix networks. An
interesting thing I noticed in our Emacs book discussion group is
that we have a good mix of members from diverse backgrounds. In the
previous series of meetings on analytic number theory, almost every
participant had a career in software engineering. But in this
discussion group about Emacs, we have members from various types of
professions such as physics, molecular biology, finance, literature,
etc. It is interesting how we had mostly software engineers in a
mathematics discussion group but a variety of professionals in a
software discussion group!
Some participants of our meetings have several years of experience with Emacs. Others are beginners. However, even those who are quite experienced with Emacs have found that they learnt many new techniques and concepts from the book. In the next few sections, I'll present some of those Emacs functions that were initially not known to some of the experienced Emacs users of our group but were found to be very useful after having learnt them from the book.
Lunar Phases
Yes, we can see the lunar phases calendar right within Emacs! I
don't know if this was interesting to other members of our group, so
I can only speak for myself here. As someone who has been
interested in astronomy since my
childhood days, I found this very exciting. Type M-x
lunar-phases RET
in your Emacs and a new buffer appears with
an output like this:
Tuesday, March 7, 2023: Full Moon 12:39pm (UTC)
Wednesday, March 15, 2023: Last Quarter Moon 2:14am (UTC)
Tuesday, March 21, 2023: New Moon 5:27pm (UTC)
Wednesday, March 29, 2023: First Quarter Moon 2:33am (UTC)
Thursday, April 6, 2023: Full Moon 4:33am (UTC)
Thursday, April 13, 2023: Last Quarter Moon 9:17am (UTC)
Thursday, April 20, 2023: New Moon 4:16am (UTC) ** Solar Eclipse **
Thursday, April 27, 2023: First Quarter Moon 9:21pm (UTC)
Friday, May 5, 2023: Full Moon 5:32pm (UTC) ** Lunar Eclipse **
Friday, May 12, 2023: Last Quarter Moon 2:34pm (UTC)
Friday, May 19, 2023: New Moon 3:56pm (UTC)
Saturday, May 27, 2023: First Quarter Moon 3:24pm (UTC)
It also shows the upcoming eclipses! In fact, there is one coming
up this month! Isn't this nice? I knew that Emacs has all sorts of
fun stuff like M-x zone RET
to zone out with a built-in
screensaver, M-x tetris RET
to play a clone of the
famous puzzle game, M-: (animate-string "hello" 0) RET
to display a string in a fun manner starting off as scattered pieces
spread randomly across the buffer that then slide and come together
to join and form the string. But to have something as obscure as
lunar phases and eclipses available within the editor was a nice
surprise! Thanks to the book, I now use this function often.
For people who are not familiar with Emacs notation for key binding,
note that M-x lunar-phases RET
means
typing alt+x followed by
typing lunar-phases
and then pressing enter.
The notation M-
represents the meta modifier key which
is mapped to alt on modern systems.
Returning to Mark
Most members knew that we can set a mark with C-SPC
(i.e., ctrl+space) and then move around in the
buffer with motion keys to highlight a region that we can cut
with C-w
or copy with M-w
and paste it
elsewhere with C-y
. However, what was new to some
members is the fact that we can also return to a mark just as
easily. The key sequence to return to mark is C-u
C-SPC
. But there is a problem.
When we set a mark with C-SPC
and start moving
around with motion keys, the text between the mark and the current
position of the cursor (known as point in Emacs) becomes a
highlighted region in modern Emacs. This highlighted region can be
annoying while browsing some code. So how do we use the mark as a
place to return to? Barring unusual workarounds like
disabling transient-mark-mode
, is there a simple way?
Yes, there is a simple trick. Type C-SPC C-SPC
to set
the mark! Typing C-SPC
the first time sets the mark
and activates the region. Typing it the second time deactivates the
region. But Emacs remembers the mark that was set. Now continue
with normal editing. Finally, type C-u C-SPC
to return
to mark.
The key sequences involved are pretty convenient.
Typing C-SPC C-SPC
involves holding down
the ctrl key, then typing space twice, and
finally releasing the ctrl key. Similarly,
typing C-u C-SPC
involves holding down
the ctrl key, typing u, then space,
and then releasing the ctrl key. Three keystrokes for
each command. They become muscle memory in no time!
Working With Other Windows
In Chapter 4, The Theory of Movement, there is a section
about working with other windows. There are a number of key
bindings available under the prefix key C-x 4
that
perform operations on another window instead of the current window.
For example, C-x 4 C-f
opens a file in another window.
This could be a faster alternative to splitting the current window
with C-x 2
or C-x 3
and then opening a
file with C-x C-f
. The single key sequence C-x 4
C-f
takes care of splitting the current window into two if
another window does not exist and opening the file there. Moreover
if another window does exist, this key sequence just reuses that
window to open the file there. Pretty nifty!
To see all the commands under the C-x 4
prefix key,
type C-x 4 C-h
. Yet another such command that I found
quite convenient is C-x 4 d
which opens Dired in
another window. This can be useful when we need to browse a
directory without hiding the current buffer.
Tab Bars
One of the many features of Emacs that most of us did not bother paying attention to earlier was the tab bar mode. Turns out it is pretty useful in managing multiple window configurations side-by-side. Each tab can be used as a workspace with a specific arrangement of windows that suits our workflow in that workspace. For example, we could arrange one tab to have three windows to display source code, a debugger, and the current directory in Dired mode. Then we could have another tab with two windows beside each other, perhaps one to display some source code and another to run the terminal.
The tab management commands are very similar to window management
commands. Just like C-x 2
splits a window to create a
new window, C-x t 2
creates a new tab. Just
like C-x 0
deletes a window, C-x t 0
deletes a tab. Similarly, C-x t o
switches to the next
tab. Tabs can be renamed and moved too with some more key bindings.
Chapter 4 has a section called Tab Bar Mode that introduces
these operations in detail.
Back to Indentation
Almost everyone knew that C-a
moves the cursor to the
beginning of the current line. But not many of us knew
about M-m
which invokes the
command back-to-indentation
. This command moves the
cursor to the first non-whitespace character on the current line.
This is another new thing some of us learnt from the book. This can
be quite useful while editing code.
Exchange Point and Mark
Let us say we set a mark somewhere with C-SPC
and then
move around to select a region. However, then we get distracted,
possibly by some typo in our buffer and we begin fixing that. At
this point, the highlighted region disappears. Say after fixing
that typo, we want to resume with the region selection again. What
do we do now? Do we go back to set the mark and begin selecting the
region again? Not really. There is an easier way. We can just
type C-x C-x
to exchange the point and the mark and
highlight the region in between. This has the effect of
reactivating the region.
Note that C-x C-x
exchanges the point and the
mark, so although it conveniently reactivates the region, the point
jumps to where the mark was set earlier. This could be quite far
from where we want the cursor to be right now. If you don't like
that the cursor moves far way to the mark, just type C-x
C-x
once again to exchange the point and mark one more time.
This has the effect of returning the cursor back to wherever it was
while keeping the region activated.
Search Toggles
Say, we begin an incremental search of the literal
string f..
in the current buffer with C-s
f..
but then we change our mind and decide that we want to
perform a regular-expression-based search using the regular
expression f..
? Do we cancel the incremental search
and begin a new regular-expression-based search
using C-M-s
? That's not necessary. Instead we can
toggle the currently ongoing incremental search into a
regular-expression-based search using the toggle key M-s
r
.
Another such nice toggle is M-%
. You are likely aware
of the global key binding M-%
used to invoke
the query-replace
command that performs
search-and-replace operation. However, when typed during an ongoing
incremental search, M-%
converts the ongoing
incremental search to a search-and-replace operation. This is
really useful sometimes. If we are searching for a complicated
string, say, C-s std::vector<int>
but then we
suddenly realise that we want to perform a search-and-replace
operation instead with the same string, we don't really have to quit
the current incremental search and start a new search-and-replace
operation. Instead we can simply change the current incremental
search to a search-and-replace operation by typing M-%
.
Similarly, we can toggle the case-sensitivity of the search
using M-s c
. If we type our search string in all
lowercase (e.g., C-s foo
), then Emacs performs a
case-sensitive search by default. I think this is a reasonable
default behaviour. This is what we want most of the time. When we
do want case-sensitive search for a lowercase string, we can switch
from the currently ongoing case-insensitive search to a
case-sensitive search with the M-s c
toggle.
The moment we introduce an uppercase character in our search string
(e.g., C-s Foo
), Emacs switches to case-sensitive mode.
Although this behaviour may appear peculiar at first, it makes sense
if you think about it. If we have bothered to type an uppercase
character in our search string, we probably care about the case, so
Emacs switches to case-sensitive search in this case. Once again,
if this is not what we want, it is trivial to change the
case-sensitive search to a case-insensitive one using the M-s
c
toggle.
There are a number of other toggles available. Chapter 4 has a pretty long subsection on incremental search. That section discusses these toggles among many other things.
Word Search Mode
Say, we start searching for the string web_server
in
some code buffer with the key sequence C-s web_server
.
But we soon realise that the code has the words web
and server
written together in all kinds of notation
like web->server
, web::server
,
web-server
, and even web server
(perhaps
in inline comments). We now decide that we want to match all of
them. In most other editors, we'll probably have to cancel the
current search and resort to a clever regular-expression-based
search. In Emacs, thanks to the toggles available in incremental
search, we can type M-s w
and the currently ongoing
incremental search will change itself to word search mode where it
now matches all these other ways those two words are written. In
word search mode, Emacs searches for a sequence of words while
ignoring any delimiters in between.
Occur Mode
A very useful feature that has been in Emacs for a long time and yet
was unknown to many of us is the occur mode. Type M-s o foo
RET
and it will pull up all matches for the regular
expression foo
in the current buffer and display the
matches in a new buffer. Now we can stay in the same buffer where
we have our text and jump to the places where the matches are found
using M-g M-n
and M-g M-p
.
It is also possible to convert an ongoing incremental search into an
occur mode search using the toggle M-s o
. For example,
type C-s foo
to start an incremental search for the
string foo
, then type M-s o
and, lo and
behold, we are now in occur mode searching for the
string foo
.
Imenu
Right after the section on occur mode, the book presents a section
about Imenu which was also new to some of us. Type M-x imenu
RET
and then type TAB
to invoke auto-completion
and it shows a list of all interesting places in the buffer to jump
too. For example, if the current buffer is a Python source code
file, then the output of this command includes all functions in the
Python file we can jump to. Use auto-completion to complete a
function name and Imenu takes us to the place where the function is
defined.
After completing the sections on occur mode and Imenu, I remember
multiple members of our group mentioning that they now want to adopt
occur mode and Imenu into their workflow. In fact, it may make
sense to bind Imenu to a convenient key binding. The author of the
book recommends binding it to M-i
. However, I do
use M-i
sometimes to insert spaces until the next
tab-stop column, so I'll suggest a different key binding that is
more consistent with Emacs
key binding conventions.
These conventions suggest that key bindings of the
form C-c letter
are reserved for the users.
Therefore, I suggest the key binding C-c i
to invoke
Imenu. Here is some Elisp code to create this key binding:
(global-set-key (kbd "C-c i") 'imenu)
Helm
Helm is a powerful filter-as-you-type framework. Most of
us already knew about this package. It does too many things and has
too many key bindings. We can barely scratch the surface in a small
section like this. But since we discussed Imenu in the previous
section, I'll mention here that Helm provides a rather nice
interface to Imenu. Helm can be enabled with M-x helm-mode
RET
or with (helm-mode)
in an Emacs
initialisation file. Once enabled, C-x c i
runs
the helm-imenu
command which provides an interactive
interface with all options laid out in a vertical format. We can
use the motion keys to select an option. We can then
type enter to activate the selection and jump to the
corresponding place in the buffer.
Helm also provides a nice interface for occur mode in the form
of helm-occur
command. The key sequence for it
is C-x c M-s o
, which one might argue is not a very
convenient key binding. Nevertheless, the user interface to
navigate the matches is pretty good.
Appending Kills
The key sequence C-M-w
is used to ensure that if the
next command happens to be a kill command, then the killed text is
appended to the last stretch of text in the kill ring. This key
sequence prevents the next kill from creating a new entry in the
kill ring.
To understand what this command does we must first understand that
after a kill command adds some new text to the kill ring, subsequent
consecutive kills append to the same stretch of text in the kill
ring, i.e., consecutive kills form a single large stretch of text in
the kill ring. This can be tested by performing consecutive kills
and then pasting with C-y
. For example, M-d M-d
M-d
kills 3 words and creates a single stretch of text
consisting of those 3 words. The consecutive kills keep adding to
the same kill entry in the kill ring. If we type C-y
now, it would yank the newest entry consisting of those 3 words from
the kill ring and paste it into the buffer.
However, the moment a non-kill command is used, it seals the current
entry in the kill ring. Any subsequent kill command creates a new
entry in the kill ring. For example, M-d M-d M-d C-p M-d M-d
C-p C-p C-y
kills 3 words at first but then it moves to the
previous line sealing that kill entry consisting of 3 words. Then
it kills 2 more words and adds them to a new entry in the kill ring.
Therefore, the final yank command pastes only those 2 words from the
kill ring.
This can be a problem if we want to kill text from various parts of
the buffer and yet create a single entry in the kill ring. That's
when C-M-w
comes useful. For example, M-d M-d
M-d C-p C-M-w M-d M-d C-p C-p C-y
kills 3 words and creates a
single entry in the kill ring consisting of those 3 words. Then it
moves one line up and kills 2 more words but this time it appends
those 2 words to the existing entry in the kill ring. Finally, it
moves two lines up and pastes the entire kill entry consisting of 5
words into the buffer.
Yank-Pop
Although most beginners and experienced users of Emacs know that
killed text goes into the kill ring and we can yank the last kill
from kill ring and paste it into the buffer using C-y
,
for many that's where the usage of kill ring stops. The kill ring,
however, has much more utility than that. It is a ring,
after all! We can keep killing text as we edit text and all killed
text gets added to the kill ring. Now C-y
always yanks
the newest kill in the kill ring and pastes it in the buffer? Can
we recall an older kill? Yes, using the M-y
key
sequence. This key sequence invokes the yank-pop
command that replaces a just-yanked stretch of killed text with an
older kill. It takes a little bit of getting used to but once this
becomes muscle memory, the kill ring becomes a very powerful tool.
We can keep dumping text to the kill ring and keep recalling text
from it while performing our editing activities. The following
exercise shows how C-y
and M-y
can be used
together.
-
Open a new file, say, with
C-x C-f foo.txt RET
and type these five words in a single line:foo bar baz qux quux
. -
Then type
C-a M-d C-g M-d C-g M-d
. At this point three stretches of text have been inserted into the kill ring. TheC-g
between everyM-d
is there only to avoid appending kills to the existing stretch of text in the kill ring. This ensures that we have three separate kills in the kill ring. -
Now type
C-y
. The last kill, i.e.,baz
is now pasted into the buffer. -
Now without typing any other key sequence, type
M-y
. The earlier pasted textbaz
is now replaced with an older stretch of text from the kill ring. Thusbaz
is replaced withbar
. -
Now once again type
M-y
. The earlier pasted textbar
is now replaced with a further older stretch of text from the kill ring. Thusbar
is replaced withfoo
.
Note in the previous steps how we are not supposed to type any other
key between the first C-y
and M-y
.
Similarly, while cycling through the kill ring, we must not type any
other key between the consecutive M-y
key sequences.
While cycling through the kill ring, when we reach the oldest kill,
the next M-y
wraps around and brings back the newest
kill.
Since Emacs 28, the key sequence M-y
also supports
browsing the kill ring and yanking any arbitrary entry from the kill
ring. For example, after trying the above experiment,
type C-g
just to make sure that we are breaking any
existing C-y
or M-y
cycle. Then
type M-y
and a minibuffer prompt appears to yank an
arbitrary kill from the kill ring. If we remember the previous
kill, we can type it out partially and type TAB
to
autocomplete it. Alternatively, we could also type TAB
initially itself to browse all the kills in the kill ring.
Join Us
That was an account of our Mastering Emacs book club discussions so far and a few interesting things we learnt. We have only recently begun reading Chapter 5 that introduces several editing and text manipulation commands. This chapter is 75 pages long and it could take a month or two to complete this chapter. There are two more chapters after that which are shorter in their lengths. They discuss some practical aspects of Emacs along with a discussion on some popular packages. We still have a long way to go before we can complete this book.
If all of this sounds like fun, you are very welcome to join our meetings. Just head over to cc/mastering-emacs/ and there you'll find everything you need to know in order to be a part of our book discussion group and join our meetings. Looking forward to seeing you in our next meeting!
Update on 30 Dec 2023: Our discussions of this book concluded on 30 Dec 2023 after 72 meetings. See the post From Fill Prefix to Tramp for more highlights from these meetings.