2

My goal is to write a simple function that can do the following:

Staring with an expression that looks like

$$ x = y $$
$$ y = z $$
$$ z = x $$

(There can more lines of such equations)

I want to write a elisp function that when I run it after selecting this region, I get the following

\begin{align}
x = y \\
y = z \\
z = x
\end{align}

I need to do a conditional replace on $$ depending on whether it is near the beginning of the line or the end of the line. Moreover, I also need to do not replace the last $$ with \\ but with \end{align}. I am new to elisp and I could not figure out how to do a conditional replace and also how to restrict the replace to the region I selected.

Any help is greatly appreciated.

1
  • I formatted your source code examples. Please, review the first example. Should there be more newlines in it?
    – Tobias
    Jan 2, 2018 at 8:35

2 Answers 2

2

If we can make some simple assumption the task is relatively simple and can even be done interactively with two consecutive normal query-replace-regexp commands.

Assumptions:

  1. There are no empty equations. I.e., all strings between equation delimiters $$ that consist of space syntax characters and newlines only belong to separators between equations.
  2. If one removes all equation delimiters $$ from the first assumption the remaining delimiters only belong to those ones that need to be replaced by \begin{align*} and \end{align*}. For an example there are no strange comments where a single equation delimiter $$ in them.

In the first run use assumption 1 and replace equation delimiters $$ that just separate equations:

M-C-% $$[[:space:]^J]+$\$ RET \\\\^J RET

The emacs manual describes regular expressions in full extent. The most important details for our use case are listed in the following:

  • The dollar sign $ stands for itself except at the end of the regular expression where it matches the empty string at end of line. That is the reason why the last dollar sign is escaped with a backslash.
  • The outer brackets [...] stand for a single character from a character set.
  • The inner brackets within the character set [:space:] stand for characters with the space syntax class.
  • You should not copy paste the character ^J. This character stands for the newline which you get if you type C-q C-j. The quoted-insert command bound to C-q causes that the following character is not looked-up in the active keymaps but inserted literally and C-j stands for the newline character #x0a. Note: C-a == 1, C-b == 2, ..., C-i == 9, C-j == 10 == #x0a.
  • The + sign behind the character set indicates that one or more characters from the character can match (we eat one or more spaces or newlines).

In the second run replace the even equation delimiters $$ with \begin{align*} and the odd ones with \end{align*}. You can use the lisp evaluation form \,(...) to differentiate between odd and even in the replacement string.

M-C-% $\$ RET \\\,(if (evenp \#) "begin" "end"){align*} RET

7
  • Thank you for the comment. Sadly this didn't work either. When I tried doing the replacement you suggested after selecting the said region, I got a message saying "Replaced 0 occurrences". I will comment later if I can figure out what the issue is. Can you please explain what "$$[[:space:]^J]+$\$" searches and replaces for? Jan 7, 2018 at 2:40
  • @QuarkyQuanta I will answer your question in the comment later when I have more time.
    – Tobias
    Jan 7, 2018 at 10:51
  • @QuarkyQuanta I've tested the proposed replacements with the full document and with a selected region. It works for both cases. The most critical part is that you should NOT copy paste the regular expressions! You must replace ^J in the given string by C-q C-j to insert a newline.
    – Tobias
    Jan 7, 2018 at 12:47
  • Thanks Tobias. It does work! @phils method is more convenient to me but thanks for explaining your method as it will be helpful in other things for sure. I was making a rookie mistake by not pressing "y" to accept the changes. But well I am a rookie as of now, so live and learn I guess. Jan 8, 2018 at 0:58
  • 1
    @QuarkyQuanta My version is better for replacing the deprecated math delimiters $$ throughout the full document. As I already mentioned it works for the full document if the simple assumptions listed in the answer are met.
    – Tobias
    Jan 8, 2018 at 8:56
4

This might suffice:

(defun my-command (beginning end)
  "Convert \"$$ ... $$\" lines in the region to an {align} block."
  (interactive "*r")
  (setq end (copy-marker end))
  (save-match-data
    (save-excursion
      (goto-char beginning)
      (insert "\\begin{align}\n")
      (while (re-search-forward "^\\(\\$\\$ \\).*\\( \\$\\$ *\\)$" end t)
        (replace-match " \\\\\\\\" nil nil nil 2) ;; replace the suffix
        (delete-region (match-beginning 1) (match-end 1))) ;; delete the prefix
      (delete-region (- (point) 3) (point)) ;; delete the final " \\"
      (insert "\n\\end{align}")
      (set-marker end nil))))
6
  • Thanks for your comment. This works but has some issues. Firstly, if I start with $$ x = y $$ $$ y = z $$ $$ z = x $$ $$ z = x^2 $$ If I select the first three equations and apply this command the result is \begin{align} x = y \\ y = z \\ z = x \\ z = x^2 \end{align} and not \begin{align} x = y \\ y = z \\ z = x \end{align} $$ z = x^2 $$ Secondly, if i try $$ e^x = \frac{x}{y} $$ $$ e^x = \frac{x}{y} x^2 $$ I get a very weird result: \begin{alig \end{align}$$ e^x = \frac{x}{y} $$ $$ e^x = \frac{x}{y} x^2 $$ Jan 7, 2018 at 2:35
  • @QuarkyQuanta, try the updated version.
    – phils
    Jan 7, 2018 at 2:59
  • so the first issue is solved i.e. it only acts on the selected region. But there are still two issues that persist. Consider the following two examples 1. $$ e^x = \frac{x}{y} $$ (linebreak) $$ e^x = \frac{x}{y} $$ 2. $$ \frac{x}{y} = x $$ (linebreak) $$ \frac{x}{y} = x $$ (linebreak) $$ \frac{x}{y} = x $$ Then I get: 1. \begin{align} (linebreak) $$ e^x = \frac{x}{y} $$ (linebreak) e^x = \frac{x}{y} (linebreak) \end{align} 2. \begin{alig (linebreak) \end{align}$$ \frac{x}{y} = x $$ (linebreak) $$ \frac{x}{y} = x $$ (linebreak) $$ \frac{x}{y} = x $$ Jan 7, 2018 at 7:00
  • I think the issue is that in the code I am attempting I have space after the $$ symbol at the end of the line but in the code submitted here there is no space. When I remove the space after the $$ symbol it works amazingly. Thanks a lot. Although, it would be ideal if the solution was agnostic to whether there was a space after the $$ at the end of the line. Jan 7, 2018 at 7:04
  • Easily done. Code updated.
    – phils
    Jan 7, 2018 at 9:32

Your Answer

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

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