Editing commands
Beyond raw motion, Macros provides the core Emacs editing commands — region-aware where Emacs is, and each command's edits coalesce into a single undo group so they undo as a unit.
The region and the kill ring
Set the mark with C-Space, then move point; the text between mark and point is the region. Commands that say "region" act on it (or fall back sensibly when there's no active region).
| Key | Command | Action |
|---|---|---|
| C-Space | set-mark |
Start a selection |
| C-w | kill-region |
Cut the region to the kill ring |
| M-w | kill-ring-save |
Copy the region |
| C-y | yank |
Paste the latest kill |
| M-y | yank-pop |
Cycle through earlier kills (after a yank) |
| C-x C-x | exchange-point-and-mark |
Swap point and mark |
| C-x C-h / cmd-a | mark-whole-buffer |
Select all |
The kill ring and the system clipboard are kept in sync by the clipboard commands (cmd-c/x/v), so copying in Macros and pasting in another app just works.
Killing & transposing
| Key | Command |
|---|---|
| C-k | kill-line |
| M-d / M-Backspace | kill-word / backward-kill-word |
| M-z | zap-to-char |
| C-t | transpose-chars |
| C-x C-t | transpose-lines |
| M-^ | join-line |
| C-o | open-line |
Search
Incremental search, Emacs-style:
| Key | Command |
|---|---|
| C-s | isearch-forward |
| C-r | isearch-backward |
Type to extend the match; C-s / C-r again to jump to the next/previous hit; C-g to cancel and return to where you started.
Replace
| Key | Command |
|---|---|
| M-% | query-replace — ask at each match of a literal string |
| C-M-% | query-replace-regexp — same, for a regexp |
M-x replace-string / replace-regexp |
replace every match without asking |
| C-c p r / C-c p % | projectile-replace / projectile-replace-regexp — across the project |
All of them run from point to the end of the buffer, or inside the active region (an evil Visual selection counts). An empty search string reuses the previous pair. An all-lower-case search matches any case, and a lower-case replacement then follows the match's case (FOO → BAR, Foo → Bar). In the regexp versions \1…\9 insert capture groups, \& the whole match, \n / \t a newline / tab.
At each match of a query session:
| Key | Action |
|---|---|
| y / SPC | replace and go on |
| n / DEL | skip |
| ! | replace all remaining |
| . | replace this one and stop |
| , | replace, but stay to look at the result |
| u / U | undo the last / every replacement |
| ^ | back to the previous match |
| E | edit the replacement string |
| q / RET | stop (any other key stops too) |
| ? | help |
The whole session is a single undo step. The keys work the same under evil and helix (M-% falls through the modal keymaps).
The project versions find the files with ripgrep (gitignore-aware), then run a query session through each file in turn: ! finishes the current file, Y replaces everything in all remaining files, N skips to the next file. At the end you're asked whether to save the files that changed. With evil, bind them on your leader, e.g. (evil-leader-set "n R" 'projectile-replace).
Occur
C-c o (occur) prompts for a regexp (an all-lower-case one ignores case) and opens an *occur* window listing every matching line with its line number, matches highlighted. n / p move between hits; Enter, o or a click jumps to the match in the source; q closes.
Editing results (occur-edit / wgrep)
In *occur* and in the project grep buffer (C-c p G), press e or C-c C-p (i under evil/helix) to make the results editable. Only the text after each file:line: prefix can change — the prefixes are read-only. Then:
- C-c C-c applies every changed line. Sources open in a buffer are edited in that buffer and left modified for you to review and save; files that aren't open are patched directly on disk. A line that changed since the search is skipped and reported.
- C-c C-k throws the edits away.
Comment toggling
M-; (comment-dwim) toggles a line comment on the current line (or region). The comment prefix is a buffer-local string set per mode (comment-string), defaulting to // ; modes like steel-mode set their own.
Sorting & case
These act on the region, or the whole buffer when there's no region:
sort-lines— sort the lines.upcase-region/downcase-region/capitalize-region— case conversion.
Run them by name with M-x, or bind them in your init.scm.
Undo & redo
| Key | Command | Action |
|---|---|---|
| C-/ | undo |
Undo the last change |
| C-? | redo |
Redo |
Undo is branching (Emacs undo-tree): undoing and then making a new edit starts a branch rather than discarding the redone edits, so no work is ever lost. M-x vundo opens a read-only view of the undo tree (g refreshes, q closes); navigate the history itself with C-/ and C-?.
Expanding the selection
Grow the region outward along tree-sitter syntax boundaries — word, then expression, statement, block (Emacs expand-region). It works in any buffer with a tree-sitter grammar, no language server needed.
| Key | Command | Action |
|---|---|---|
| C-= | er/expand |
Widen the selection to the enclosing node |
| M-= | er/contract |
Step back to the previous, narrower selection |
In evil visual state, + and - do the same.
Multiple cursors
Edit in many places at once (Emacs mc.el). Add secondary cursors, then typed text, Enter, and Backspace are replicated at every cursor.
| Key | Command | Action |
|---|---|---|
| C-c C-n | mc-add-cursor-below |
Add a cursor on the next line |
| C-c C-p | mc-add-cursor-above |
Add a cursor on the previous line |
| C-c C-d | mc-mark-next-like-this |
Add a cursor at the next occurrence of the word at point |
| C-g | mc-clear |
Collapse back to a single cursor |
Plain motions move only the primary cursor — add the cursors first, then type or delete.
Jumping to a character
Jump anywhere on screen in a couple of keystrokes (Emacs avy / ace-jump).
| Key | Command | Action |
|---|---|---|
| C-c j | avy-goto-char |
Type a character; every visible occurrence is tagged with a hint letter — press the letter to jump |
In evil normal state this is g s. C-g cancels the jump.
The buffer list
C-x C-b (ibuffer) opens a buffer manager (Emacs ibuffer) listing every open buffer. Enter switches to the buffer on the line, d or x kills it, g refreshes. (To pick a buffer through Helm instead, use C-x b.)