1

I'm trying to write a script (in emacs lisp) to convert TeX display math style like, e.g.,

$$
x + y = z
$$

into the LaTeX

\begin{equation*}
x + y = z
\end{equation*}

To get my purpose I imagined this algorithm:

  1. While scanning the file for the "$$" strings, I would replace temporarily, once at time, the "$$" string with "$$\TESTIFDISPLAYMATH" where \TESTIFDISPLAYMATH is something like:

    \newcommand{\TESTIFDISPLAYMATH}{\mathchoice{\wlog{DISPLAY MATH OK}}{}{}{}}
    

    (this is necessary to avoid false positives like "$x,$$y,$")

  2. At this point I'd compile my .tex file to generate the .log file and scanning it for my target string.

  3. After removing the "\TESTIFDISPLAYMATH" string, if "DISPLAY MATH OK" I would perform my replacement.

My questions are:

  1. Can I approach my task in a faster way? I mean, e.g., without the need to fully compile my .tex file for each instance of "$$".

  2. Is there a tool that can do it already?

I'm open to any suggestion.

Gabriele
  • 1,815
  • If you have your $$ in a form like above what prevents you from simply using search & replace to transform $$\n into \[\n (or something different)? – TeXnician Jan 15 '19 at 17:48
  • @TeXnician, this is the way I've done it until now. I'm trying to automate this task. – Gabriele Jan 15 '19 at 18:18
  • 1
    My impression is that this is more a Emacs rather than TeX question. – egreg Jan 15 '19 at 19:03
  • @egreg I don't thing so, because I'm searching, e.g., for a LaTeX way to speed up the compilation of the .tex file. In emacs I would have used the syntactic highlighting but it not enough strong. – Gabriele Jan 15 '19 at 19:24
  • 2
    I'm not sure if this is helpful, but you could perform the regex replacement \$\$\([^$]+\)\$\$\\begin{equation}\1\\end{equation}. This does not match the false positive you mention because it only searches for $$…$$ with no $ between them. (It does fail for $x,$$y,$$z,$, but it seems unlikely something like that would appear in your document.) – Circumscribe Jan 15 '19 at 20:14
  • @Circumscribe, It is useful. In a "paranoic mode" I'll need to temporarily mask improbable escaped $ in the math contents but it should work. – Gabriele Jan 15 '19 at 20:25
  • @GabrieleNicolardi: I don't expect your proposed algorithm would work by the way because every branch of a \mathchoice is executed (but only one is printed). This means that you'll see "DISPLAY MATH OK" in your log irrespective of whether the \TESTIFDISPLAYMATH occurs in a display environment or not. – Circumscribe Jan 15 '19 at 21:12
  • If you add \everydisplay\expandafter{\the\everydisplay\GenericWarning{\relax}{DISPLAY MATH}} to your preamble TeX will print DISPLAY MATH on input line <line number>. for every display equation though. – Circumscribe Jan 15 '19 at 21:26
  • @Circumscribe, you're right about the mathchoice behaviour. I didn't expect it. I wonder if this issue can be approched by the LaTeX side. – Gabriele Jan 15 '19 at 21:34
  • Alternatively: \newcommand{\TESTIFDISPLAYMATH}{} together with \everydisplay\expandafter{\the\everydisplay\renewcommand{\TESTIFDISPLAYMATH}{\wlog{DISPLAY MATH OK}}} would work. This way \TESTIFDISPLAYMATH does nothing, but it is redefined to print your message when it is used inside a display environment. (It will also print this message if used inside a text{…} in a display environment though.) – Circumscribe Jan 15 '19 at 21:38
  • @Circumscribe, if you like, please articulate your hints in an answer to my question. – Gabriele Jan 15 '19 at 21:42
  • I'm not sure if any of these really answer your question. I like the regex solution best (and I expect it'll nearly always) work, but if that's the answer the question is kind of off-topic. – Circumscribe Jan 15 '19 at 21:45
  • 1
    I know you want to do this in Emacs lisp, but many years ago I wrote a perl script to do this and you might find it helpful at least in terms of figuring out the regex. You can find it at https://github.com/loopspace/debuck – Andrew Stacey Jan 15 '19 at 22:40
  • @LoopSpace this could also be helpful. Thanks! – Gabriele Jan 15 '19 at 23:12

2 Answers2

2

I don't really know what the best way to do this is, but here are a couple of suggestions.

  1. Since you're using Emacs, you could try the following regex replacement:

    \$\$\([^$]+\)\$\$ → \\begin{equation}\1\\end{equation}
    

    This matches anything of the form $$…$$, where can be any sequence of characters (including newlines) that does not contain $. It does not fail for the false positive which you mention ($x,$$y,$), but it would fail for something like $x,$$y,$$z,$.

    Just to be safe you may want to first replace \\ by something unique and then replace \$ by something unique (both non-regex) and undo these replacements afterwards. The first replacement is to prevent the replacement of \$ in \\$ and the second is to prevent replacement of \$$ when \$ is a literal $ sign and the second starts or ends an equation.

  2. If you would like to know the line numbers for all display math environments you could add

    \everydisplay\expandafter{\the\everydisplay\GenericWarning{\relax}{DISPLAY MATH}}
    

    to your preamble. This prints DISPLAY MATH on input line <line> for every display equation.

    (The contents of \everydisplay is inserted at the start of every display environment. You can add something to it with \everydisplay\expandafter{\the\everydisplay<addition>}. )

  3. Your \TESTIFDISPLAYMATH algorithm doesn't actually work because each of the branches of \mathchoice is always executed (though only one is printed). Consequently, DISPLAY MATH OK will be printed irrespective of whether this macro is called from within a display environment.

    If you want to go this route you may want to instead try something like

    \let\TESTIFDISPLAYMATH\empty %% <- does nothing
    \makeatletter %% <- make @ usable in command names
    \everydisplay\expandafter{\the\everydisplay
      \@ifnextchar\TESTIFDISPLAYMATH{\wlog{DISPLAY MATH OK}}{}}
    \makeatother  %% <- revert @
    

    You should add this at the end of your preamble (and in particular after loading amsmath or maybe even right after \begin{document} (to be safe). It prints DISPLAY MATH OK whenever the very first thing that appears in an equation is \TESTIFDISPLAYMATH. It may be incompatible with some packages (if they change \everydisplay during runtime, though I'm not aware of any).

    Alternatively you could try

    \let\TESTIFDISPLAYMATH\empty %% <- does nothing
    \everydisplay\expandafter{\the\everydisplay
      \renewcommand\TESTIFDISPLAYMATH{\wlog{DISPLAY MATH OK}}}
    

    This way \TESTIFDISPLAYMATH normally does nothing, but it is redefined to write DISPLAY MATH OK to the log file whenever it is used inside a display math environment. It will also trigger when used inside a \text{…} inside such an environment though, so it's still not perfect.

Circumscribe
  • 10,856
2

Not heavily tested, but a lisp function can look like this. It replaces all occurrences of $$ which are in a line of their own with an equation* environment. If invoked with C-u M-x, it also looks for inline $$ and replaces them.

(defun GN/fix-math ()
  "Search and replace TeX's $$ with LaTeX's \"equation*\" environment.
Replaces only $$ in single lines.  If invoked with `C-u', then
also looks after $$ which are inline.  User is asked for
confirmation then."
  (interactive)
  (save-excursion
    (save-restriction
      (widen)          
      (goto-char (point-min))
      (let ((kill-whole-line nil))
        (while (and (re-search-forward (concat "^[ \t]*" (regexp-quote "$$") "[ \t]*$") nil t)
                    (texmathp))
          (beginning-of-line)
          (kill-line)
          (insert "\\begin{equation*}")
          (re-search-forward (concat "^[ \t]*" (regexp-quote "$$") "[ \t]*$") nil t)
          (beginning-of-line)
          (kill-line)
          (LaTeX-close-environment)
          (forward-line -1)
          (LaTeX-fill-environment nil)))))
  (when current-prefix-arg
    (save-excursion
      (save-restriction
        (widen)
        (goto-char (point-min))
        (while (re-search-forward (regexp-quote "$$") nil t)
          (when (yes-or-no-p "Replace $$? ")
            (delete-char -2)
            (newline)
            (save-excursion
              (end-of-line -1)
              (delete-horizontal-space))
            (insert "\\begin{equation*}")
            (newline)
            (indent-according-to-mode)
            (re-search-forward (regexp-quote "$$") nil t)
            (delete-char -2)
            (LaTeX-close-environment)
            (forward-line -1)
            (LaTeX-fill-environment nil)))))))
Arash Esbati
  • 7,416