;;; org-ref-bibliography-links.el --- Bibliography and bibliographystyle links -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2024 John Kitchin ;; Author: John Kitchin ;; Keywords: convenience ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; ;;; Commentary: ;; ;;; Code: (defcustom org-ref-latex-bib-resolve-func #'file-relative-name "Function to expand paths to the bibliography file on latex export. Use this to convert a path to what you want, e.g. relative to some directory, absolute, etc." :type 'function :group 'org-ref) (defcustom org-ref-validate-bibliography nil "If non-nil, validate bibliography files in fontification. This can be slow, so we don't do it by default." :type 'boolean :group 'org-ref) ;;* bibliography* links (defun org-ref-get-bibfile-path (bibfile) "Get a path to BIBFILE as local file, or using kpsewhich. This should allow you to use a bib file that is setup with TeX variables like BIBINPUTS." (or (when (file-exists-p bibfile) bibfile) (let ((f (replace-regexp-in-string "\n$" "" (shell-command-to-string (format "kpsewhich %s" bibfile))))) (unless (string= "" f) f)))) (defun org-ref-bibliography*-export (cmd bibfiles _desc backend) "Exporting function for bibliography links. To be used as a partial function e.g. (apply-partially \"bibliography\" 'org-ref-bibliography*-export) Argument CMD is the command it should export to. Argument BIBFILES is a comma-separated list of strings. Argument BACKEND is the export backend." (cond ((eq backend 'latex) (format "%s{%s}" cmd (if (not (string= "" bibfiles)) (replace-regexp-in-string "\\.bib" "" (mapconcat 'identity (mapcar (lambda (f) (funcall org-ref-latex-bib-resolve-func (org-ref-get-bibfile-path f))) (split-string bibfiles ",")) ",")) ""))))) (defun org-ref-bibliography-activate (start end path _bracketp) "Activate a bibliography link. Adds a warning face to non-existent or invalid bib-files. START and END are the bounds of the link. PATH is a comma-separated list of bibfiles." (goto-char start) (cl-loop for p in (split-string path ",") do (setq p (string-trim p)) (search-forward p) (setq p (org-ref-get-bibfile-path p)) (put-text-property (match-beginning 0) (match-end 0) 'org-ref-bibfile p) (put-text-property (match-beginning 0) (match-end 0) 'help-echo (format "File exists at %s" p)) ;; activate files that don't exist (when (or (null p) (not (file-exists-p p))) (put-text-property (match-beginning 0) (match-end 0) 'face 'font-lock-warning-face) (put-text-property (match-beginning 0) (match-end 0) 'help-echo "This file was not found.")) (when (and p (file-exists-p p)) (when org-ref-validate-bibliography ;; Let's do a validation, but only if it has changed since the last time we checked. (let* ((mod-time-last-check (or (get-text-property (match-beginning 0) 'mod-time-last-check) '(0 0 0 0))) (last-modified (file-attribute-modification-time (file-attributes p))) (bibtex-valid)) (if (time-equal-p mod-time-last-check last-modified) (setq bibtex-valid (get-text-property (match-beginning 0) 'bibtex-valid)) ;; the times were not equal, so we check and store the state. (setq bibtex-valid (save-match-data (with-current-buffer (find-file-noselect p) (bibtex-validate)))) (put-text-property (match-beginning 0) (match-end 0) 'mod-time-last-check last-modified) (put-text-property (match-beginning 0) (match-end 0) 'bibtex-valid bibtex-valid)) (unless bibtex-valid (put-text-property (match-beginning 0) (match-end 0) 'face 'font-lock-warning-face) (put-text-property (match-beginning 0) (match-end 0) 'help-echo "This file did not pass `bibtex-validate'."))))))) (defun org-ref-bibliography*-follow (_path) "Function to follow bibliography links." (interactive) (save-excursion (unless (get-text-property (point) 'org-ref-bibfile) (re-search-forward ":")) (find-file (org-ref-get-bibfile-path (get-text-property (point) 'org-ref-bibfile))))) (defun org-ref-printbibliography-export (options _desc backend) "Export function for printbibliography links. Argument OPTIONS are the options used for the command. Optional argument BACKEND is the export backend." (cond ((eq backend 'latex) ;; write out the biblatex bibliography command (format "\\printbibliography%s" (if (not (string= "" options)) (format "[%s]" options) ""))))) (defvar org-ref-cite-types) (declare-function org-element-map "org-element") (declare-function org-element-property "org-element") (declare-function org-element-parse-buffer "org-element") (declare-function bibtex-completion-show-entry "bibtex-completion") (declare-function org-ref-possible-bibfiles "org-ref-core") (declare-function org-ref-parse-cite-path "org-ref-citation-links") (defun org-ref-insert-bibliography-link () "Insert a bibliography link for the files used in this buffer." (interactive) (let* ((cite-links (org-element-map (org-element-parse-buffer) 'link (lambda (lnk) (when (assoc (org-element-property :type lnk) org-ref-cite-types) lnk)))) (keys (delete-dups (cl-loop for cl in cite-links append (cl-loop for ref in (plist-get (org-ref-parse-cite-path (org-element-property :path cl)) :references) collect (plist-get ref :key))))) (files (delete-dups (cl-loop for key in keys collect (save-window-excursion (bibtex-completion-show-entry (list key)) (buffer-file-name)))))) (insert (format "[[bibliography:%s]]" (string-join files ","))))) (defun org-ref-bibliography-complete (&optional arg) "Completion function for bibliography links." (concat "bibliography:" (completing-read "Bibliography file: " (org-ref-possible-bibfiles)))) (defun org-ref-nobibliography-complete (&optional arg) "Completion function for bibliography links." (concat "nobibliography:" (completing-read "Bibliography file: " (org-ref-possible-bibfiles)))) (defvar org-ref-default-citation-link) (defun org-ref-bibtex-store-link () "Store a link from a bibtex file. Only supports the cite link. This essentially the same as the store link in org-bibtex, but it creates a cite link." (when (eq major-mode 'bibtex-mode) (let ((link (concat org-ref-default-citation-link ":&" (save-excursion (bibtex-beginning-of-entry) (cdr (assoc "=key=" (bibtex-parse-entry))))))) (push (list link) org-stored-links) (car org-stored-links)))) ;; ** bibliography* links (org-link-set-parameters "bibliography" :follow #'org-ref-bibliography*-follow :store #'org-ref-bibtex-store-link :complete #'org-ref-bibliography-complete :help-echo "Bibliography link" :export (apply-partially 'org-ref-bibliography*-export "\\bibliography") :activate-func #'org-ref-bibliography-activate :face 'org-link) (org-link-set-parameters "nobibliography" :complete #'org-ref-nobibliography-complete :store #'org-ref-bibtex-store-link :help-echo "No bibliography link" :activate-func #'org-ref-bibliography-activate :follow #'org-ref-bibliography*-follow :export (apply-partially 'org-ref-bibliography*-export "\\nobibliography") :face 'org-link) (org-link-set-parameters "nobibliography*" :complete #'org-ref-nobibliography-complete :store #'org-ref-bibtex-store-link :help-echo "No bibliography link" :activate-func #'org-ref-bibliography-activate :follow #'org-ref-bibliography*-follow :export (apply-partially 'org-ref-bibliography*-export "\\nobibliography*") :face 'org-link) (org-link-set-parameters "printbibliography" :export #'org-ref-printbibliography-export) ;; Note I removed the addbibresource link, it goes in the header, not the document. ;; *** bibliographystyle (defvar-local org-ref-bst-styles nil "A list of known bibliography styles. Used to cache results.") (defun org-ref-clear-bst-cache () "Clear `org-ref-bst-styles' to reload it." (interactive) (setq org-ref-bst-styles nil)) (defun org-ref-bibliography-styles () "Return a list of known bibliography styles. Returns `org-ref-bst-styles' or sets it and returns it." (or org-ref-bst-styles (setq org-ref-bst-styles (mapcar 'file-name-nondirectory (mapcar 'file-name-sans-extension (org-ref--flatten-list (mapcar (lambda (path) (setq path (replace-regexp-in-string "!" "" path)) (when (file-directory-p path) (org-ref--directory-files path (lambda (f) (org-ref--file-ext-p f "bst"))))) (split-string ;; https://tex.stackexchange.com/questions/431948/get-a-list-of-installed-bibliography-styles-with-kpsewhich?noredirect=1#comment1082436_431948 (shell-command-to-string "kpsewhich -expand-path '$BSTINPUTS'") ":")))))))) (defun org-ref-bibliographystyle-complete-link (&optional arg) "Completion function for bibliography style links. ARG is a not used." (when (executable-find "kpsewhich") (concat "bibliographystyle:" (completing-read "Style: " (org-ref-bibliography-styles))))) (defun org-ref-bibliographystyle-activate (start _end path _bracketp) "Activation function for bibliography styles. START is the beginning position of the link. Optional argument PATH contains the selected style." (unless (member path (org-ref-bibliography-styles)) (goto-char start) (search-forward path) (put-text-property (match-beginning 0) (match-end 0) 'face 'font-lock-warning-face) (put-text-property (match-beginning 0) (match-end 0) 'help-echo "Unrecognized style"))) (defun org-ref-bibliographystyle-export (style _desc backend) "Export function for bibliographystyle links. Argument STYLE is the desired style. Optional argument BACKEND is the export backend." (cond ((or (eq backend 'latex) (eq backend 'beamer)) ;; write out the latex bibliography command (format "\\bibliographystyle{%s}" style)) ;; No other backend needs this I think (t ""))) (org-link-set-parameters "bibliographystyle" :complete #'org-ref-bibliographystyle-complete-link :activate-func #'org-ref-bibliographystyle-activate :export #'org-ref-bibliographystyle-export) (provide 'org-ref-bibliography-links) ;;; org-ref-bibliography-links.el ends here ;; Local Variables: ;; byte-compile-warnings: (not docstrings) ;; End: