I can find an answer to a similar problem here, however although I can get that example to function I can't get what I want to do to work.

Here is the example text to be edited, a partial section of this site http://latexcolor.com/:

Anti-flash white    #F2F3F4 \definecolor{anti-flashwhite}{rgb}{0.95, 0.95, 0.96}
Antique brass   #CD9575 \definecolor{antiquebrass}{rgb}{0.8, 0.58, 0.46}
Antique fuchsia #915C83 \definecolor{antiquefuchsia}{rgb}{0.57, 0.36, 0.51}
Antique white   #FAEBD7 \definecolor{antiquewhite}{rgb}{0.98, 0.92, 0.84}
Ao  #0000FF \definecolor{ao}{rgb}{0.0, 0.0, 1.0}
Ao (English)    #008000 \definecolor{ao(english)}{rgb}{0.0, 0.5, 0.0}

So what I was trying to do was write a macro that would format each of the entries to the following :

% Anti-flash white    
% #F2F3F4 
\definecolor{anti-flashwhite}{rgb}{0.95, 0.95, 0.96}

I can write a macro that will do this - the problem is when there is a large file I want to run the macro over either the whole file or a selection of it. I find that I'm currently guessing how many lines I need to run it for, as the macros are recursive and just keep running.

So how do I visually select the block of text above then write a macro to format it to the given layout?

The end result will be

% Anti-flash white    
% #F2F3F4 
\definecolor{anti-flashwhite}{rgb}{0.95, 0.95, 0.96}

% Antique brass   
% #CD9575 
\definecolor{antiquebrass}{rgb}{0.8, 0.58, 0.46}

% Antique fuchsia 
% #915C83 
\definecolor{antiquefuchsia}{rgb}{0.57, 0.36, 0.51}

% Antique white   
% #FAEBD7 
\definecolor{antiquewhite}{rgb}{0.98, 0.92, 0.84}

% Ao  
% #0000FF 
\definecolor{ao}{rgb}{0.0, 0.0, 1.0}

% Ao (English)    
% #008000 
\definecolor{ao(english)}{rgb}{0.0, 0.5, 0.0}

edit - referenced SO post in full

Here is the referenced post copied verbatim so that people can see what doesn't work (I'm not sure if copy pasting like this is cool, keeps it in one place though);

For example, the following macro capitalizes every word but the first on a line:

:let @q="^dwgU$P"

So running it on the following (where the + lines are selected)

 0000: a long long time ago
 0001: in a galaxy far away
+0002: naboo was under an attack
+0003: and i thought me and qui-gon jinn
+0004: could talk the federation in
 0005: to maybe cutting them a little slack.

With the above normal @q command, produces:

 0000: a long long time ago
 0001: in a galaxy far away
 0002: naboo WAS UNDER AN ATTACK
 0005: to maybe cutting them a little slack.

3 Answers 3


To run a macro on every line you use :{range}normal! <macro>

So for instance if you recorded a macro into q and visually selected the additional lines to apply the macro to, you'd type

:'<,'>normal! @q

This issue you are seeing is because running a macro in parallel assumes that all lines will stay in place. To perform a macro on lines that move you'd have to write a macro that is position aware and recursive. Then you'd run the macro using {count}@<register>

So make sure your macro always ends in the same position it started but on the next line you wish to apply it. I tend to start a macro with a 0 or a ^ so I know I'm starting at the beginning of the line. At the end I just need to move the cursor to the next line and not really mind my column position.

Hack Solution

Since you know when creating the macro that you'll be inserting newline, instead put in a token in its place (for example the word "NEWLINE"). Create your macro to do all the steps in place. Apply the macro across your selection :'<,'>normal @q. The last step is to make your previous selection again using gv and then do a swap of your token :'<,'>s/NEWLINE/\r/

  • Doesn't work - this is the same example as linked in the OP as well. I don't think it takes into account the line changes / creations that are done in the example. Have you tested this?
    – baxx
    Apr 17, 2015 at 18:29
  • 1
    Was having issues loading the link for a moment so I posted the quick answer. You are correct, the line changes affect the {range} portion of the normal command.
    – jecxjo
    Apr 17, 2015 at 19:04
  • Using the {count}@<register> technique works perfectly fine for me. If you don't feel like counting, yank the selection into an empty temporary buffer (or use NrrwRgn) and do 999@<register>.
    – Matt Boehm
    Apr 17, 2015 at 19:06
  • @jecxjo I'm aware of running it with {count}@register, the question was how to apply this to a selection without having to know the exact amount of times it had to be run. Cheers
    – baxx
    Apr 17, 2015 at 19:08
  • 1
    Without writing a function that identifies the line movement, you can't. Even though the normal command is thought of being triggered in parallel it really isn't. The range is changed into line numbers and the macro is run on each line. Since the lines move you can't run it in parallel mode.
    – jecxjo
    Apr 17, 2015 at 19:14

