vimtricks.wiki Concise Vim tricks, one at a time.

How do I build a macro programmatically using Vimscript instead of recording it?

Answer

:call setreg('q', keys, 'c')

Explanation

The setreg() function writes any string directly into a named register, letting you construct macro keystrokes from Vimscript expressions rather than live recording. This is invaluable when the macro content needs to include computed values — file paths, line numbers, dynamic patterns — that cannot be known at record time.

How it works

  • setreg(register, content, type) sets the named register to content
  • register is a single character ('q', 'a', etc.)
  • content is a string of keystrokes in the same notation Vim stores internally: use \<Esc>, \<CR>, etc., or the literal control characters via "\x1b"
  • type must be 'c' (characterwise) for the register to behave as a macro; linewise ('l') or blockwise ('b') registers do not work as macros
  • After calling setreg(), run the macro with @q as normal

Example

Append the current line number to each line in a range, without hardcoding the number:

" Build a macro that appends " -- line N" to the current line
let lnum = line('.')
call setreg('q', 'A -- line ' . lnum . "\<Esc>", 'c')
@q

Or build a find-and-replace macro using a computed pattern:

let word = expand('<cword>')
call setreg('q', ':s/\<' . word . '\>/NEW_NAME/g\<CR>', 'c')

Then @q runs the substitution immediately, and @@ repeats it on demand.

Tips

  • Inspect the result with :reg q before running to verify the content looks right
  • To include literal special keys, use "\<Esc>" or \"\<CR>\" in the expression; these are the same escape sequences Vim stores internally
  • Combine with :argdo or :bufdo plus norm @q to replay the programmatic macro across many files

Next

How do I prevent cursor movement in insert mode from splitting the undo block?