17

I'm in the process of beautifying some source code using vim. {1}

I've created a couple of vim macros that do the individual steps: removing trailing whilespace, removing empty lines after {, removing empty lines before {, that kind of stuff.

Now, I want to create a macro that executes all the individual macros in sequence. However, once the first recursive macro terminates (because it can no longer find any matches -> matching error), my "wrapper" macro terminates, too.

Is there a way to make a vim macro continue after a submacro generated an error?

{1} I know about automatic reformatters. I might even use them on my current problem. I merely mentioned source reformatting for the example's sake. Do not post any answers about this source beautifier or that. The question is not about code reformatting per se, but about vim macros.

Example:

  • Macro 1 - trimming trailing whitespace - qw/\s\+$d$@wq
  • Macro 2 - deleting empty lines before } - qe/\n\n *}dd@eq
  • Wrapper Macro - retabbing, Macro 1, Macro 2 - qr:retab@w@eq

When I execute the wrapper - @r - it will retab, then execute Macro 1 until no more trailing whitespace are found, then terminate (without executing Macro 2).

Clarification:

What I am looking for is how to call a submacro so that when that submacro terminates, the calling macro continues?

4 Answers 4

15

I suggest you use :try to "absorb" the submacro's error.

Here is a silly example:

:let @a='f|dt|@a'
:let @q=':try|exe "norm! @a"|endtry^Mj0@q'
@q

Your wrapper macro would look something like this:

let @r=':retab^M:try|exe "norm! @w"|endtry|try|exe "norm! @e"|endtry^M'
1
  • While garyjohn provided a workaround for not having an error in a submacro, this is the only answer that actually does handle such an error. I have to apologize though; I did not check your answer in time for you to get the (now expired) bounty... :-(
    – DevSolar
    Sep 25, 2011 at 14:35
28

If the failing command is a substitution that isn't finding its pattern, e.g.,

:%s/foo/bar/

when foo doesn't exist in the buffer, you can add the e flag to ignore that error, e.g.,

:%s/foo/bar/e

See

:help :s_flags

You can tell Vim to ignore the errors from some :ex commands by preceding them with :silent!. See

:help :silent


Edit Following the Addition of Example to the Question

Macro 1 and Macro 2 are both recursive and neither has any explicit mechanism for terminating the recursion. My guess is that one of Vim's internal tests for unlimited recursion is being triggered, which generates an error. If Macro 1 is generating such an error, then that error will terminate the execution of Wrapper Macro right after @w.

My suggestion would be to limit the number of times your macros are being executed by rewriting them as :ex commands and limiting the range of lines over which they are executed. For example:

qw:%s/\s\+$//^Mq
qe:%s/\n\n *}/\r}/^Mq

where ^M means typing your Enter or Return key. I haven't tested those together in your Wrapper Macro, but I think you'll be able to fix any mistakes I may have made.

3
  • It is not a substitution in my case, but a simple find (i.e., /foo). While :silent! indeed did the trick of the recursive submacro not aborting the wrapper macro, now the submacro doesn't terminate at all... and I suspect that making it somehow terminate again would again terminate the wrapper macro too. Seems like I'm stuck. :-\
    – DevSolar
    Aug 5, 2011 at 11:03
  • It's difficult to say what might be wrong without seeing the macros you're using, or a simple example that exhibits the same problem.
    – garyjohn
    Aug 5, 2011 at 15:20
  • Added an example.
    – DevSolar
    Aug 6, 2011 at 14:34
2

What worked for me was a combination of @Peter Rincker and @garyjohn answers. Seperately I just kept getting weird issues.

nnoremap <leader>rt :silent! %s/\s\+$//e<CR>
let @r='\rt'
let @t=':try|silent! exe "norm! @r"|endtry|w^M'

I think the silent! in the <leader>rt, and the pipe characters between the endtry and the w were the keys - simply having the line break after the endtry failed. The trys worked when I'd just enter them, but when run as a macro the error about the search text wasn't found would keep being raised.

1

I routinely create macros on-the-fly when I need to do a repetitive task amongst many files. In my case, I have NERDtree (http://www.vim.org/scripts/script.php?script_id=1658) installed in VIM, which create a vsplit window, and on the left, I have a list of all files in the current directory. In one example, I need to find the first instance of the string MY_TEST in the file, and then delete, if present, a prefix labelled as CUSTOM_PREFIX- from that line, save the file, then move onto the next.

Here's how I have this macro work on about 2000 files in the current directory. I could write a bash script to do this, but it's quicker in VIM, in my case. The + denotes keys being pressed simul

# Start recording a macro sequence to register 'A'

q,a

# Search silently for the string "MY_TEST"; DO NOT report an error
# if the string could not be found.
:silent! /MY_TEST


# Replace, on just the current line, the string "CUSTOM_PREFIX-", with
# nothing (ie: delete it), and suppress any warning messages if the
# string could not be found on the current line.
:s/CUSTOM_PREFIX-//e


# Now issue some UI commands. CTRL-W then and arrow key lets you hope
# between tabs/splits/windows within vim.

CTRL+W,

# Scroll down to the next file in the list in NERDtree



# Open the next file

ENTER

# End the macro

q

# Run the macro a thousand times in VIM

1000,@,a

Hope this helps!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .