Imagine if you could record yourself editing text, then replay that recording 1000 times. Now imagine if you could save snippets of text or positions in variables you can instantly recall. That’s macros and registers. Together, they turn repetitive tasks into a single keystroke and make you look like you’re hacking the Matrix when really you’re just editing a CSV file.
This is where Emacs stops being a text editor and starts being a text manipulation engine that responds to your will.
The simplest way to record a macro:
F3 - Start recording
... - Do stuff
F4 - Stop recording
F4 - Play it back
That’s it. You just automated something.
🚸 IDE Refugee Note: This is like recording actions in Photoshop or Excel, except it works everywhere in Emacs, on any text, in any mode. And it’s been here since the 1970s.
Let’s say you have:
alice
bob
charlie
dave
And want:
User: alice (ID: 1)
User: bob (ID: 2)
User: charlie (ID: 3)
User: dave (ID: 4)
Here’s how:
F3 - Start recordingC-a - Beginning of lineUser: C-e - End of lineC-n - Next lineF4 - Stop recordingNow the magic:
F4 - Applies to bob (but with ID: 1)C-u 2 F4 - Applies to next 2 linesBut wait, the ID doesn’t increment! Let’s fix that…
Emacs has a built-in counter for macros:
F3 - Start recordingC-a - Beginning of lineUser: C-e - End of lineC-x C-k C-i - Insert counter (starts at 0))C-n - Next lineF4 - Stop recordingSet the counter before running:
C-x C-k C-c 1 RET ; Set counter to 1
C-u 3 F4 ; Apply to next 3 lines
Now each line gets an incrementing ID!
You can have multiple macros:
C-x C-k C-k - Execute second-to-last macroC-x C-k C-n - Cycle to next macro in ringC-x C-k C-p - Cycle to previous macroMade a mistake? Edit the macro:
C-x C-k C-e ; Edit last macro
This opens a buffer showing your macro as text:
C-a ; beginning-of-line
User: SPC ; self-insert-command * 6
C-e ; end-of-line
...
Edit it like any text, then C-c C-c to save.
Turn your macro into a function:
C-x C-k n my-awesome-macro RET ; Name it
M-x insert-kbd-macro RET ; Insert as Elisp
This generates:
(fset 'my-awesome-macro
"\C-aUser: \C-e")
Put this in your config and bind it:
(global-set-key (kbd "C-c m") 'my-awesome-macro)
Use search to make macros conditional:
C-s pattern - Search for patternIf search fails, macro stops. Perfect for processing only certain lines.
A macro that calls itself:
F4 (call-last-kbd-macro)C-x q ; Query user during macro
When macro runs, it pauses and asks for confirmation.
Registers are single-character variables that can store:
C-x r s a ; Save region to register 'a'
C-x r i a ; Insert contents of register 'a'
Example workflow:
C-x r s b - Save to register ‘b’C-x r i b - Insert boilerplateC-x r SPC a ; Save position to register 'a'
C-x r j a ; Jump to position in register 'a'
Perfect for jumping between locations:
C-x r SPC h - Mark header locationC-x r SPC f - Mark footer locationC-x r j h - Jump back to headerC-x r j f - Jump to footerC-u 100 C-x r n a ; Store 100 in register 'a'
C-x r i a ; Insert 100
C-x r + a ; Increment register 'a'
Use case: Counters in macros:
(defun my-numbered-list ()
"Insert a numbered list item."
(interactive)
(insert (format "%d. " (get-register ?n)))
(set-register ?n (1+ (get-register ?n))))
C-x r w a ; Save window configuration to 'a'
C-x r j a ; Restore window configuration
This saves your entire window layout:
C-x r w z - Save to register ‘z’C-x r j z - Restore layout instantlyC-x r r a ; Save rectangle to register 'a'
C-x r i a ; Insert rectangle
Combined with rectangle commands, this is powerful for column editing.
This is where the magic happens:
;; Macro that uses registers
F3 ; Start macro
C-x r i h ; Insert header from register
C-n ; Next line
... do stuff ...
C-x r i f ; Insert footer from register
F4 ; End macro
Starting with:
John,Doe,30
Jane,Smith,25
Bob,Johnson,35
Want:
INSERT INTO users (first, last, age) VALUES ('John', 'Doe', 30);
INSERT INTO users (first, last, age) VALUES ('Jane', 'Smith', 25);
INSERT INTO users (first, last, age) VALUES ('Bob', 'Johnson', 35);
Macro:
F3 - StartC-a - Beginning of lineINSERT INTO users (first, last, age) VALUES ('C-s , - Search for comma', 'C-s , - Next comma', C-e - End of line);C-n - Next lineF4 - StopGenerate 100 test users:
(defun generate-test-users (n)
"Generate N test users."
(interactive "nNumber of users: ")
(dotimes (i n)
(insert (format "User_%03d,user%03d@example.com,password%03d\n"
i i i))))
Or with a macro using the counter:
C-x C-k C-c 1 - Set counter to 1F3 - Start macroUser_C-x C-k C-i - Insert counter (formatted with leading zeros),userC-x C-k C-i - Insert counter again@example.comRET - NewlineF4 - StopC-u 99 F4 - Create 99 moreView all registers:
C-x r l ; List registers
C-x r + a ; Append to text register
C-u C-x r s a ; Prepend region to register (with prefix arg)
Save registers across sessions:
(savehist-mode 1)
(add-to-list 'savehist-additional-variables 'register-alist)
Bookmarks are like permanent, named position registers:
C-x r m ; Set bookmark
C-x r b ; Jump to bookmark
C-x r l ; List bookmarks
They’re saved automatically and persist across sessions.
Convert function calls from oldFunc(a, b, c) to newFunc(c, b, a):
F3 - Start macroC-s oldFunc( - Find functionC-f - Enter parensC-M-f - Move forward over first argumentC-x r s 1 - Save to register 1C-d - Delete commaF4 - StopTransform JSON to YAML:
(defun json-to-yaml-macro ()
"Macro to convert JSON to YAML."
(interactive)
(kmacro-set-counter 0)
(while (re-search-forward "\"\\([^\"]+\\)\": " nil t)
(replace-match "\\1: ")
(when (looking-at "\"\\([^\"]+\\)\"")
(replace-match "\\1"))))
;; Step through macro execution
(setq kmacro-step-edit-mini-window-height 15)
C-x C-k SPC ; Step-edit macro
;; See what macro does
C-x C-k C-v ; View last macro
;; Ring management
C-x C-k C-d ; Delete head of macro ring
The Appendix awaits with a survival guide for IDE refugees—tips and tricks to make your transition from VS Code/IntelliJ/Sublime as smooth as possible.
But now that you know macros and registers, you’ve unlocked Emacs’s automation superpowers. You can record any sequence of actions and replay it endlessly. You can store and recall text, positions, and window configurations instantly. You’re not just editing text—you’re programming your editing.
“I spent 2 hours writing a macro to save 5 minutes of work. Totally worth it because I’ll use it again. Maybe. Probably. Okay, it was just fun.” —Every Emacs User Eventually