From e995e069ca6fc80b9f45659d9743c52600b091f1 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 12 Dec 2021 15:34:45 +0100 Subject: [PATCH] oc-biblatex: Allow style customization * lisp/oc-biblatex.el (org-cite-biblatex-styles): (org-cite-biblatex-style-shortcuts): New defcustoms. (org-cite-biblatex--expand-shortcuts): New function. (org-cite-biblatex-export-citation): Extract adequate style from defcustoms above instead of hard-coding all styles. --- etc/ORG-NEWS | 6 ++ lisp/oc-biblatex.el | 179 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 147 insertions(+), 38 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 80f6b7175..7bf79e758 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -97,6 +97,12 @@ argument. ** Miscellaneous +*** Styles are customizable in ~biblatex~ citation processor + +It is now possible to add new styles or modify old ones in ~biblatex~ +citation processor. See ~org-cite-biblatex-styles~ for more +information. + *** Citation processors can declare styles dynamically When a citation processor is registered, it is now possible to set diff --git a/lisp/oc-biblatex.el b/lisp/oc-biblatex.el index 8496a912c..c5ed6c6cc 100644 --- a/lisp/oc-biblatex.el +++ b/lisp/oc-biblatex.el @@ -81,6 +81,99 @@ If \"biblatex\" package is already required in the document, e.g., through (const :tag "No option" nil)) :safe #'string-or-null-p) +(defcustom org-cite-biblatex-styles + '(("author" "caps" "Citeauthor*" nil nil) + ("author" "full" "citeauthor" nil nil) + ("author" "caps-full" "Citeauthor" nil nil) + ("author" nil "citeauthor*" nil nil) + ("locators" "bare" "notecite" nil nil) + ("locators" "caps" "Pnotecite" nil nil) + ("locators" "bare-caps" "Notecite" nil nil) + ("locators" nil "pnotecite" nil nil) + ("noauthor" "bare" "cite*" nil nil) + ("noauthor" nil "autocite*" nil nil) + ("nocite" nil "nocite" nil t) + ("text" "caps" "Textcite" "Textcites" nil) + ("text" nil "textcite" "textcites" nil) + (nil "bare" "cite" "cites" nil) + (nil "caps" "Autocite" "Autocites" nil) + (nil "bare-caps" "Cite" "Cites" nil) + (nil nil "autocite" "autocites" nil)) + "List of styles and variants, with associated BibLaTeX commands. + +Each style follows the pattern + + (NAME VARIANT COMMAND MULTI-COMMAND NO-OPTION) + +where: + + NAME is the name of the style, as a string, or nil. The nil + style is the default style. As such, it must have an entry in + the list. + + VARIANT is the name of the style variant, as a string or nil. + The nil variant is the default variant for the current style. + As such, each style name must be associated to a nil variant. + + COMMAND is the LaTeX command to use, as a string. It should + not contain the leading backslash character. + + MULTI-COMMAND is the LaTeX command to use when a multi-cite + command is appropriate. When nil, the style is deemed + inappropriate for multi-cites. The command should not contain + the leading backslash character. + + NO-OPTION is a boolean. When non-nil, no optional argument + should be added to the LaTeX command. + +Each NAME-VARIANT pair should be unique in the list. + +It is also possible to provide shortcuts for style and variant +names. See `org-cite-biblatex-style-shortcuts'." + :group 'org-cite + :package-version '(Org . "9.6") + :type '(repeat + (list :tag "Style/variant combination" + ;; Name part. + (choice :tag "Style" + (string :tag "Name") + (const :tag "Default style" nil)) + ;; Variant part. + (choice :tag "Variant" + (string :tag "Name") + (const :tag "Default variant" nil)) + ;; Command part. + (string :tag "Command name") + (choice :tag "Multicite command" + (string :tag "Command name") + (const :tag "No multicite support" nil)) + (choice :tag "Skip optional arguments" + (const :tag "Yes" t) + (const :tag "No" nil))))) + +(defcustom org-cite-biblatex-style-shortcuts + '(("a" . "author") + ("b" . "bare") + ("c" . "caps") + ("cf" . "caps-full") + ("f" . "full") + ("l" . "locators") + ("n" . "nocite") + ("na" . "noauthor") + ("t" . "text")) + "List of shortcuts associated to style or variant names. + +Each entry is a pair (NAME . STYLE-NAME) where NAME is the name +of the shortcut, as a string, and STYLE-NAME is the name of +a style in `org-cite-biblatex-styles'." + :group 'org-cite + :package-version '(Org . "9.6") + :type '(repeat + (cons :tag "Shortcut" + (string :tag "Name") + (string :tag "Full name"))) + :safe t) + ;;; Internal functions (defun org-cite-biblatex--package-options (initial style) @@ -185,6 +278,18 @@ non-nil, do not add optional arguments to the command." (org-cite-biblatex--atomic-arguments (org-cite-get-references citation) info no-opt)))) +(defun org-cite-biblatex--expand-shortcuts (style) + "Return STYLE pair with shortcuts expanded." + (pcase style + (`(,style . ,variant) + (cons (or (alist-get style org-cite-biblatex-style-shortcuts + nil nil #'equal) + style) + (or (alist-get variant org-cite-biblatex-style-shortcuts + nil nil #'equal) + variant))) + (_ (error "This should not happen")))) + ;;; Export capability (defun org-cite-biblatex-export-bibliography (_keys _files _style props &rest _) @@ -214,44 +319,42 @@ PROPS is the local properties of the bibliography, as a property list." "Export CITATION object. STYLE is the citation style, as a pair of either strings or nil. INFO is the export state, as a property list." - (apply - #'org-cite-biblatex--command citation info - (pcase style - ;; "author" style. - (`(,(or "author" "a") . ,variant) - (pcase variant - ((or "caps" "c") '("Citeauthor*")) - ((or "full" "f") '("citeauthor")) - ((or "caps-full" "cf") '("Citeauthor")) - (_ '("citeauthor*")))) - ;; "locators" style. - (`(,(or "locators" "l") . ,variant) - (pcase variant - ((or "bare" "b") '("notecite")) - ((or "caps" "c") '("Pnotecite")) - ((or "bare-caps" "bc") '("Notecite")) - (_ '("pnotecite")))) - ;; "noauthor" style. - (`(,(or "noauthor" "na") . ,variant) - (pcase variant - ((or "bare" "b") '("cite*")) - (_ '("autocite*")))) - ;; "nocite" style. - (`(,(or "nocite" "n") . ,_) '("nocite" nil t)) - ;; "text" style. - (`(,(or "text" "t") . ,variant) - (pcase variant - ((or "caps" "c") '("Textcite" "Textcites")) - (_ '("textcite" "textcites")))) - ;; Default "nil" style. - (`(,_ . ,variant) - (pcase variant - ((or "bare" "b") '("cite" "cites")) - ((or "caps" "c") '("Autocite" "Autocites")) - ((or "bare-caps" "bc") '("Cite" "Cites")) - (_ '("autocite" "autocites")))) - ;; This should not happen. - (_ (error "Invalid style: %S" style))))) + (pcase-let* ((`(,name . ,variant) (org-cite-biblatex--expand-shortcuts style)) + (candidates nil) + (style-match-flag nil)) + (catch :match + ;; Walk `org-cite-biblatex-styles' and prioritize matching + ;; candidates. At the end of the process, the optimal candidate + ;; should appear in front of CANDIDATES. + (dolist (style org-cite-biblatex-styles) + (pcase style + ;; A matching style-variant pair trumps anything else. + ;; Return it. + (`(,(pred (equal name)) ,(pred (equal variant)) . ,_) + (throw :match (setq candidates (list style)))) + ;; nil-nil style-variant is the fallback value. Consider it + ;; only if nothing else matches. + (`(nil nil . ,_) + (unless candidates (push style candidates))) + ;; A matching style with default variant trumps a matching + ;; variant without the adequate style. Ensure the former + ;; appears first in the list. + (`(,(pred (equal name)) nil . ,_) + (push style candidates) + (setq style-match-flag t)) + (`(nil ,(pred (equal variant)) . ,_) + (unless style-match-flag (push style candidates))) + ;; Discard anything else. + (_ nil)))) + (apply + #'org-cite-biblatex--command citation info + (pcase (seq-first candidates) + (`(,_ ,_ . ,command-parameters) command-parameters) + ('nil + (user-error + "Missing default style or variant in `org-cite-biblatex-styles'")) + (other + (user-error "Invalid entry %S in `org-cite-biblatex-styles'" other)))))) (defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _) "Prepare document preamble for \"biblatex\" usage.