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.

  • C-h f perform-replace says: "Don't use this in your own program" – Stefan Feb 28 '19 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. – Gabriele Nicolardi Feb 28 '19 at 6:51
  • Same difference in the present case. – Stefan Feb 28 '19 at 13:01
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.
  • Could "collapsing undo history" be a good idea? – Gabriele Nicolardi Mar 1 '19 at 5:39
  • @GabrieleNicolardi: I don't see a good reason to want to do that here. – Stefan Mar 1 '19 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? – Gabriele Nicolardi Mar 1 '19 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 '19 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. – Gabriele Nicolardi Mar 1 '19 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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