0

In my LaTeX code I want to replace one-line eqnarray environments with the equation environment. To do that I need to "count" the new-line (\\) strings in a region and I also need to ignore some new-line strings that may occur in some math sub-enviroments like array, matrix etc.

If I have, e.g.:

\begin{eqnarray}
x+y&=& 1 
\begin{array}{ccc}
a & b & c\\
d & e & f\\
\end{array}
\end{eqnarray}

I need to ignore the \\ strings inside the array environment.

To do that I can, temporarily replace these strings with arbitrary strings but I have the downside of the reappearing of them (the arbitrary strings) when i undo some changes.

So I tried to use the with-silent-modification macro:

(defun latex-one-line-eqnarray-to-equation ()
  (interactive)
    (save-excursion
      (let* ((a (copy-marker (point-min)))
             (z (copy-marker (point-max))))

    (goto-char a)
    (unwind-protect
        (while (search-forward-regexp "\\\\begin{eqnarray}" nil t)
          (save-excursion
            (let ((b (copy-marker (match-beginning 0)))
                  (e (copy-marker (search-forward-regexp 
                                   "\\\\end{eqnarray}" nil t))))

              ;; *LaTeX sub environment line break occultation:*
              (save-restriction
                (narrow-to-region b e)
                (goto-char b)
                (while (search-forward-regexp "\\\\begin{array}" nil t)
                  (save-excursion
                    (let ((i (copy-marker (match-beginning 0)))
                          (f (copy-marker (search-forward-regexp 
                                           "\\\\end{array}" nil t))))

                      (with-silent-modifications
                        (perform-replace "\\\\" "❤❤" nil nil nil 1 nil i f)))))

                (when (= 0 (how-many "\\\\\\\\" b e))
                  (perform-replace "{eqnarray}" 
                                   "{equation}" t nil nil 1 nil b e))))))

      ;; *UNWINDFORMS*
      (with-silent-modifications
        (perform-replace "❤❤" "\\\\" nil nil nil 1 nil a z))))))

In this case the trick seems to work because I (temporarily) replace my target string with one of the same length (\\ with ❤❤).

Is it a coincidence? Is it safe to operate this way?

Note. I'd prefer to use some "tex property" to hide my target string to the how-many function but I've not been able to find the way to do it.

Note 2. I red the with-silent-modifications docstring:

If BODY performs real modifications to the buffer’s text, other than cosmetic ones, undo data may become corrupted.

3
  • C-h f perform-replace says: "Don't use this in your own program"
    – Stefan
    Feb 28, 2019 at 1:30
  • @Stefan This is not true (complete). The docstring says: Don’t use this in your own program unless you want to query and set the mark just as ‘query-replace’ does. Feb 28, 2019 at 6:51
  • Same difference in the present case.
    – Stefan
    Feb 28, 2019 at 13:01

1 Answer 1

2

Replacing a string with one of the same length indeed falls within the "cosmetic" description, tho only if that change doesn't additionally affect the output of syntax-ppss.

I believe in your case, it should indeed be safe to operate this way.

But the reasons why it's safe are pretty subtle, so it'd be better to find another way to do it. The only truly safe changes to do in with-silent-modifications are text-property changes. So maybe the better option is the following:

  • in the loop through array env, just add a within-array-env text-property over the whole env (without bothering to search for \\).
  • Instead of calling how-many, loop through all the matches of the regexp and check whether they are tagged with the within-array-env property: if all of them are, then you can convert to equation.
  • At the end, remove that within-array-env.
7
  • Could "collapsing undo history" be a good idea? Mar 1, 2019 at 5:39
  • @GabrieleNicolardi: I don't see a good reason to want to do that here.
    – Stefan
    Mar 1, 2019 at 13:42
  • I mean, if I remove the two with-silent-modifications macro instances, could "collapsing undo history" be a good method to let emacs consider all the changes performed by each single the (first) while iteration as a single undo block? Mar 1, 2019 at 15:37
  • It would do that, but it wouldn't solve other cases. E.g. when there's no eqnarray to be converted, the buffer would still be marked as "modified" because of the other transient modifications.
    – Stefan
    Mar 1, 2019 at 17:04
  • Ok, in this case I don't care if the buffer would be marked as modified... I was looking for a method to make undo avoid to put back in the buffer my temporary "cosmetic" substitutions. Mar 1, 2019 at 17:41

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.