Notes on Mastering Emacs: Chapter 4: The Theory of Movement
The following notes were taken while discussing Chapter 4 of the book Mastering Emacs by Mickey Petersen (2022 edition) in book discussion group meetings.
An index of notes for all chapters are available at notes.html.
Contents
Basics
The following complete key sequences illustrate a few basic commands:
-
C-x C-f foo.txt RET
: Edit file namedfoo.txt
. -
C-x C-s
: Save current buffer to file. -
C-x b *scratch* RET
: Switch to the buffer named*scratch*
. -
C-x k *scratch* RET
: Kill the buffer named*scratch*
. -
C-x k RET
: Kill current buffer. In fact, like the previous key sequence above, typingC-x k
first prompts for the buffer name. However, the current buffer name is selected as the default value already. As a result, typingRET
kills the current buffer. -
C-x C-b
: List buffers. -
C-x C-c
: Exit Emacs. This command offers to save all unsaved buffers before exiting Emacs. -
ESC ESC ESC
: This command exits the current context. What that means depends very much on the context. It performs exactly one of the following actions: If there is an active region, then it is deactivated; if a minibuffer is open, it gets rid of it; if recursive edit is in progress, it quits one level of recursive editing; if multiple windows are open, it deletes other windows so that the current window becomes the only window in the frame. The aforementioned conditions are tested one by one and as soon as one of the conditions is met, the corresponding action is executed and the other conditions are skipped. -
C-/
: Undo changes. -
F10
: Activate the menu bar.
In my experience, I have found that ESC ESC ESC
is most
useful when a stray minibuffer is open but the cursor is on some
other buffer instead of the minibuffer and I need to close the
minibuffer. Here are some steps that demonstrate this usage:
-
Type
M-x white
and pause. We now have a partially typed command in the minibuffer. -
Now pretend that we get distracted by some imperfections in the text buffer that was open earlier and we want to fix those first. Type
C-x o
to move away from the minibuffer and go back to the text buffer to perform some editing tasks.In this step, we could have typed
C-g
to quit the minibuffer first but we did not do that. We pretended to get distracted by the text buffer and went straight to it from the minibuffer by typingC-x o
. At this point, the cursor is in the text buffer and the minibuffer remains open at the bottom. The open minibuffer can be distracting while performing the text editing tasks. TypingC-g
now will not get rid of the minibuffer because the cursor is no longer in the minibuffer. -
Now one way to close the open minibuffer could be to type
C-x o
to go back to the minibuffer window and typeC-g
. However, there is a more direct way to do this as explained in the next point. -
Type
ESC ESC ESC
to get rid of the minibuffer at the bottom. This works even when the cursor is not in the minibuffer but is in the text buffer instead.
Major Mode Load Order
The chapter mentions the following order for detecting major mode:
- File-local variables
- Program loader directives
- Magic mode detection
- Automatic mode detection
Let us start from the bottom of the list and share some experimental results that illustrate how the major mode detection works.
File-Local Variables
-
Create a text file named
foo.txt
with the following content:#include <iostream> int main() { std::cout << "hello, world\n"; return 0; }
Then open this file in Emacs (say, with
C-x C-f foo.txt RET
). Emacs sets the major mode toText
(i.e.,text-mode
).Type
M-: major-mode RET
to confirm that indeed the value ofmajor-mode
istext-mode
. This happens due to automatic mode detection which determines the major mode based on the file name. In this case it sees that the file name ends with.txt
and enablestext-mode
. We will discuss automatic mode detection further in the section Automatic Mode Detection. -
Now edit the previous file to add file-local variables in the header as follows:
// -*- mode: c++; c-basic-offset: 4 -*- #include <iostream> int main() { std::cout << "hello, world\n"; return 0; }
Now reload the buffer. You could simply kill the buffer with
C-x k
and reopen the file withC-x C-f foo.txt RET
or alternatively, reload the buffer withM-x revert-buffer RET yes RET
.After reloading the buffer, you should see that the
C++
mode (i.e.,c++-mode
) is enabled. As a result, C++ syntax highlighting should be visible. FurtherC-x h TAB
should reformat the code to use 4 spaces for each level of indentation. -
File-local variables may be specified in the footer too as shown below:
#include <iostream> int main() { std::cout << "hello, world\n"; return 0; } // Local Variables: // mode: c++ // c-basic-offset: 6 // End:
Now reloading the buffer should show that
c++-mode
is enabled and typingC-x h TAB
should reformat the code to use 6 spaces for each level of indentation. -
What happens if the file-local variables in the header and footer contradict each other? To test this out, edit the buffer to have the following content:
// -*- mode: c++; c-basic-offset: 4 -*- #include <iostream> int main() { std::cout << "hello, world\n"; return 0; } // Local Variables: // mode: python // c-basic-offset: 6 // End:
Reloading the buffer should show that
c++-mode
is active. TypingC-x h TAB
should reformat the code to use 6 spaces for each level of indentation. Therefore the mode specified in the header remains effective. For other variables, the ones specified in the footer have precedence.Of course, the example above is intended for curiosity and exploration. In practical use, however, it is best to avoid assigning conflicting values to file-local variables in both the header and footer. Such inconsistencies can lead to confusion and make the effect of the variables difficult to understand.
Occur Mode
TODO: More notes coming up here soon!