oc-biblatex.el (18367B)
1 ;;; oc-biblatex.el --- biblatex citation processor for Org -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. 4 5 ;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr> 6 7 ;; This file is part of GNU Emacs. 8 9 ;; GNU Emacs is free software: you can redistribute it and/or modify 10 ;; it under the terms of the GNU General Public License as published by 11 ;; the Free Software Foundation, either version 3 of the License, or 12 ;; (at your option) any later version. 13 14 ;; GNU Emacs is distributed in the hope that it will be useful, 15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 ;; GNU General Public License for more details. 18 19 ;; You should have received a copy of the GNU General Public License 20 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 21 22 ;;; Commentary: 23 24 ;; This library registers the `biblatex' citation processor, which provides 25 ;; the "export" capability for citations. 26 27 ;; The processor relies on "biblatex" LaTeX package. As such it ensures that 28 ;; the package is properly required in the document's preamble. More 29 ;; accurately, it will reuse any "\usepackage{biblatex}" already present in 30 ;; the document (e.g., through `org-latex-packages-alist'), or insert one using 31 ;; options defined in `org-cite-biblatex-options'. 32 33 ;; In any case, the library will override style-related options with those 34 ;; specified with the citation processor, in `org-cite-export-processors' or 35 ;; "cite_export" keyword. If you need to use different styles for bibliography 36 ;; and citations, you can separate them with "bibstyle/citestyle" syntax. E.g., 37 ;; 38 ;; #+cite_export: biblatex authortitle/authortitle-ibid 39 40 ;; The library supports the following citation styles: 41 ;; 42 ;; - author (a), including caps (c), full (f) and caps-full (cf) variants, 43 ;; - locators (l), including bare (b), caps (c) and bare-caps (bc) variants, 44 ;; - noauthor (na), including bare (b) variant, 45 ;; - nocite (n), 46 ;; - text (t), including caps (c) variant, 47 ;; - default style, including bare (b), caps (c) and bare-caps (bc) variants. 48 49 ;; When citation and style permit, the library automatically generates 50 ;; "multicite" versions of the commands above. 51 52 ;; Bibliography is printed using "\printbibliography" command. Additional 53 ;; options may be passed to it through a property list attached to the 54 ;; "print_bibliography" keyword. E.g., 55 ;; 56 ;; #+print_bibliography: :section 2 :heading subbibliography 57 ;; 58 ;; Values including spaces must be surrounded with double quotes. If you need 59 ;; to use a key multiple times, you can separate its values with commas, but 60 ;; without any space in-between: 61 ;; 62 ;; #+print_bibliography: :keyword abc,xyz :title "Primary Sources" 63 64 ;;; Code: 65 66 (require 'org-macs) 67 (org-assert-version) 68 69 (require 'map) 70 (require 'org-macs) 71 (require 'oc) 72 73 (declare-function org-element-property "org-element-ast" (property node)) 74 (declare-function org-element-parent "org-element-ast" (node)) 75 (declare-function org-export-data "org-export" (data info)) 76 77 78 ;;; Customization 79 (defcustom org-cite-biblatex-options nil 80 "Options added to \"biblatex\" package. 81 If \"biblatex\" package is already required in the document, e.g., through 82 `org-latex-packages-alist' variable, these options are ignored." 83 :group 'org-cite 84 :package-version '(Org . "9.5") 85 :type '(choice 86 (string :tag "Options (key=value,key2=value2...)") 87 (const :tag "No option" nil)) 88 :safe #'string-or-null-p) 89 90 (defcustom org-cite-biblatex-styles 91 '(("author" "caps" "Citeauthor*" nil nil) 92 ("author" "full" "citeauthor" nil nil) 93 ("author" "caps-full" "Citeauthor" nil nil) 94 ("author" nil "citeauthor*" nil nil) 95 ("locators" "bare" "notecite" nil nil) 96 ("locators" "caps" "Pnotecite" nil nil) 97 ("locators" "bare-caps" "Notecite" nil nil) 98 ("locators" nil "pnotecite" nil nil) 99 ("noauthor" "bare" "cite*" nil nil) 100 ("noauthor" nil "autocite*" nil nil) 101 ("nocite" nil "nocite" nil t) 102 ("text" "caps" "Textcite" "Textcites" nil) 103 ("text" nil "textcite" "textcites" nil) 104 (nil "bare" "cite" "cites" nil) 105 (nil "caps" "Autocite" "Autocites" nil) 106 (nil "bare-caps" "Cite" "Cites" nil) 107 (nil nil "autocite" "autocites" nil)) 108 "List of styles and variants, with associated BibLaTeX commands. 109 110 Each style follows the pattern 111 112 (NAME VARIANT COMMAND MULTI-COMMAND NO-OPTION) 113 114 where: 115 116 NAME is the name of the style, as a string, or nil. The nil 117 style is the default style. As such, it must have an entry in 118 the list. 119 120 VARIANT is the name of the style variant, as a string or nil. 121 The nil variant is the default variant for the current style. 122 As such, each style name must be associated to a nil variant. 123 124 COMMAND is the LaTeX command to use, as a string. It should 125 not contain the leading backslash character. 126 127 MULTI-COMMAND is the LaTeX command to use when a multi-cite 128 command is appropriate. When nil, the style is deemed 129 inappropriate for multi-cites. The command should not contain 130 the leading backslash character. 131 132 NO-OPTION is a boolean. When non-nil, no optional argument 133 should be added to the LaTeX command. 134 135 Each NAME-VARIANT pair should be unique in the list. 136 137 It is also possible to provide shortcuts for style and variant 138 names. See `org-cite-biblatex-style-shortcuts'." 139 :group 'org-cite 140 :package-version '(Org . "9.6") 141 :type '(repeat 142 (list :tag "Style/variant combination" 143 ;; Name part. 144 (choice :tag "Style" 145 (string :tag "Name") 146 (const :tag "Default style" nil)) 147 ;; Variant part. 148 (choice :tag "Variant" 149 (string :tag "Name") 150 (const :tag "Default variant" nil)) 151 ;; Command part. 152 (string :tag "Command name") 153 (choice :tag "Multicite command" 154 (string :tag "Command name") 155 (const :tag "No multicite support" nil)) 156 (choice :tag "Skip optional arguments" 157 (const :tag "Yes" t) 158 (const :tag "No" nil))))) 159 160 (defcustom org-cite-biblatex-style-shortcuts 161 '(("a" . "author") 162 ("b" . "bare") 163 ("bc" . "bare-caps") 164 ("c" . "caps") 165 ("cf" . "caps-full") 166 ("f" . "full") 167 ("l" . "locators") 168 ("n" . "nocite") 169 ("na" . "noauthor") 170 ("t" . "text")) 171 "List of shortcuts associated to style or variant names. 172 173 Each entry is a pair (NAME . STYLE-NAME) where NAME is the name 174 of the shortcut, as a string, and STYLE-NAME is the name of 175 a style in `org-cite-biblatex-styles'." 176 :group 'org-cite 177 :package-version '(Org . "9.6") 178 :type '(repeat 179 (cons :tag "Shortcut" 180 (string :tag "Name") 181 (string :tag "Full name"))) 182 :safe t) 183 184 185 ;;; Internal functions 186 (defun org-cite-biblatex--package-options (initial style) 187 "Return options string for \"biblatex\" package. 188 189 INITIAL is an initial style of comma-separated options, as a string or nil. 190 STYLE is the style definition as a string or nil. 191 192 Return a string." 193 (let* ((options-no-style 194 (and initial 195 (let ((re (rx string-start (or "bibstyle" "citestyle" "style")))) 196 (seq-filter 197 (lambda (option) (not (string-match re option))) 198 (split-string (org-unbracket-string "[" "]" initial) 199 "," t " \t"))))) 200 ;; Check whether the string is in key=val,... 201 (biblatex-options-p (and (stringp style) (string-match-p "\\`[^,=]+=[^,]+\\(,[^=]+=[^,]+\\)\\'" style))) 202 (style-options 203 (cond 204 ((null style) nil) 205 ;; Assume it is a valid options string for biblatex if it is in key=val,... format 206 ((not (string-match "/" style)) (list (if biblatex-options-p style (concat "style=" style)))) 207 (t 208 (list (concat "bibstyle=" (substring style nil (match-beginning 0))) 209 (concat "citestyle=" (substring style (match-end 0)))))))) 210 (if (or options-no-style style-options) 211 (format "[%s]" 212 (mapconcat #'identity 213 (append options-no-style style-options) 214 ",")) 215 ""))) 216 217 (defun org-cite-biblatex--multicite-p (citation) 218 "Non-nil when citation could make use of a \"multicite\" command." 219 (let ((references (org-cite-get-references citation))) 220 (and (< 1 (length references)) 221 (seq-some (lambda (r) 222 (or (org-element-property :prefix r) 223 (org-element-property :suffix r))) 224 references)))) 225 226 (defun org-cite-biblatex--atomic-arguments (references info &optional no-opt) 227 "Build argument for the list of citation REFERENCES. 228 When NO-OPT argument is non-nil, only provide mandatory arguments." 229 (let ((mandatory 230 (format "{%s}" 231 (mapconcat (lambda (r) (org-element-property :key r)) 232 references 233 ",")))) 234 (if no-opt mandatory 235 (let* ((origin (pcase references 236 (`(,reference) reference) 237 (`(,reference . ,_) 238 (org-element-parent reference)))) 239 (suffix (org-element-property :suffix origin)) 240 (prefix (org-element-property :prefix origin))) 241 (concat (and prefix 242 (format "[%s]" (org-trim (org-export-data prefix info)))) 243 (cond 244 (suffix (format "[%s]" 245 (org-trim (org-export-data suffix info)))) 246 (prefix "[]") 247 (t nil)) 248 mandatory))))) 249 250 (defun org-cite-biblatex--multi-arguments (citation info) 251 "Build \"multicite\" command arguments for CITATION object. 252 INFO is the export state, as a property list." 253 (let ((global-prefix (org-element-property :prefix citation)) 254 (global-suffix (org-element-property :suffix citation))) 255 (concat (and global-prefix 256 (format "(%s)" 257 (org-trim (org-export-data global-prefix info)))) 258 (cond 259 ;; Global pre/post-notes. 260 (global-suffix 261 (format "(%s)" 262 (org-trim (org-export-data global-suffix info)))) 263 (global-prefix "()") 264 (t nil)) 265 ;; All arguments. 266 (mapconcat (lambda (r) 267 (org-cite-biblatex--atomic-arguments (list r) info)) 268 (org-cite-get-references citation) 269 "")))) 270 271 (defun org-cite-biblatex--command (citation info name &optional multi no-opt) 272 "Return BibLaTeX command NAME for CITATION object. 273 274 INFO is the export state, as a property list. 275 276 When optional argument MULTI is non-nil, use it as a multicite 277 command name when appropriate. When optional argument NO-OPT is 278 non-nil, do not add optional arguments to the command." 279 (if (and multi (org-cite-biblatex--multicite-p citation)) 280 (format "\\%s%s" multi (org-cite-biblatex--multi-arguments citation info)) 281 (format "\\%s%s" 282 name 283 (org-cite-biblatex--atomic-arguments 284 (org-cite-get-references citation) info no-opt)))) 285 286 (defun org-cite-biblatex--expand-shortcuts (style) 287 "Return STYLE pair with shortcuts expanded." 288 (pcase style 289 (`(,style . ,variant) 290 (cons (or (alist-get style org-cite-biblatex-style-shortcuts 291 nil nil #'equal) 292 style) 293 (or (alist-get variant org-cite-biblatex-style-shortcuts 294 nil nil #'equal) 295 variant))) 296 (_ (error "This should not happen")))) 297 298 (defun org-cite-biblatex-list-styles () 299 "List styles and variants supported in `biblatex' citation processor. 300 The output format is appropriate as a value for `:cite-styles' keyword 301 in `org-cite-register-processor', which see." 302 (let ((shortcuts (make-hash-table :test #'equal)) 303 (variants (make-hash-table :test #'equal))) 304 (pcase-dolist (`(,name . ,full-name) org-cite-biblatex-style-shortcuts) 305 (push name (gethash full-name shortcuts))) 306 (pcase-dolist (`(,name ,variant . ,_) org-cite-biblatex-styles) 307 (unless (null variant) (push variant (gethash name variants)))) 308 (map-apply (lambda (style-name variants) 309 (cons (cons (or style-name "nil") 310 (gethash style-name shortcuts)) 311 (mapcar (lambda (v) 312 (cons v (gethash v shortcuts))) 313 variants))) 314 variants))) 315 316 317 ;;; Export capability 318 (defun org-cite-biblatex-export-bibliography (_keys _files _style props &rest _) 319 "Print references from bibliography. 320 PROPS is the local properties of the bibliography, as a property list." 321 (concat "\\printbibliography" 322 (and props 323 (let ((key nil) 324 (results nil)) 325 (dolist (datum props) 326 (cond 327 ((keywordp datum) 328 (when key (push key results)) 329 (setq key (substring (symbol-name datum) 1))) 330 (t 331 ;; Comma-separated values are associated to the 332 ;; same keyword. 333 (push (mapconcat (lambda (v) (concat key "=" v)) 334 (split-string datum "," t) 335 ",") 336 results) 337 (setq key nil)))) 338 (format "[%s]" 339 (mapconcat #'identity (nreverse results) ",")))))) 340 341 (defun org-cite-biblatex-export-citation (citation style _ info) 342 "Export CITATION object. 343 STYLE is the citation style, as a pair of either strings or nil. 344 INFO is the export state, as a property list." 345 (pcase-let* ((`(,name . ,variant) (org-cite-biblatex--expand-shortcuts style)) 346 (candidates nil) 347 (style-match-flag nil)) 348 (catch :match 349 ;; Walk `org-cite-biblatex-styles' and prioritize matching 350 ;; candidates. At the end of the process, the optimal candidate 351 ;; should appear in front of CANDIDATES. 352 (dolist (style org-cite-biblatex-styles) 353 (pcase style 354 ;; A matching style-variant pair trumps anything else. 355 ;; Return it. 356 (`(,(pred (equal name)) ,(pred (equal variant)) . ,_) 357 (throw :match (setq candidates (list style)))) 358 ;; nil-nil style-variant is the fallback value. Consider it 359 ;; only if nothing else matches. 360 (`(nil nil . ,_) 361 (unless candidates (push style candidates))) 362 ;; A matching style with default variant trumps a matching 363 ;; variant without the adequate style. Ensure the former 364 ;; appears first in the list. 365 (`(,(pred (equal name)) nil . ,_) 366 (push style candidates) 367 (setq style-match-flag t)) 368 (`(nil ,(pred (equal variant)) . ,_) 369 (unless style-match-flag (push style candidates))) 370 ;; Discard anything else. 371 (_ nil)))) 372 (apply 373 #'org-cite-biblatex--command citation info 374 (pcase (seq-elt candidates 0) ;; `seq-first' is not available in Emacs 26. 375 (`(,_ ,_ . ,command-parameters) command-parameters) 376 ('nil 377 (user-error 378 "Missing default style or variant in `org-cite-biblatex-styles'")) 379 (other 380 (user-error "Invalid entry %S in `org-cite-biblatex-styles'" other)))))) 381 382 (defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _) 383 "Prepare document preamble for \"biblatex\" usage. 384 385 OUTPUT is the final output of the export process. FILES is the list of file 386 names used as the bibliography. 387 388 This function ensures \"biblatex\" package is required. It also adds resources 389 to the document, and set styles." 390 (with-temp-buffer 391 (save-excursion (insert output)) 392 (when (search-forward "\\begin{document}" nil t) 393 ;; Ensure there is a \usepackage{biblatex} somewhere or add one. 394 ;; Then set options. 395 (goto-char (match-beginning 0)) 396 (let ((re (rx "\\usepackage" 397 (opt (group "[" (*? anything) "]")) 398 "{biblatex}"))) 399 (cond 400 ;; No "biblatex" package loaded. Insert "usepackage" command 401 ;; with appropriate options, including style. 402 ((not (re-search-backward re nil t)) 403 (save-excursion 404 (insert 405 (format "\\usepackage%s{biblatex}\n" 406 (org-cite-biblatex--package-options 407 org-cite-biblatex-options style))))) 408 ;; "biblatex" package loaded, but without any option. 409 ;; Include style only. 410 ((not (match-beginning 1)) 411 (search-forward "{" nil t) 412 (insert (org-cite-biblatex--package-options nil style))) 413 ;; "biblatex" package loaded with some options set. Override 414 ;; style-related options with ours. 415 (t 416 (replace-match 417 (save-match-data 418 (org-cite-biblatex--package-options (match-string 1) style)) 419 nil nil nil 1)))) 420 ;; Insert resources below. 421 (forward-line) 422 (insert (mapconcat (lambda (f) 423 (format "\\addbibresource%s{%s}" 424 (if (org-url-p f) "[location=remote]" "") 425 f)) 426 files 427 "\n") 428 "\n")) 429 (buffer-string))) 430 431 432 ;;; Register `biblatex' processor 433 (org-cite-register-processor 'biblatex 434 :export-bibliography #'org-cite-biblatex-export-bibliography 435 :export-citation #'org-cite-biblatex-export-citation 436 :export-finalizer #'org-cite-biblatex-prepare-preamble 437 :cite-styles #'org-cite-biblatex-list-styles) 438 439 (provide 'oc-biblatex) 440 ;;; oc-biblatex.el ends here