Subject: Re: bug#31132: AUCTeX, RefTeX and biblatex's multicite commands

From: Arash Esbati <[email-masked]>
Date: Tue, 09 Apr 2024 14:39:29 +0200

Hi Gustavo,

gusbrs <[email-masked]> writes:

> Oh, reading the old thread was a little embarrassing. I didn't even
> know how to reply to a mailing list (full html contents at the end,
> etc., sigh... ;-).

Wait another year or two, and you'll be writing Mails within Emacs ;-)

> I did test things and the TL;DR is: looks good to me.

Thanks, I installed that change on Emacs master, commit 7236010d2e.

> I did not attempt to test for possible side effects in other areas,
> given that `reftex-move-to-previous-arg' and `reftex-what-macro' are
> general purpose utility functions. But I think the patch at hand is
> pretty careful in changing the syntax of the parentheses only locally.
> That's what I'd do too and my assessment is that I see no reason it
> shouldn't be safe.

Thanks, this is/was also my impression; hopefully no side-effects.

> The only other thing of note I observed is that the mcite-like
> citation commands are not fontified as reference/citation commands
> (they get only the generic `font-latex-sedate-face'). I had agreed
> with you that the case is tricky and arguably not worth the trouble on
> RefTeX's side of things. But fontification for them should be trivial.
> So, why not?

Ok, I had another look, and biblatex manual says on page 120:

  In contrast to that, a biblatex entry set is an entity in its own
  right.

So I think the change attached gives sort of support for mcite-like
macros, incl. fontification.  Do you want to give it a roll?  Please
ignore the re-indent changes.

> Nice, perhaps something useful comes out of it. I'll share the code
> first and comment later.

Thanks, I will read through it later.  I'd like to address the issues in
this report and close it before we proceed.

> I'm using `el-patch' in my config, but it should be easy enough to
> read, even if you are not acquainted with it.

Yes, I think I get the message.

> Net of this kind of stuff are some things which accumulated over time,
> to add support to specific packages, or fixing things I didn't like.
> In hindsight, my general take on those is that RefTeX seems to presume
> that only the standard, or very traditional, reference and citation
> commands exist and, hence, chooses to hard-code most of the relevant
> behavior, making it very hard to extend and tweak. Inaccessible to the
> less daring users and, more importantly, to AUCTeX style files.

Yes, this is true: RefTeX hardcodes plenty of stuff.

> I recall you questioning somewhat the relevance of this peculiar
> notation of biblatex's citation lists
> (https://lists.gnu.org/archive/html/auctex-devel/2023-05/msg00015.html)
> so, as a regular user of them, I'm glad and thankful to see you soften
> a little your stance in this regard.

TBH, I still don't want to touch font-latex.el in order to make
fontification for cite-lists work, but we have an imperfect solution in
place which hopefully is enough for you and other users :-)

Again, thank you for your support.

Best, Arash
diff --git a/style/biblatex.el b/style/biblatex.el
index 31180226..7f9d4145 100644
--- a/style/biblatex.el
+++ b/style/biblatex.el
@@ -237,20 +237,56 @@ for citation keys."
         (TeX-argument-insert (mapconcat #'identity items ",") optional))
       (setq noinsert t))))
 
