Question

I need to change the name of a math variable across a big Latex document, with the help of vim regular expressions, which I am struggling to learn.

For example, I want to change a variable labelled as 't' into 's', so for example

\begin{equation}
    f(X) \leq \sqrt{t(t+1)}
\end{equation}

should turn into

\begin{equation}
    f(X) \leq \sqrt{s(s+1)}
\end{equation}

For this I need to search for the character 't' somewhere between a \begin{equation} and its matching \end{equation} possibly several line breaks away, but the character 't' should not be part of any keyword such as \sqrt

I have tried

%s/\\begin{equation}\_.\{-}\(\\\a*\)\@!\zst\ze\_.\{-}\\end{equation}/s/g

but it doesn't work as it should. Besides, this pattern even matches keywords like \sqrt and I don't understand why. However, I am aware that sandwiching the match between two \_.\{-} as I am doing, won't yield the desired result, since it won't match multiple occurrences of the character 't'.

I would prefer a pure vim regexp solution, if possible.

Note:

Since I use different environments besides equation, such as align, eqnarray, multline, etc., it would be even better to insert an OR into the pattern such as

/\\begin{\(equation\|align\|eqnarray\|multline\)}

and then find its matching \end{…}. How do you accomplish that?

Was it helpful?

Solution

Let's build it up by pieces. First, as @rbernabe suggests, use the start-of-word and end-of-word patterns to find "t" as an entire word:

/\<t\>

Next, to replace all such on the current line, use

:s/\<t\>/s/g

Now, position the cursor on a \begin{equation} line. The range .,/\\end{equation}/ specifies all lines up to and including the end of the equation environment, so

:.,/\\end{equation}/s/\<t\>/s/g

will replace all the single-word "t"s with "s" in that range. In order to repeat this command for all equation environments in the file, use :g/\\begin{equation}/{command}:

:g/\\begin{equation}/.,/\\end{equation}/s/\<t\>/s/g

If you want to play vimgolf, then the . is optional. Finally, if you want to handle multiple environments, I am afraid there is no way to tie the variable environment name in the \begin{...} to the closing \end{...}. If none of your equation-like environments is nested, then you can use

:g/\\begin{\(equation\|align\|eqnarray\|multline\)}/.,/\\end{\(equation\|align\|eqnarray\|multline\)}/s/\<t\>/s/g

You can also use an explicit loop, which might help if there are nested environments:

:let envs = ['equation', 'align', 'eqnarray', 'multline']
:for env in envs
:  execute 'g/\\begin{' . env . '}/.,/\\end{' . env . '}/s/\<t\>/s/g'
:endfor

Of course, regular expressions are not a substitute for real parsing. As long as your actual LaTeX code is similar to your sample, this should work, but you will run into trouble if you ever have something like

\end{equation} This is ordinary text with a t in it.

OTHER TIPS

You need to match only t as a whole word. To accomplish this the first thing you should change to your regexp is to add the word start and en boundaries \< and \> So your example would look like:

%s/\\begin{equation}\_.\{-}\(\\\a*\)\@!\<t\>\ze\_.\{-}\\end{equation}/s/g

Second thing is to use very magic, most vim user found it more convenient:

:%s/\v\\begin\{equation\}\_.{-}(\\\a*)@!<t>\ze\_.{-}\\end\{equation\}/s/g

You are matching words like sqrt because \\\a* match a empty string. By using word delimiters < and > (using \v) or \< and \> (not using \v) you limit matchings of t only to a single t not surrounded by word characters like it was a word on it's own.

Then you have two alternatives the first is to use confirmation char by adding c to the flags:

:%s/\v\\begin\{equation\}\_.{-}(\\\a*)@!<t>\ze\_.{-}\\end\{equation\}/s/gc

Then you can selectively use the substitution or escape it And the second alternative is to limit matches using a range (and using the confirmation if you like):

:/\v\\begin\{equation\}/,/\v\\end\{equation\}/s/\v<t>/s/gc

Regards

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top