Conditional action in a macro
Asked Answered
S

2

8

In a VIM macro, how are the conditional actions handled?

 if condition is true
       do this
 else
       do something else

Basic example

File content:

_
abcdefg

The macro will do:

G^
if letter is one of the following letters: a e i o u
   gg$i0<ESC>
else
   gg$i1<ESC>
Gx

Repeated 7 times the buffer will be:

_0111011

So, how can I verify if a condition is true and then run an action?

Schizophyceous answered 6/6, 2014 at 16:33 Comment(1)
@romainl Some research on Internet, but actually, I have no idea how this can be done.Feed
J
9

As there's no "conditional" command in Vim, this cannot strictly be done with a macro. You can only use the fact that when a command in a macro beeps, the macro replay is aborted. Recursive macros use this fact to stop the iteration (e.g. when the j command cannot move to a following line at the end of the buffer).

On the other hand, conditional logic is very easy in Vimscript, and a macro can :call any Vimscript function easily.

Your example could be formulated like this:

function! Macro()
    " Get current letter.
    normal! yl
    if @" =~# '[aeiou]'
        execute "normal! gg$i0\<ESC>"
    else
        execute "normal! gg$i1\<ESC>"
    endif
endfunction
Jacklin answered 6/6, 2014 at 18:31 Comment(0)
C
3

One solution is to adopt a more functional (as opposed to imperative) approach e.g. this particular task (and many others) can be done with a substitution:

G:s/\v([aeiou])|./\=strlen(submatch(1)) ? 1 : 0/g<CR>gggJ

i.e.:

G  " go to the first non-blank character on the last line

" replace each vowel with 1 and everything else with 0
:s/\v([aeiou])|./\=strlen(submatch(1)) ? 1 : 0/g<CR>

gg " go to the first line
gJ " and append the line below

Depending on the task, you might find that plugins (such as abolish.vim) package the logic in a more convenient way than dropping to vimscript.

Another approach is to use :global as described here, again moving any additional logic into the command(s) where convenient/necessary. If the text to process isn't already in the right format for :g (which is line-based), it can be split into lines, operated on with :g, and then reassembled/restored.

You can also bundle together a sequence of non-overlapping commands with | (:help :bar) to approximate an if/else chain e.g. to convert:

DMY: 09/10/2011 to 10/11/2012
DMY: 13/12/2011
MDY: 10/09/2011 to 11/10/2012
MDY: 12/13/2011

to:

DMY: 2011-10-09 to 2012-11-10
DMY: 2011-12-13
MDY: 2011-10-09 to 2012-11-10
MDY: 2011-12-13

formatted for clarity (for the :execute usage see here):

:exe 'g/^DMY:/s/\v(\d\d)\D(\d\d)\D(\d{4})/\3-\2-\1/g' |
      g/^MDY:/s/\v(\d\d)\D(\d\d)\D(\d{4})/\3-\1-\2/g
Chickie answered 8/8, 2014 at 20:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.