[{"data":1,"prerenderedAt":426},["ShallowReactive",2],{"lesson-2026-04-02":3},{"id":4,"title":5,"body":6,"date":409,"description":410,"difficulty":411,"extension":412,"format":413,"meta":414,"navigation":344,"path":415,"progression":230,"seo":416,"stem":417,"subtopic":418,"tags":419,"tldr":424,"topic":53,"triggered":344,"__hash__":425},"lessons\u002Flessons\u002F2026-04-02.md","Macro Composition: Building Complex Vim Automations",{"type":7,"value":8,"toc":399},"minimark",[9,13,32,40,45,48,64,75,82,86,121,124,132,135,159,163,169,172,188,200,204,210,213,234,237,246,250,256,259,268,271,275,281,287,295,310,314,321,325,392,395],[10,11,5],"h1",{"id":12},"macro-composition-building-complex-vim-automations",[14,15,16,17,21,22,25,26,31],"p",{},"Most Vim users record macros the traditional way: ",[18,19,20],"code",{},"qa",", fumble through some commands, hope nothing breaks, then ",[18,23,24],{},"q",". But ",[27,28,30],"cite",{"index":29},"9-21,9-22,9-23","you shouldn't compose macros on the fly — if a macro is more than three keystrokes, compose it directly in a scratch buffer",".",[14,33,34,35,39],{},"Macros are text. Once you internalize this, everything changes. ",[27,36,38],{"index":37},"9-1,9-13","Running a macro is simply instructing Vim to take the contents of a register and execute them as keystrokes",". This means you can write, edit, debug, and version-control your macros like any other code.",[41,42,44],"h2",{"id":43},"constructing-macros-as-text","Constructing Macros as Text",[14,46,47],{},"Instead of recording, build macros by setting register contents directly:",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-vim shiki shiki-themes github-light",":let @q = 'I\u002F\u002F ^[A (added)^[j'\n","vim","",[18,56,57],{"__ignoreMap":54},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,52],{},[14,65,66,67,70,71,31],{},"The ",[18,68,69],{},"^["," represents the Escape key. ",[27,72,74],{"index":73},"8-21,8-22,8-23","To insert special keys like Escape and Enter, use Ctrl+v followed by the key — you'll get ^[ for Escape, ^M for Enter",[14,76,77,78,31],{},"Better yet, use single quotes to avoid escaping nightmares. ",[27,79,81],{"index":80},"7-22,7-23","Single quotes allow you to type almost everything literally, whereas double-quotes require escaping characters like backslashes and quotes",[41,83,85],{"id":84},"the-iterative-macro-development-process","The Iterative Macro Development Process",[87,88,89,97,106,112],"ol",{},[90,91,92,96],"li",{},[93,94,95],"strong",{},"Write the macro in a scratch buffer"," — open a new buffer and type out your intended keystrokes",[90,98,99,102,103],{},[93,100,101],{},"Test incrementally"," — select portions and execute with ",[18,104,105],{},"@\"",[90,107,108,111],{},[93,109,110],{},"Refine and debug"," — edit the text directly, no re-recording required",[90,113,114,117,118],{},[93,115,116],{},"Load into a register"," — ",[18,119,120],{},":let @q = 'your-macro-text'",[14,122,123],{},"Consider this macro for wrapping function calls with error handling:",[49,125,130],{"className":126,"code":128,"language":129},[127],"language-text","ciw^Rtry { ^[pA } catch(e) { console.error(e); }^[f{%\n","text",[18,131,128],{"__ignoreMap":54},[14,133,134],{},"Breaking it down:",[136,137,138,144,150,153],"ul",{},[90,139,140,143],{},[18,141,142],{},"ciw"," — change inner word (replace function name)",[90,145,146,149],{},[18,147,148],{},"^R"," — paste from register",[90,151,152],{},"Build the try-catch structure",[90,154,155,158],{},[18,156,157],{},"f{%"," — jump to opening brace, then to matching closing brace",[41,160,162],{"id":161},"macro-editing-and-repair","Macro Editing and Repair",[14,164,165,31],{},[27,166,168],{"index":167},"8-30,8-31,8-32,8-33","Registers are shared across recording, delete and yank commands. When you call a macro, the register content is treated as commands. So editing a register automatically updates the macro behavior",[14,170,171],{},"To inspect macro content:",[49,173,175],{"className":51,"code":174,"language":53,"meta":54,"style":54},":reg q    \" View register q contents\n\"qp       \" Paste macro content to edit\n",[18,176,177,182],{"__ignoreMap":54},[58,178,179],{"class":60,"line":61},[58,180,181],{},":reg q    \" View register q contents\n",[58,183,185],{"class":60,"line":184},2,[58,186,187],{},"\"qp       \" Paste macro content to edit\n",[14,189,190,191,194,195,199],{},"After editing, reload with ",[18,192,193],{},":let @q = 'new-content'",". ",[27,196,198],{"index":197},"1-17","Macros are just text stored in named registers",", so treat them like any other text object.",[41,201,203],{"id":202},"combining-macros-with-ex-commands","Combining Macros with Ex Commands",[14,205,206,31],{},[27,207,209],{"index":208},"9-15,9-16","You needn't restrict yourself to single-keystroke vi commands when composing macros. You can include Ex commands as well",[14,211,212],{},"Powerful pattern: macro + range operation:",[49,214,216],{"className":51,"code":215,"language":53,"meta":54,"style":54},":let @f = 'I    ^[>>j'                    \" Add 4 spaces and indent\n:15,25norm @f                            \" Apply to lines 15-25\n:%norm @f                                \" Apply to entire file\n",[18,217,218,223,228],{"__ignoreMap":54},[58,219,220],{"class":60,"line":61},[58,221,222],{},":let @f = 'I    ^[>>j'                    \" Add 4 spaces and indent\n",[58,224,225],{"class":60,"line":184},[58,226,227],{},":15,25norm @f                            \" Apply to lines 15-25\n",[58,229,231],{"class":60,"line":230},3,[58,232,233],{},":%norm @f                                \" Apply to entire file\n",[14,235,236],{},"Or embed substitutions within macros:",[49,238,240],{"className":51,"code":239,"language":53,"meta":54,"style":54},":let @c = ':s\u002Ffunction\u002Fasync function\u002F^Mj'\n",[18,241,242],{"__ignoreMap":54},[58,243,244],{"class":60,"line":61},[58,245,239],{},[41,247,249],{"id":248},"recursive-macros-for-complex-patterns","Recursive Macros for Complex Patterns",[14,251,252,31],{},[27,253,255],{"index":254},"10-27,10-28","Recursion occurs when we execute a macro while recording it. Clear the register first with qqq",[14,257,258],{},"Classic recursive macro pattern — process until end of file:",[49,260,262],{"className":51,"code":261,"language":53,"meta":54,"style":54},":let @r = '0\u002Fpattern^M\u003Coperations>j@r'\n",[18,263,264],{"__ignoreMap":54},[58,265,266],{"class":60,"line":61},[58,267,261],{},[14,269,270],{},"The recursion breaks when the search fails (no more matches), making it self-terminating.",[41,272,274],{"id":273},"pro-tips-for-macro-composition","Pro Tips for Macro Composition",[14,276,277,31],{},[27,278,280],{"index":279},"10-11,10-12,10-13","Use mnemonics to remember macros — if you have a macro for modifying functions use f register (qf), n (qn) for numbers. Name it with whatever first thing comes to mind",[14,282,283,31],{},[27,284,286],{"index":285},"9-10,9-11","Don't forget you can prepend a count — 6@a would run the macro 6 times. Compose macros so they make sense when run multiple times",[14,288,289,290,294],{},"For persistent macros, ",[27,291,293],{"index":292},"7-8,7-9","Vim automatically saves registers to viminfo and restores them at startup",". Or explicitly save to vimrc:",[49,296,298],{"className":51,"code":297,"language":53,"meta":54,"style":54},"\" Permanent macro for CSS property formatting\nlet @c = 'I  ^[A;^[j'\n",[18,299,300,305],{"__ignoreMap":54},[58,301,302],{"class":60,"line":61},[58,303,304],{},"\" Permanent macro for CSS property formatting\n",[58,306,307],{"class":60,"line":184},[58,308,309],{},"let @c = 'I  ^[A;^[j'\n",[41,311,313],{"id":312},"pro-tip","Pro Tip",[14,315,316,320],{},[27,317,319],{"index":318},"7-29,7-30,7-31","To append to an existing macro instead of re-recording, use a capital letter. If you recorded with qa...q, append with qA...q",". This lets you iteratively build complex macros without losing your work.",[41,322,324],{"id":323},"example","Example",[49,326,328],{"className":51,"code":327,"language":53,"meta":54,"style":54},"\" Convert array destructuring to object destructuring\n:let @d = 'f[ci[{^[f]s}^['\n\n\" Before: const [a, b, c] = getData()\n\" After:  const {a, b, c} = getData()\n\n\" Apply to multiple lines:\n:10,20norm @d\n\n\" Or make it recursive to handle nested structures:\n:let @D = 'f[ci[{^[f]s}^[j@D'\n",[18,329,330,335,340,346,352,358,363,369,375,380,386],{"__ignoreMap":54},[58,331,332],{"class":60,"line":61},[58,333,334],{},"\" Convert array destructuring to object destructuring\n",[58,336,337],{"class":60,"line":184},[58,338,339],{},":let @d = 'f[ci[{^[f]s}^['\n",[58,341,342],{"class":60,"line":230},[58,343,345],{"emptyLinePlaceholder":344},true,"\n",[58,347,349],{"class":60,"line":348},4,[58,350,351],{},"\" Before: const [a, b, c] = getData()\n",[58,353,355],{"class":60,"line":354},5,[58,356,357],{},"\" After:  const {a, b, c} = getData()\n",[58,359,361],{"class":60,"line":360},6,[58,362,345],{"emptyLinePlaceholder":344},[58,364,366],{"class":60,"line":365},7,[58,367,368],{},"\" Apply to multiple lines:\n",[58,370,372],{"class":60,"line":371},8,[58,373,374],{},":10,20norm @d\n",[58,376,378],{"class":60,"line":377},9,[58,379,345],{"emptyLinePlaceholder":344},[58,381,383],{"class":60,"line":382},10,[58,384,385],{},"\" Or make it recursive to handle nested structures:\n",[58,387,389],{"class":60,"line":388},11,[58,390,391],{},":let @D = 'f[ci[{^[f]s}^[j@D'\n",[14,393,394],{},"Macros aren't magic spells you record once and hope work. They're code. Write them, debug them, version them. Your future self will thank you when that 47-keystroke transformation just works.",[396,397,398],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":54,"searchDepth":184,"depth":184,"links":400},[401,402,403,404,405,406,407,408],{"id":43,"depth":184,"text":44},{"id":84,"depth":184,"text":85},{"id":161,"depth":184,"text":162},{"id":202,"depth":184,"text":203},{"id":248,"depth":184,"text":249},{"id":273,"depth":184,"text":274},{"id":312,"depth":184,"text":313},{"id":323,"depth":184,"text":324},"2026-04-02","Most Vim users record macros the traditional way: qa, fumble through some commands, hope nothing breaks, then q. But you shouldn't compose macros on the fly — if a macro is more than three keystrokes, compose it directly in a scratch buffer.","intermediate","md","deep-dive",{},"\u002Flessons\u002F2026-04-02",{"title":5,"description":410},"lessons\u002F2026-04-02","macro-composition",[53,420,421,422,423],"macros","automation","registers","productivity","Learn advanced macro techniques: building macros from scratch, editing macro content directly, combining macros with Ex commands, and creating recursive patterns for complex text transformations.","F1EJl1Zwfu1akYYDWdX5piM-RA4BCUd9920It2NjpLY",1775249074011]