3

I'm trying to match

\item \macro{this\var}.% YYYY-MM-01

where 01 can be 1 and replace with

\item The Month is MM
\item \macro{this\var}.% YYYY-MM-01

when NOT this case:

\item MonthYY

\item \macro{this\var}.% YYYY-MM-01

I'm tryng with

%s/\v(\\item \c[a-z][a-z0-9]*)\n\+)@!\\item \\macro{this\var}.\% [0-9]*-\([0-9]*\)-(1\n|01\n)/\\item The month is \1\r\\item \\macro{this\\var}/g

but it matches also when the line "\item MonthYY" is present.

Suggestions?

Thanks for attention and best regards

1

1 Answer 1

4

It's very nice that you gave this fairly complex regex a good try! There are some issues with it which are fairly hard to spot, so learning how to debug a regex would come handy.

To debug a regex, it's very useful to enable the 'hlsearch' and 'incsearch' options, which allow you to visually see what the regex actually matched and to highlight matches while you're writing a regex, which allows you to quickly spot and correct mistakes on the spot. So start by enabling those (possibly permanently, in your vimrc):

:set hlsearch incsearch

Then, it's often useful to compose a complex regex in a search (with the / command) rather than in a :s. First use a search to make sure it's matching what you want it to match, then you can use that same regex in a different context (such as the :s you want to use it in.)

Now, regarding your particular regex, one issue that's somewhat pervasive is you're using the \v "very magic" mode (which is a great idea here!) but you seem to be mixing up situations that need a backslash escape under \v (such as \{ and \} to match literal curly braces, and to a minor extent \. to match a literal dot instead of any character) and those that need to not have a backslash (such as \+ should be + to repeat one or more newlines, or \( and \) around the capture group for the month, it should be ( and ) instead, the version escaped with backslashes will match a literal pair of parens instead.)

You can also simplify your regex quite a bit by using the shorthand \d for a digit, or \w for a word character (alphabetic or numeric), which you could use instead of the [0-9] and [a-z0-9] groups (would also allow you to skip \c to force case insensitive matching.)

Finally, the issue with your negative match to prevent matches right after another item is that you're using the @! operator, which matches forward for that expression, while what you really want is @<!, which matches backward, to rule out matches that are preceded by the token in the item.

Correcting the issues mentioned above, together with a few more optimizations (e.g. using 0?1 to match 1 or 01 at the end, using an explicit $ to ensure that's the end of the line, skipping capture groups where they're not needed), I got to this regex which seems to be working as expected:

/\v(\\item \w*\n+)@<!\\item \\macro\{this\\var\}\.\% \d+-(\d+)-0?1$

You can then use this regex into a :s command. There are two issues that should be fixed in your attempt. The first is that the month is actually in capture group \2, since capture group \1 is the negative look-behind match. (You could also use %( and ) for expressions that need to be inside parens but shouldn't be capture groups, but using \2 instead of \1 is a simple enough fix in this case.)

Second one is that, to repeat the matched line, you can simply use & in the replacement side, that will preserve the matched contents exactly as they appear. (In the command you were working on, you seemed to stop at \macro{this\var}, so it's not totally clear whether you wanted to drop the last part... Your example does include it verbatim, in which case & would have been appropriate.)

Putting it all together:

:%s/\v(\\item \w*\n+)@<!\\item \\macro\{this\\var\}\.\% \d+-(\d+)-0?1$/\\item The month is \2\r&/

Hopefully the fixed regex and :s command will work well for you, and also the hints in this answer will in the future help you compose and debug complex regexes such as the one needed for this particular match.

5
  • 1
    Thanks. Your answer is very usefull. Dec 26, 2021 at 21:47
  • Using /\v(\\item \w*\n+)@<! I match everything but line below \item MonthYY while I have to NOT match also this line Dec 26, 2021 at 22:09
  • Not sure if I follow your last comment... Which line are you matching that you should not match? Perhaps instead of \w* you need something more like .*, is that perhaps what the problem is?
    – filbranden
    Dec 27, 2021 at 0:28
  • Sorry, it a bit involved. I mean in the not part of regex I should match everything except the line \item MonthYY while using /\v(\\item \w*\n+)@<! it matches everything exept line after that \item MonthYY; on the other hand with /\v(\\item \w*\n+)@! it NOT matches only first character,the backslash, of the line that should NOT match (\item MonthYY) Dec 27, 2021 at 4:41
  • It seems this works as searching pattern: \v(\\item \w+\s?\n)@<!(\_^\n)?\n\_^\\item \\macro\{my\\var\}\.\% \w+ (\d+)-(\d+)-0?1$, sometime there is no empty line before that one I want to match. Jan 16, 2022 at 4:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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