It looks like your nasty regex was not nasty enough... :-)
Search Section
The search would have to be changed to this:
^Hide\(\(\(Show\|Hide\)\@!\_.\)*\)SetFontSize 28
This includes quite a few uncommon things and so many parenthesis... Let's see what we have in there:
The Caret (^
)
The caret is used to mean beginning of the line. I think we're already familiar with this one.
An important point, the ^
does not work except as the very first character in your pattern. After the it is taken verbatim. To include a beginning of line within your expression you need to use \_^
. However, in our situation we did not need that.
(There is a similar phenomena with $
and \_$
)
The first and last parenthesis (\( ... \)
)
The first and last parenthesis are used on their own which means it will grab whatever appears inside and set it in parameter \1
. You already used that in your own regex, so I'll assume you also are familiar with this one.
The second set of parenthesis
As you may notice, there is a second set of parenthesis followed by an asterisk \( ... \)*
. This means we are looking for whatever matches any number of times. This is the usual way of using the asterisk so you should be familiar with it.
The third set of parenthesis, OR, and \_.
Yes, there are actually three parenthesis before the word Show
. This last set is necessary for two reasons: the \|
and the following @!
.
In regard to the OR operation, you should already be familiar with it.
Show\|Hide or Hide\|Show
The order does not matter here. The \
is necessary in front of the |
to work in vim.
The parenthesis around this expression allows us to follow the expression by something. Here the @!
.
\(Show\|Hide\)@!
This one is much less familiar. It means if not matched. The use of this is not very easy, though, but you need to follow that expression with what you want to extract which should not match said expression. This is why we have \_.
behind that pattern.
The \_.
means match anything whatsoever. Contrary to the .
on its own, which doesn't match the \n
character. In other words, we match any character on any number of lines unless it matches Show
or Hide
.
Note that the parenthesis around that expression are also important as is the asterisk, so this whole thing is really what makes it work:
\(\(Show\|Hide\)@!\_.\)*
a.k.a. match whatever up to the next Show
or Hide
characters (note that it would also match Showing
, Shower
, HideMe
, etc. you should be able to use \<
and \>
if it is necessary to match the word exactly.)
Side Note: to search on multiple lines, it is also possible to use the \n
character in the pattern. However, it's not as versatile than the \_.
pattern.
SetFontSize 28
Now the section has to include SetFontSize 28
as well. Just like you had in your regex. If no SetFontSize 28
appears in that section, try the search again on the next section.
Because of the negation above (the match except Show
or Hide
) the search doesn't leak to the next section, taking the risk of messing it up.
Replacement Section
The replacement is just the same as you had it:
.../Hide\1SetFontSize 18/
We use the parenthesis in the Search so the \1
works as expected.
Complete Search and Replace
The resulting patterns goes like this:
:%s/^Hide\(\(\(Show\|Hide\)@!\_.\)*\)SetFontSize 28/Hide\1SetFontSize 18/
The \(Show\|Hide\)
should include all the possible headers.
Sources
Regex to match any character including newline (\_.\{-}
)
Search for lines not containing pattern and other helpful searches (@!
)
Vim documentation: pattern (\_^
)
Hide
block have aSetFontSize
line (whatever be the value)? If so, you could use:%s/Hide\_.\{-\}SetFontSize \zs28/18/
whatever be the value
would cause trouble, your solution only works if everyHide
block have aSetFontSize
line and it's value is exactly28
, otherwise, it matches until28
of another block.