So assuming you have the lines currently highlighted, here's what I would do:

y              "yank the selection
:new<CR>       "open an empty new split
Vp             "paste the lines into the split (replacing the 1 blank line already there)
qa             "start recording macro a
  I%<space><esc> "insert % at beginning of line
  f#hvgel        "select the space between the # and the previous word
  c<cr>%<space>  "replace with enter, %, space
  <esc>          "leave insert mode
  f\hr<cr>       "replace the space before the \ with an enter
  o<esc>         "insert a blank line below the current line
  j              "jump down to the start of the next thing to change
q              "stop recording
999@a          "repeat the macro 999 times
dd             "delete the trailing blank line
ggyG           "copy the lines
:q!            "close the window
gvp            "re-select the selection and paste over it with the new version

Some notes:

  1. Since we copied the text into a separate buffer, we can use the 999@a trick because when the macro hits the bottom of the buffer and tries to go down, it will hit an error and stop.
  2. The NrrwRgn plugin provides a nifty method for editing parts of a file in a temporary window and is a worthy alternative.
  3. There are some multiple cursor plugins that try to solve this problem, but I personally prefer the "good ol' fashioned way"
  4. If you didn't want to split into a separate window and really did want to know the number of lines selected, you could calculate this as (line("'>") - line("'<")) + 1
  • Just had chance to go through this, didn't think of creating a new split to work in that's cool :) I had to change some of the mappings for it to work though. Going from the c<cr>%<space> line: c<cr>%<space> THEN <esc> /\<cr> i<del><cr> o<esc>j Perhaps I've missed something on your post, it didn't seem to work for me though. I guess mapping the whole thing to a macro isn't really doable, a function would probably be easier. cheers :) (clicking "edit" on this comment might make it clearer to see the steps)
    – baxx
    Apr 28, 2015 at 13:53
  • You're right, I forgot to leave insert mode after that change command. I tried this out in vim before writing and did get it working as a macro, but must have made some errors while transcribing it here.
    – Matt Boehm
    Apr 28, 2015 at 14:02
  • 1
    Just tried it again and fixed the two bugs (added <esc>, switched forward slash to backslash). FWIW, the raw contents of my a register after recording the macro were I% ^[f#hvgelc^M% ^[f\hr^Mo^[j
    – Matt Boehm
    Apr 28, 2015 at 14:10

As jecxgo points out, the problem is that the obvious solution of recording a macro that processes a single line and then applying that to the selection of lines with :normal fails because, when the lines are expanded, this messes with the other lines in the range.

jecxgo solves this by not expanding the lines within the macro but instead doing so in a separate step afterwards, and Matt Boehm solves it by moving the lines into a separate buffer for processing. These are both neat tricks!

However, here's an alternative solution which does not require any edits before recording the macro or after running it.

Instead it uses a different trick: it relies on the format of the data to ensure it always edits the correct line within the range, even though each individual invocation of the macro causes the lines to move around.

Recording the macro

Starting from anywhere in the buffer, type:


You now have a macro in your "q register that you can apply to ranges of lines:

  • @q will run the macro on a single line,
  • :%norm @q will run the macro on every line in the file,
  • Making a visual mode selection and then typing :norm @q (which Vim will expand to '<,'>norm @q) will run the macro on the lines in the visual selection.
  • 3,5norm @q will run the macro on lines 3 through 5.

A copy/pastable version

If you just want to try the macro out and don't want to have to type it in, here's a version you can copy/paste into the command line or your .vimrc:

let @q = "qqO\<Esc>/^\\w\<CR>``dd``i% \<Esc>f#i\<CR>% \<Esc>f\\i\<CR>\<Esc>o\<Esc>q"

How it works

Let's break it down:

  • qq First we start recording.

  • OEsc We're going to make sure we're on the correct line by searching forwards. In order to ensure that we're not already on the correct line, we move the cursor upwards from the correct starting point. In order to ensure this works when the the cursor is on the first line in the file, we do so by adding a line, rather than by simply moving the cursor upwards.

  • /^\wEnter Now we search forwards to find the correct starting point for our edits. The search we use looks for a line that starts with a letter or a number. We know that this will skip past any previously processed lines, because they all begin with either % or \, (or are entirely blank).

  • ``dd`` Then we delete the line we just added. (I feel like a simple u should work here too, but it doesn't, and I don't have time to figure out why right now.)

  • i%SpaceEscf#iEnter%SpaceEscf\iEnterEscoEsc Now we are in the right place so we can make our edits. (For simplicity, I'm using a fairly naive set of edits here, as this is unrelated to the real point of this answer, viz. the steps above. If trailing whitespace is an issue for you, sub in the edit commands in Matt Boehm's answer here.)

  • q End the recording.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.