Ciao Gabriele,
Gabriele Nicolardi via bug-auctex via Bug reporting list for AUCTeX
<[email protected]> writes:
> A regex without lookbehind cannot reliably encode "preceded by an even
> number of backslashes" through alternation alone.
Yes, we have to try harder if we want to fix issue.
> The robust fix is to capture the whole backslash run with a single,
> non-alternating quantifier, and check its parity in Lisp:
>
> In texmathp-compile:
>
> texmathp-toggle-regexp
> (concat "\\(\\\\*\\)"
> (regexp-opt togglers t))
>
> In texmathp, the toggle-scanning loop needs a matching change (group 1
> no longer means "real toggle", it now holds the backslash run, so
> parity must be checked before deciding to toggle):
>
> (while (re-search-forward texmathp-toggle-regexp pos t)
> (when (zerop (mod (length (match-string-no-properties 1)) 2))
> (if (setq math-on (not math-on))
> (setq sw-match (cons (match-string-no-properties 2)
> (match-beginning 2)))
> (setq sw-match nil))))
I only had a brief look at this, but within AUCTeX, we have the function
`TeX-escaped-p' which we can use for this. My proposal is this (look
for the ;; Next line changed: and ;; (unless ...) added:):
--8<---------------cut here---------------start------------->8---
(defun texmathp-compile ()
"Compile the value of `texmathp-tex-commands' into the internal lists.
Call this when you have changed the value of that variable without using
customize (customize calls it when setting the variable)."
(interactive)
;; Extract lists and regexp.
(setq texmathp-macros nil texmathp-environments nil)
(setq texmathp-memory
(cons texmathp-tex-commands texmathp-tex-commands-default))
(setq texmathp-tex-commands1 (append texmathp-tex-commands
texmathp-tex-commands-default))
(let ((list (reverse texmathp-tex-commands1))
entry type switches togglers)
(while (setq entry (car list))
(setq type (nth 1 entry)
list (cdr list))
(cond ((memq type '(env-on env-off)) (push (car entry)
texmathp-environments))
((memq type '(arg-on arg-off)) (push (car entry) texmathp-macros))
((memq type '(sw-on sw-off)) (push (car entry) switches))
((memq type '(sw-toggle)) (push (car entry) togglers))))
(setq texmathp-onoff-regexp
(concat "\\(?:[^\\]\\|\\`\\)"
(regexp-opt switches t))
texmathp-toggle-regexp
;; Next line changed:
;; (concat "\\([^\\$]\\|\\`\\)"
(concat "\\(\\`\\|\\)"
(regexp-opt togglers t)))))
(defun texmathp ()
"Determine if point is inside (La)TeX math mode.
Returns t or nil. Additional info is placed into `texmathp-why'.
The functions assumes that you have (almost) syntactically correct (La)TeX in
the buffer.
See the variable `texmathp-tex-commands' about which commands are checked."
(interactive)
(let* ((pos (point)) math-on sw-match
(bound (save-excursion
(if (re-search-backward
(if (memq major-mode '(doctex-mode docTeX-mode))
"[\n\r]%*[ \t]*[\n\r]"
"[\n\r][ \t]*[\n\r]")
nil 1 texmathp-search-n-paragraphs)
(match-beginning 0)
(point-min))))
(mac-match (texmathp-match-macro bound))
(env-match (texmathp-match-environment
(if (and mac-match (> (cdr mac-match) bound))
(cdr mac-match)
bound)))
(match (cons nil bound)))
;; Select the nearer match
(and env-match (setq match env-match))
;; Use `>=' instead of `>' in case called inside \ensuremath{..}
;; beginning just at (point-min).
(and mac-match (>= (cdr mac-match) (cdr match)) (setq match mac-match))
(setq math-on (memq (nth 1 (assoc (car match) texmathp-tex-commands1))
'(env-on arg-on)))
;; Check for switches
(and (not math-on)
(setq sw-match (texmathp-match-switch bound))
;; Use `>=' instead of `>' by similar reason as above. (bug#41559)
(>= (cdr sw-match) (cdr match))
(eq (nth 1 (assoc (car sw-match) texmathp-tex-commands1)) 'sw-on)
(setq match sw-match math-on t))
;; Check for togglers
(if (not math-on)
(save-excursion
(goto-char (cdr match))
(while (re-search-forward texmathp-toggle-regexp pos t)
;; (unless ...) added:
(unless (TeX-escaped-p (1- (point)))
(if (setq math-on (not math-on))
(setq sw-match (cons (match-string-no-properties 2)
(match-beginning 2)))
(setq sw-match nil))))
(and math-on sw-match (setq match sw-match))))
;; Store info, show as message when interactive, and return
(setq texmathp-why match)
;; Check also if the match is inside a verbatim construct and
;; return immediately nil. This relies on the function
;; `LaTeX-verbatim-p'. We add a check here in case this library
;; is used stand-alone without latex.el provided by AUCTeX
;; (bug#61410) and the `major-mode' doesn't derive from `TeX-mode'
;; (bug#69681):
(if (and (derived-mode-p 'TeX-mode)
(fboundp 'LaTeX-verbatim-p)
(LaTeX-verbatim-p (cdr match)))
(progn
(setq texmathp-why `(nil . ,(cdr match)))
(when (called-interactively-p 'any)
(message "math-mode is off: Math command in verbatim construct at
buffer position %d"
(cdr match)))
nil)
(and (called-interactively-p 'any)
(message "math-mode is %s: %s begins at buffer position %d"
(if math-on "on" "off")
(or (car match) "new paragraph")
(cdr match)))
(and math-on t))))
--8<---------------cut here---------------end--------------->8---
> I tested it against backslash runs from N=0 up to N=101 (even and
> odd), the original tabular/TikZ case, your \\\$ counterexample, $$
> display math combined with backslash runs, and multi-cell tabular rows
> -- all correct. I also confirmed the compiled regexp still lets
> regexp-opt factor "$" and "$$" exactly as before
> ("\\(\\\\*\\)\\($\\$?\\)"), so display math is unaffected.
Can you please run your tests with the suggestion above?
> For full transparency, as with my initial report: I used an AI
> assistant (Claude) throughout this follow-up as well -- to analyze why
> the paired-backslash regex fails structurally, to design this
> alternative, and to build and run a test harness (an actual Emacs
> instance, byte-compiling texmathp.el and exercising the real texmathp
> function against dozens of cases, including the ones above) rather
> than reasoning about the regex in the abstract. I independently
> reviewed the test cases and the reasoning, and I'm satisfied the fix
> and the testing are sound, but of course I'd still welcome your
> review, especially given your closer familiarity with texmathp's
> history and edge cases.
Thanks for the disclaimer above. Note that currently, the GNU project
doesn't accept any code generated with LLMs until further notice. So we
can't install your suggestion above. So we either find another solution
(which I've proposed) or your change has to wait (if we agree on it).
TBH, I'm not happy with ("\\(\\\\*\\)\\($\\$?\\)").
Best, Arash
_______________________________________________
bug-auctex mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/bug-auctex