4

I want to do a search and replace in a latex file as follows:

:%s/\\todo{.*}/\1/gc

That should transform texts like "abc \todo{def} ghi" into "abc def ghi". It happens that when the \todo{...} command is inside another one, vim tries to match the second (closing) bracket with that of the outer command. For instance, "abc \textbf{def \todo{ghi} jkl}" becomes "abc \textbf{def ghi} jkl" when it should be "abc \textbf{def ghi jkl}".

Is there a way to match the corresponding bracket?

Edit:

Sorry for not pointing that out before. It would be nice if it could match exactly the correspondent bracket since there can be commands inside as well as ouside the \todo{...} command.

Edit:

"abc \todo{def \textbf{ghi} jkl}" -> "abc def \textbf{ghi} jkl"
"abc \textbf{def \todo{ghi} jkl}" -> "abc \textbf{def ghi jkl}"
freitass
  • 6,542
  • 5
  • 40
  • 44

3 Answers3

2

Non Recursive

Tell it to match anything but {}s:

:%s/\\todo{\([^{}]\+\)}/\1/

Recursive

The Vim regex language doesn't support the conditional regexes that would be required to match patterns in the way you suggest.

But, you can use an external language like perl to do so:

:perldo $_ =~ s/\\todo ({( (?: [^{}]*+ | (?1) )* )}) /\2/gx

For more information about recursive regex's checkout this SO post. I also found We's Puzzling Blog helpful in sussing these kinds of regexes out.

Community
  • 1
  • 1
dsummersl
  • 6,588
  • 50
  • 65
  • It should be noted that this (or any other regex solution) will be problematic if there are further commands *inside* `\todo` instead of around it. – Martin Ender Nov 29 '12 at 20:50
  • And comments, and multiline blocks, and... :) If there isn't a refactoring tool to make the changes freitass needs to make, I'm hard pressed to think of other solutions beyond hand editing, regexes, and writing a full program. The parser will only tell you when your source doesn't parse - which is helpful, but it certainly won't make the changes for you. – dsummersl Nov 30 '12 at 19:04
0

You do not want your regex to be greedy. See answer How can I make my match non greedy in vim?

In your case this should do the trick:

:%s/\v\\todo\{(.{-})\}/\1/gc
Community
  • 1
  • 1
topek
  • 18,609
  • 3
  • 35
  • 43
0

Can regular expressions be used to match nested patterns? No.

We can support one level of nesting as follows:

We want to pair curly braces inside the \todo{}. So we have to match as many [^{}] and {[^{}]*} as possible.

:%:s/\\todo{\(\([^{}]\|{[^{}]*}\)*\)}/\1/

We add an extra \(\) to store the entire thing in \1.

Community
  • 1
  • 1
Kenneth Cheng
  • 446
  • 4
  • 7