Skip to content

Instantly share code, notes, and snippets.

@navicore
Created April 13, 2026 21:30
Show Gist options
  • Select an option

  • Save navicore/07b8cc46444af40e534d3f9e0f4f23a0 to your computer and use it in GitHub Desktop.

Select an option

Save navicore/07b8cc46444af40e534d3f9e0f4f23a0 to your computer and use it in GitHub Desktop.
neovim vim oil macros tuturial

Neovim Macros + Oil: A Deliberate Practice Tutorial

Macro Quick Reference

Before diving in, the core loop:

Keys What it does
qa Start recording into register a
q Stop recording
@a Play register a
@@ Replay the last played macro
8@a Play macro a eight times
Ctrl-A Increment the number under/after the cursor
Ctrl-X Decrement the number under/after the cursor

The golden rule of macros: end your macro with the cursor positioned so that replaying it hits the next target. Usually that means finishing with j (move down one line).

The error-stop trick: macros abort on any failed motion. If f. can't find a . on the line, the macro stops. This is a feature — it means "run until there's nothing left to do" via 999@a.


Setup

Open a terminal in a scratch area and launch Neovim:

mkdir -p ~/tmp/oil && cd ~/tmp/oil nvim .

You should be in an Oil buffer (:Oil if not). You'll see the directory listing. Everything below happens from here.

Create a practice directory: type o to open a new line below the cursor, type practice/ and hit <Esc>, then :w to save. Oil creates the directory. Press <CR> on the practice/ line to enter it. You're now in an empty Oil buffer — your playground.


Exercise 1 — Create Numbered Files with a Macro

Goal: Create file_01.txt through file_10.txt without typing each one.

Steps:

  1. Type the first entry manually: o file_01.txt <Esc>
  2. Now record the macro: qa (start recording into register a)
  3. yyp — yank the current line, paste below. Cursor is now on the new (duplicate) line.
  4. Ctrl-A — increment the number. file_01.txt becomes file_02.txt.
  5. q — stop recording.

You now have file_01.txt and file_02.txt in the buffer, and a macro in register a that duplicates + increments.

  1. 8@a — run the macro 8 more times.

You should see file_01.txt through file_10.txt listed.

  1. :w — save. Oil creates all 10 files on disk.

What you practiced: yy, p, Ctrl-A inside a macro, and counted replay with 8@a.


Exercise 2 — Change Extensions: .txt to .md

Goal: Rename every file from .txt to .md.

Steps:

  1. Move to the first file line (gg or 1G to be safe).
  2. qa — start recording into register a.
  3. f. — jump to the . in .txt.
  4. C — change from cursor to end of line (deletes .txt, enters insert mode).
  5. .md — type the new extension.
  6. <Esc> — back to normal mode.
  7. j — move down to the next line.
  8. q — stop recording.
  9. 9@a — run on the remaining 9 files.
  10. :w — save. All files are now .md.

What you practiced: f. (find character), C (change to end of line), and the j-at-the-end pattern.


Exercise 3 — Add a Prefix to Every Filename

Goal: Rename file_01.md to draft_file_01.md (and so on for all).

Steps:

  1. gg — go to the first file line.
  2. qa — start recording.
  3. I — insert at the beginning of the line.
  4. draft_ — type the prefix.
  5. <Esc> — back to normal mode.
  6. j — move down.
  7. q — stop recording.
  8. 9@a — run on the rest.
  9. :w — save.

Note: if you have icons enabled in Oil, I might land before the icon. In that case, use ^i instead of I (go to first non-blank, then insert), or 0w then i. Adjust to taste.

What you practiced: I (insert at line start) in a macro.


Exercise 4 — Remove the Prefix

Goal: Undo the previous exercise — turn draft_file_01.md back to file_01.md.

Steps:

  1. gg
  2. qa
  3. ^ — jump to first non-blank character (the d in draft_).
  4. df_delete through the first _. This deletes draft_ exactly.
  5. j
  6. q
  7. 9@a
  8. :w