+;; Support for mcite-like Citation Commands, see § 3.9.10 of Biblatex
+;; reference manual.
+(defun LaTeX-arg-biblatex-mcite (optional)
+  "Query and insert the mandatory argument of \\mcite compat macros."
+  (let ((set-name (TeX-read-string
+                   (TeX-argument-prompt optional nil "Set")))
+        (read-cite (lambda (opt)
+                     (if (and (fboundp 'reftex-citation)
+                              (fboundp 'reftex-plug-flag)
+                              (reftex-plug-flag 3))
+                         (reftex-citation t)
+                       (TeX-completing-read-multiple
+                        (TeX-argument-prompt opt nil "Key(s)")
+                        (LaTeX-bibitem-list)))))
+        cite-keys arg)
+    (cond ((and (not (string-empty-p set-name))
+                (y-or-n-p "Add citation keys to the set name?"))
+           ;; We want \mcite{set-name,*key1,*key2,*key3}
+           (setq cite-keys (funcall read-cite optional))
+           (when cite-keys
+             (setq cite-keys (mapcar (lambda (x) (concat "*" x))
+                                     cite-keys)))
+           (setq arg (concat set-name
+                             (when cite-keys
+                               (concat "," (mapconcat #'identity
+                                                      cite-keys
+                                                      ","))))))
+          ;; We want \mcite{key1}.  FIXME: Are multiple keys
+          ;; allowed?
+          ((string-empty-p set-name)
+           (setq arg (mapconcat #'identity
+                                (funcall read-cite optional)
+                                ",")))
+          (t (setq arg "")))
+    (TeX-argument-insert arg optional)))
+
 (defun LaTeX-arg-biblatex-natbib-notes (optional)
   "Prompt for two note arguments of a natbib compat citation command."
   (when TeX-arg-cite-note-p
-      (let ((pre (TeX-read-string
-                  (TeX-argument-prompt optional nil "Prenote")))
-            (post (TeX-read-string
-                   (TeX-argument-prompt optional nil "Postnote"))))
-        (TeX-argument-insert pre optional)
-        (TeX-argument-insert post optional)
-        ;; pre is given, post is empty: Make sure that we insert an
-        ;; extra pair of `[]', otherwise pre becomes post
-        (when (and pre (not (string= pre ""))
-                   (string= post ""))
-          (insert LaTeX-optop LaTeX-optcl)))))
+    (let ((pre (TeX-read-string
+                (TeX-argument-prompt optional nil "Prenote")))
+          (post (TeX-read-string
+                 (TeX-argument-prompt optional nil "Postnote"))))
+      (TeX-argument-insert pre optional)
+      (TeX-argument-insert post optional)
+      ;; pre is given, post is empty: Make sure that we insert an
+      ;; extra pair of `[]', otherwise pre becomes post
+      (when (and pre (not (string= pre ""))
+                 (string= post ""))
+        (insert LaTeX-optop LaTeX-optcl)))))
 
 (TeX-add-style-hook
  "biblatex"
@@ -334,32 +370,45 @@ for citation keys."
       (TeX-arg-completing-read-multiple (LaTeX-bibitem-list) "Keys"))
     ;;; Citation Commands
     '("cite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                  (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                 (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Cite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                  (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                 (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("parencite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Parencite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("footcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("footcitetext" (TeX-arg-conditional TeX-arg-cite-note-p
-                                          (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                         (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     ;; Style-specific Commands
     '("textcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Textcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("smartcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Smartcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("cite*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                   (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                  (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("parencite*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                        (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                       (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("supercite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     ;; Qualified Citation Lists
     '("cites" LaTeX-arg-biblatex-cites)
     '("Cites" LaTeX-arg-biblatex-cites)
@@ -374,45 +423,62 @@ for citation keys."
     '("supercites" LaTeX-arg-biblatex-cites)
     ;; Style-independent Commands
     '("autocite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Autocite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("autocite*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Autocite*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("autocites" LaTeX-arg-biblatex-cites)
     '("Autocites" LaTeX-arg-biblatex-cites)
     ;; Text Commands
     '("citeauthor" (TeX-arg-conditional TeX-arg-cite-note-p
-                                        (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                       (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Citeauthor" (TeX-arg-conditional TeX-arg-cite-note-p
-                                        (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                       (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citeauthor*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                         (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                        (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Citeauthor*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                         (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                        (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citetitle" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citetitle*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                        (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                       (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citeyear" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citeyear*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citedate" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citedate*" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("citeurl" (TeX-arg-conditional TeX-arg-cite-note-p
-                                     (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                    (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("parentext" "Text")
     '("brackettext" "Text")
     ;; Special Commands
     '("fullcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("footfullcite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                          (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                         (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("volcite"
       (TeX-arg-conditional TeX-arg-cite-note-p (["Prenote"]) ()) "Volume"
       (TeX-arg-conditional TeX-arg-cite-note-p (["Page"]) ()) TeX-arg-cite)
@@ -450,15 +516,20 @@ for citation keys."
       (TeX-arg-conditional TeX-arg-cite-note-p (["Prenote"]) ()) "Volume"
       (TeX-arg-conditional TeX-arg-cite-note-p (["Page"]) ()) TeX-arg-cite)
     '("notecite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Notecite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                      (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                     (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("pnotecite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("Pnotecite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     '("fnotecite" (TeX-arg-conditional TeX-arg-cite-note-p
-                                       (["Prenote"] ["Postnote"]) ()) TeX-arg-cite)
+                      (["Prenote"] ["Postnote"]) ())
+      TeX-arg-cite)
     ;; Low-level Commands
     '("citename"
       (TeX-arg-conditional TeX-arg-cite-note-p (["Prenote"] ["Postnote"]) ())
@@ -522,8 +593,8 @@ for citation keys."
              (list
               (car cmd)
               '(TeX-arg-conditional TeX-arg-cite-note-p
-                                    ([LaTeX-arg-biblatex-natbib-notes])
-                                    nil)
+                   ([LaTeX-arg-biblatex-natbib-notes])
+                 nil)
               #'TeX-arg-cite))))
          cmds))
 
@@ -540,6 +611,44 @@ for citation keys."
                                     ("Citealp"      "*[[{"))
                                   'biblatex))))
 
+   ;; § 3.9.10 mcite-like Citation Commands
+   (when (or (LaTeX-provided-package-options-member "biblatex" "mcite")
+             (LaTeX-provided-package-options-member "biblatex" "mcite=true"))
+     (let ((cmds '("mcite"      "Mcite"
+                   "mparencite" "Mparencite"
+                   "mfootcite"  "mfootcitetext"
+                   "mtextcite"  "Mtextcite"
+                   "msupercite"
+                   "mautocite"  "Mautocite"))
+           (spec "*[[{"))
+       ;; Add the macros incl. optional arguments:
+       (apply #'TeX-add-symbols
+              (mapcar
+               (lambda (cmd)
+                 (list cmd
+                       '(TeX-arg-conditional TeX-arg-cite-note-p
+                            (["Prenote"] ["Postnote"])
+                          nil)
+                       #'LaTeX-arg-biblatex-mcite))
+               cmds))
+       ;; Cater for the starred versions as well:
+       (apply #'TeX-add-symbols
+              (mapcar
+               (lambda (cmd)
+                 (list (concat cmd "*")
+                       '(TeX-arg-conditional TeX-arg-cite-note-p
+                            (["Prenote"] ["Postnote"])
+                          nil)
+                       #'LaTeX-arg-biblatex-mcite))
+               cmds))
+       ;; Fontification for compat macros does not go into `font-latex.el':
+       (when (and (featurep 'font-latex)
+                  (eq TeX-install-font-lock 'font-latex-setup))
+         (font-latex-add-keywords (mapcar (lambda (cmd)
+                                            (list cmd spec))
+                                          cmds)
+                                  'biblatex))))
+
    (LaTeX-add-environments
     ;;; Bibliography commands
     ;; Bibliography Sections