2

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
 0003: and I THOUGHT ME AND QUI-GON JINN
 0004: could TALK THE FEDERATION IN
 0005: to maybe cutting them a little slack.
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 '15 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 '15 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 '15 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 '15 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 '15 at 19:14
1

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 '15 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 '15 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 '15 at 14:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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