I'm writing a document where I had to change all instances of the variable i to x and the document is already considerably long. Is there a editor that will allow me to do this, or in general, replace text inside math environments?
- 755
8 Answers
emacs can do this sort of thing fairly easily
(defun change-mathvar (a b)
(interactive "sfrom: \nsto: ")
(beginning-of-buffer)
(while (re-search-forward
"\\(\\\\(\\|\\\\\\[\\|[^\\\\]\$\$?\\|\\\\begin{equation}\\|\\\\begin{align}\\)" nil 1)
(query-replace-regexp a b t (point)
(progn (re-search-forward
"\\(\\\\)\\|\\\\\\]\\|[^\\\\]\$\$?\\|\\\\end{equation}\\|\\\\end{align}\\)" nil 1) (point)))))
this looks for $ \( \[ \begin{equation} \begin{align} as math-start. Other environments can be added.
Starting from a document such as
\documentclass{article}
\begin{document}
i i aib
\[i i aib \]
i i aib
\begin{equation}
i i aib
\end{equation}
\end{document}
then executing M-x change-mathvar the editor will prompt for the old and new names then do a query-replace of the variable names to produce:
\documentclass{article}
\begin{document}
i i aib
\[x x aib \]
i i aib
\begin{equation}
x x aib
\end{equation}
\end{document}
Note it hasn't changed anything out of math and it only changes i where it appears as a complete word, not aib. If you want aib to change as well change the t in the code to nil to make a non-delimited match.
- 27,421
- 757,742
My solution using emacs (>=24) and auctex:
(defun latex-replace-in-math ()
"Call `query-replace-regexp' with `isearch-filter-predicate' set to only match inside LaTeX math environments."
(interactive)
(let ((isearch-filter-predicate
(lambda (BEG END)
(save-excursion (save-match-data (goto-char BEG) (texmathp)))))
(case-fold-search nil))
(call-interactively 'query-replace-regexp)))
This uses texmathp to detect math environments.
Undefining case-fold-search has the effect of making the search case sensitive; this usually makes sense for variable symbols.
Usual regular expressions can be used, e.g. searching for \<i\> instead of just i avoids changing \sin to \sxn.
- 861
-
Thanks alot! One caveat: It also finds the letters in "equation", if you use \begin{equation} and \end{equation}. Any idea how to exclude those? – Eike Feb 24 '23 at 13:04
-
WinEdt is enough:
In the "Replace" dialog box, choose the check box "Whole words only" and "Regular expressions".
Search for
i\E{isMath}and Replace withx.
Then, it works. However, for expression like _i, it will also change to _x which maybe not that you want.
You can also replace text not in math environment by using i\e{isMath}.
I find this trick by searching "Regular Expressions" in the diaglog "WinEdt help". Maybe you find more useful tricks in the help.
I once wrote a perl script to do just that. It's called MathGrep and can be obtained from https://github.com/loopspace/mathgrep. The biggest caveat is that it doesn't recognise dollars (see Are \( and \) preferable to dollar signs for math mode?), but then I also wrote a script to convert all dollars to \(...\) and \[...\] as well which is at https://github.com/loopspace/debuck.
I'm struggling to think of a use-case where this would beat David's Emacs script though ...
- 153,724
- 43
- 389
- 751
What about to use TeX itself for solving this task? Write the following line at beginning of your document:
\mathcode`i=\mathcode`x
- 74,238
Vim
In vim with the vimtex plugin,
:%s/beta/\=vimtex#syntax#in_mathzone() ? 'alpha' : submatch(0)/g
will replace beta by alpha only in math zones. The math zones are defined by the vimtex plugin and recognize \begin{equation}, \begin{align}}, \[, \( and $, $$ as you would expect in the following example latex document
\documentclass{amsart}
\begin{document}
This beta won't be replaced. However this one, $\beta + \gamma$ will, so will (\beta + \gamma) and
$$
\beta + \gamma,
$$
[
\beta + \gamma,
]
\begin{equation}
\beta + \gamma,
\end{equation}
and
\begin{align}
\beta + \gamma.
\end{align}
\end{document}
A bit of explanation. Vim substitute command lets you use an expression instead of a string as the replacement with \=. This is documented for example at https://vim.fandom.com/wiki/Using_an_expression_in_substitute_command. The expression is evaluated at each match (in our example, at each beta in the text file).
Then the vimtex plugin provides a function vimtex#syntax#in_mathzone() that checks if the current cursor is in math mode. If that's the case, we return alpha, and otherwise submatch(0) will replace the match by itself and no change will be made.
A caveat is if you would like to use the c flag with many matches. For instance
:%s/M/\=vimtex#syntax#in_mathzone() ? '\mathbf{M}' : submatch(0)/gc
will ask you to confirm the replacement not only in math mode, but also at every occurrences of M in the document including outside math mode, even though confirming the replacement outside math mode will not perform anything.
- 183
TeXstudio has a build-in search that allows to search only in certain areas, such as math environment, commands, etc. There are a couple of buttons to the right of the search field, allowing you to customize the search.
If you need more complicated searches, you can also use regular expressions.
Even though the questions has been answered, I add this answer for future reference, for people that like to use TeXstudio.
- 111
I have reviewed the responses suggesting Emacs as the recommended editor for this type of operation. I would like to propose a solution that, in my opinion, is a bit more comprehensive and flexible.
I have written a function to temporarily set the value of isearch-filter-predicate (it works with query-replace* functions too):
;; From AUCTeX, but it works as a standalone too:
(require 'texmathp)
(defvar-local ifp-predicates-list '(skip-maths keep-maths)
"List of isearch filter predicates")
(defun skip-maths (beg end)
"Return nil if some text BEG to END is in a LaTeX maths
environment or in in-line maths."
(catch 'skip-region
(let ((pos beg))
(save-excursion
(save-match-data
(goto-char pos)
(while (< pos end)
(when (texmathp) (throw 'skip-region nil))
(goto-char (setq pos (1+ pos)))))
t))))
(defun keep-maths (beg end)
"Return t if some text BEG to END is NOT in a LaTeX maths
environment or in in-line maths."
(catch 'skip-region
(let ((pos beg))
(save-excursion
(save-match-data
(goto-char pos)
(while (< pos end)
(unless (texmathp) (throw 'skip-region nil))
(goto-char (setq pos (1+ pos)))))
t))))
(defun with-temp-isearch-filter-predicate-test ()
"Temporarily assigns a predicate to the variable `isearch-filter-predicate`
in order to make portions of the buffer visible/invisible to search and replacement functions
based on specific criteria."
(interactive)
(save-excursion
(let* ((IFP_DEFAULT isearch-filter-predicate)
;; https://emacs.stackexchange.com/q/80307/15606 :
(set-message-function nil)
(IFP (completing-read
"Specify which predicate you want to activate [TAB]: "
ifp-predicates-list
nil
t
nil
nil
nil))
(isearch-filter-predicate (intern-soft IFP))
;; Non-nil means to allow minibuffer commands while in the minibuffer:
(enable-recursive-minibuffers t))
(read-string
(format
"The current value assigned to the variable `isearch-filter-predicate' is `%s'.
Press ENTER to restore the default value (`%s') at the end of the operations: "
IFP
IFP_DEFAULT)))))
I'm working on a function to combine "multiple" predicates at the same time.
- 1,815

\( \sum_\text{\( p \) such that \( 2p+1 \) is prime} i^p \)when wishing to changeptoq. – Andrew Swann Jul 31 '12 at 15:21p; so you don't get the chance to query replace the other mathp's even on a second run. – Andrew Swann Jul 31 '12 at 15:45\)– David Carlisle Jul 31 '12 at 15:55(progn ...)construction should count opening and closing delimiters and stop when it finds one extra close. How hard would it be? – Bruno Le Floch Aug 12 '13 at 18:12