Subject: bug#61400: Different filling for verbatim macros

From: Ikumi Keita <[email-masked]>
Date: Sat, 11 Feb 2023 17:55:52 +0900

Hi all,

>>>>> Arash Esbati <[email-masked]> writes:
> Tassilo Horn <[email-masked]> writes:
>> Hm, (fill-nobreak-p) returns nil only on the \ of the \verb|bar| but
>> returns non-nil on each \ of the other verbatim macros.  (I can't look
>> any further now so it's your turn again. ;-))

> I think I've got it: `LaTeX-verbatim-p' does its job as a predicate, but
> for filling, it should only return non-nil when point is inside the
> argument, and not when point is in front of the \verb macro itself, this
> is what `fill-move-to-break-point' does.

I realized that multiple players are involved in this issue. Let's see
how they interact:
Usually `LaTeX-verbatim-macro-boundaries' returns cons (BEG . END) where
BEG is the point just before the leading backslash of \verb etc.
However, there are inconsistencies when the point is at just before the
leading backslash; When "\verb..." is at beginning of a line, return
value is nil:

-!-\verb+abc+ ... (LaTeX-verbatim-macro-boundaries) -> nil

Otherwize, BEG is not the point just before the leading backslash, but
less than that value by 1:

xyz -!-\verb+abc+ ... (car (LaTeX-verbatim-macro-boundaries)) -> the
                       point just after the "z"

For this reason, `LaTeX-current-verbatim-macro' doesn't behave
consistently. It returns nil in the former case while the null string ""
in the latter.

-!-\verb+abc+     ... (LaTeX-current-verbatim-macro) -> nil
xyz -!-\verb+abc+ ... (LaTeX-current-verbatim-macro) -> ""

Here is the origin of the inconsistency.
----------------------------------------------------------------------
(defun LaTeX-verbatim-macro-boundaries ()
[...]
      ;; Search backwards for the macro start, unless we are facing one
      (unless (looking-at (concat (regexp-quote TeX-esc) verbatim-regexp)) --- (1)
        (catch 'found
          (while (progn
                   (skip-chars-backward (concat "^\n" (regexp-quote TeX-esc))
                                        (line-beginning-position))
                   (when (looking-at verbatim-regexp) (throw 'found nil)) --- (2)
                   (or (bobp) (forward-char -1))
                   (/= (point) (line-beginning-position))))))
      ;; Search forward for the macro end, unless we failed to find a start
      (unless (bolp)                                                    --- (3)
        (let* ((beg (1- (point)))
[...]
            (cons beg (1+ (point)))))))))
----------------------------------------------------------------------
When the point is just before the leading backslash, `looking-at' at (1)
matches and emacs skips to (3). But the block after (3) expects that the
point is just _after_ the leading backslash, as the rest of the code
exhibits. Actually, `looking-at' at (2) doesn't include backslash, so
emacs proceeds to (3) with the point at just _after_ the leading
backslash when (2) matches.

We can fix this inconsistency by the following code:
----------------------------------------------------------------------
(defun LaTeX-verbatim-macro-boundaries ()
[...]
      ;; Search backwards for the macro start, unless we are facing one
      (if (looking-at (concat (regexp-quote TeX-esc) verbatim-regexp))
          (forward-char 1)
        (catch 'found
[...]
----------------------------------------------------------------------
With this fix, `LaTeX-current-verbatim-macro' returns sane result even
when the point is just before "\verb". It just behave like
`TeX-current-macro'.

However, the story continues. With the above fix, Arash's original
example provides
Now we can write some text foo
and \path{bar}

Now we can write some text foo
and \path|bar|

Now we can write some text foo
and \Verb|bar|

Now we can write some text foo
and \Verb{bar}

Now we can write some text foo
and \verb|bar|

Yes, the behavior becomes coherent, but in the undesired direction. :-(

As Arash wrote, the reason is that `LaTeX-verbatim-p' returns non-nil
just before the leading backslashes for all these cases.
----------------------------------------------------------------------
(defun LaTeX-verbatim-p (&optional pos)
[...]
    (or (progn                                       --- (4)
          (syntax-propertize (point))                --- (4)
          (nth 3 (syntax-ppss)))                     --- (4)
        (member (LaTeX-current-verbatim-macro)                           --- (5)
                (LaTeX-verbatim-macros-with-delims))                     --- (5)
        (member (TeX-current-macro) (LaTeX-verbatim-macros-with-braces)) --- (5)
        (member (LaTeX-current-environment) (LaTeX-verbatim-environments)))))
----------------------------------------------------------------------
The block (4) returns nil when the point is just before the beginning of
quoted region. However, (5) returns non-nil when the point is just
before the verbatim macro. This isn't suitable for predicate of filling.

For consistency, maybe we should modify `TeX-current-macro' and
`LaTeX-current-verbatim-macro' so that they return nil when the point is
just before the verbatim macro. However, I'm not yet sure whether such
change brings about bad side effects.

Perhaps the safe fix is to tweak `TeX-current-macro' and
`LaTeX-current-verbatim-macro' so that they accept optional argument for
use in `LaTeX-verbatim-p'?

(By the way, 
        (member (LaTeX-current-verbatim-macro)
                (LaTeX-verbatim-macros-with-delims))
is a bit inefficient because `LaTeX-current-verbatim-macro' only
responds to macros included in `(LaTeX-verbatim-macros-with-delims)' for
the first place.)

Regards,
Ikumi Keita
#StandWithUkraine #StopWarInUkraine