What you practiced: df_ — the f + d combination. d + f{char} means "delete from cursor through the next occurrence of {char}." This is a workhorse motion for trimming prefixes of varying length.


Exercise 5 — Rename the Base Word

Goal: Change file_01.md to chapter_01.md (and so on).

Steps:

  1. gg
  2. qa
  3. ^ — first non-blank character.
  4. ct_change to (but not including) _. This deletes file and drops you into insert mode with _01.md still intact.
  5. chapter — type the new word.
  6. <Esc>
  7. j
  8. q
  9. 9@a
  10. :w

What you practiced: ct_ — one of the most useful motions for structured names. c + t{char} changes everything up to (but not including) the target character.


Exercise 6 — Bulk Renumber with Ctrl-A Arithmetic

Goal: Shift all chapter numbers up by 100 — chapter_01.md becomes chapter_101.md, etc.

Steps:

  1. gg
  2. qa
  3. 100Ctrl-A — add 100 to the first number on this line. 01 becomes 101.
  4. j
  5. q
  6. 9@a
  7. :w

You now have chapter_101.md through chapter_110.md.

What you practiced: Counted Ctrl-A. You can use any number — 5Ctrl-A adds 5, Ctrl-X subtracts. This is incredibly powerful for batch renumbering.


Exercise 7 — Multi-Buffer Macro: Add Content to Every File

Goal: Open each file, insert a title line based on the filename, save, return to Oil, and repeat.

This is where macros in Oil get really powerful — you can script a workflow that crosses buffer boundaries.

Steps:

  1. gg — first file.
  2. qa — start recording.
  3. ^ — go to start of filename.
  4. yt.yank to the . (copies chapter_101 into the default register, without the .md).
  5. <CR> — open the file.
  6. O — open a line above (or i if the file is empty — O is safer).
  7. # — type the heading prefix.
  8. <Esc>p — escape, then paste the filename. The line now reads # chapter_101.
  9. :w<CR> — save the file.
  10. - — Oil's "go to parent directory" mapping. You're back in the Oil listing, cursor on the file you just edited.
  11. j — move down to the next file.
  12. q — stop recording.
  13. 9@a

After this, every file has a heading line matching its own name.

What you practiced: cross-buffer macros, yt. (yank-to), using - to bounce back to Oil, combining Oil navigation with file editing.


Exercise 8 — The Gauntlet: Create a Second Set and Interleave

Goal: Create appendix_A.md through appendix_E.md using a macro, then add content to each.

This time, no hand-holding — try to compose it yourself using what you've learned:

  1. Type appendix_A.md manually on a new line.
  2. Record a macro that duplicates the line and increments the letter. Hint: Ctrl-A works on letters too when you set nrformats+=alpha — run :set nrformats+=alpha first.
  3. 4@a to get A through E.
  4. :w
  5. Now record a second macro (use register bqb) that opens each appendix file, inserts # Appendix X as a heading (yanking the letter from the filename), saves, and returns.
  6. 4@b

Bonus: Record a macro into register c that opens each file and appends a second line Status: draft under the heading. Chain it: 4@c.


Cheat Sheet: Motions That Shine in Macros

| Motion | Meaning | Macro use | |--------|---------|-----------| | f. | Find next . | Jump to extension | | t_ | To (before) next _ | Target delimiters | | C | Change to end of line | Replace suffix | | I | Insert at line start | Add prefix | | A | Append at line end | Add suffix | | df_ | Delete through _ | Remove prefix | | ct_ | Change to _ | Replace prefix | | yt. | Yank to . | Copy basename | | Ctrl-A / Ctrl-X | Increment / decrement | Renumber | | j | Move down | Advance to next target |


Mental Model for "Should I Use a Macro?"

Ask yourself: "Am I about to do the same 3+ keystrokes more than twice?"

If yes, record it the second time you do it. By the third occurrence you're already replaying instead of typing. In Oil this comes up constantly — renaming batches, restructuring directories, seeding files with boilerplate. The buffer is the filesystem, so every text macro becomes a filesystem operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment